Skip to content

Commit 611ac4e

Browse files
committed
fix rustpython
1 parent 54a1734 commit 611ac4e

File tree

6 files changed

+83
-74
lines changed

6 files changed

+83
-74
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ thiserror = "2.0"
1111
tokio = { version = "1.36.0", features = ["sync", "macros"] }
1212

1313
[features]
14-
15-
default = ["rustpython"]
16-
# default = ["pyo3"]
14+
default = ["pyo3"]
15+
# default = ["rustpython"]
1716
pyo3 = ["dep:pyo3"]
1817
rustpython = ["dep:rustpython-vm", "dep:rustpython-stdlib"]
1918

@@ -27,9 +26,11 @@ tempfile = "3"
2726
[dependencies.rustpython-vm]
2827
version = "0.4.0"
2928
optional = true
29+
features = ["threading", "serde", "importlib"]
3030
[dependencies.rustpython-stdlib]
3131
version = "0.4.0"
3232
optional = true
33+
features = ["threading"]
3334

3435
# The `full` feature for tokio is needed for the tests
3536
[dev-dependencies.tokio]

src/lib.rs

Lines changed: 26 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -183,62 +183,34 @@ impl PyRunner {
183183
}
184184
/// Set python venv environment folder (does not change interpreter)
185185
pub async fn set_venv(&self, venv_path: &Path) -> Result<(), PyRunnerError> {
186-
let set_venv_code = r##"
187-
import sys
188-
import os
189-
190-
def add_venv_libs_to_syspath(venv_path):
191-
"""
192-
Adds the site-packages folder (and .pth entries) from a virtual environment to sys.path.
193-
194-
Args:
195-
venv_path (str): Path to the root of the virtual environment.
196-
"""
197-
if not os.path.isdir(venv_path):
198-
raise ValueError(f"{venv_path} is not a directory")
199-
200-
if os.name == "nt":
201-
# Windows: venv\Lib\site-packages
202-
site_packages = os.path.join(venv_path, "Lib", "site-packages")
203-
else:
204-
# POSIX: venv/lib/pythonX.Y/site-packages
205-
py_version = f"python{sys.version_info.major}.{sys.version_info.minor}"
206-
site_packages = os.path.join(venv_path, "lib", py_version, "site-packages")
207-
208-
if not os.path.isdir(site_packages):
209-
raise RuntimeError(f"Could not find site-packages in {venv_path}")
210-
211-
# Add site-packages itself
212-
if site_packages not in sys.path:
213-
sys.path.insert(0, site_packages)
214-
215-
# Process .pth files inside site-packages
216-
for entry in os.listdir(site_packages):
217-
if entry.endswith(".pth"):
218-
pth_file = os.path.join(site_packages, entry)
219-
try:
220-
with open(pth_file, "r") as f:
221-
for line in f:
222-
line = line.strip()
223-
if not line or line.startswith("#"):
224-
continue
225-
if line.startswith("import "):
226-
# Execute import statements inside .pth files
227-
exec(line, globals(), locals())
228-
else:
229-
# Treat as a path
230-
extra_path = os.path.join(site_packages, line)
231-
if os.path.exists(extra_path) and extra_path not in sys.path:
232-
sys.path.insert(0, extra_path)
233-
except Exception as e:
234-
print(f"Warning: Could not process {pth_file}: {e}")
235-
236-
return site_packages
237-
"##;
186+
if !venv_path.is_dir() {
187+
return Err(PyRunnerError::PyError(format!(
188+
"Could not find venv directory {}",
189+
venv_path.display()
190+
)));
191+
}
192+
let set_venv_code = include_str!("set_venv.py");
238193
self.run(&set_venv_code).await?;
194+
195+
let site_packages = if cfg!(target_os = "windows") {
196+
venv_path.join("Lib").join("site-packages")
197+
} else {
198+
let version_code = "f\"python{sys.version_info.major}.{sys.version_info.minor}\"";
199+
let py_version = self.eval(version_code).await?;
200+
venv_path
201+
.join("lib")
202+
.join(py_version.as_str().unwrap())
203+
.join("site-packages")
204+
};
205+
#[cfg(all(feature = "pyo3", not(feature = "rustpython")))]
206+
let with_pth = "True";
207+
#[cfg(feature = "rustpython")]
208+
let with_pth = "False";
209+
239210
self.run(&format!(
240-
"add_venv_libs_to_syspath({})",
241-
print_path_for_python(&venv_path.to_path_buf())
211+
"add_venv_libs_to_syspath({}, {})",
212+
print_path_for_python(&site_packages),
213+
with_pth
242214
))
243215
.await
244216
}

src/pyo3_runner.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ pub(crate) fn python_thread_main(mut receiver: mpsc::Receiver<PyCommand>) {
3131
py.eval(&c_code, Some(&globals), None)
3232
.and_then(|obj| py_any_to_json(py, &obj))
3333
}
34-
CmdType::RunFile(file) => {
35-
handle_run_file(py, &globals, file)
36-
}
34+
CmdType::RunFile(file) => handle_run_file(py, &globals, file),
3735
CmdType::ReadVariable(var_name) => {
3836
get_py_object(&globals, &var_name).and_then(|obj| py_any_to_json(py, &obj))
3937
}
@@ -79,7 +77,7 @@ fn handle_run_file(
7977
globals: &pyo3::Bound<'_, PyDict>,
8078
file: std::path::PathBuf,
8179
) -> PyResult<Value> {
82-
let code = format!(
80+
let code = format!(
8381
r#"
8482
import sys
8583
sys.path.insert(0, {})

src/rustpython_runner.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,22 @@ pub(crate) fn python_thread_main(mut receiver: mpsc::Receiver<PyCommand>) {
2424

2525
interp.enter(|vm| {
2626
let scope = vm.new_scope_with_builtins();
27+
vm.run_code_string(
28+
scope.clone(),
29+
"import sys; sys.path.append('./')",
30+
"<init>".into(),
31+
)
32+
.unwrap();
2733
while let Some(cmd) = receiver.blocking_recv() {
2834
let result = match &cmd.cmd_type {
2935
CmdType::RunCode(code) => vm
3036
.run_code_string(scope.clone(), code, "<string, run>".to_owned())
3137
.map(|_| Value::Null),
3238
CmdType::EvalCode(code) => eval::eval(vm, code, scope.clone(), "<string, eval>")
3339
.and_then(|obj| py_to_json(vm, &obj)),
34-
CmdType::RunFile(file) => {
35-
handle_run_file(vm, scope.clone(), file).map(|_| Value::Null)
36-
}
40+
CmdType::RunFile(file) => vm
41+
.run_script(scope.clone(), file.to_str().unwrap())
42+
.map(|_| Value::Null),
3743
CmdType::ReadVariable(var_name) => {
3844
read_variable(vm, scope.clone(), var_name).and_then(|obj| py_to_json(vm, &obj))
3945
}
@@ -55,16 +61,6 @@ pub(crate) fn python_thread_main(mut receiver: mpsc::Receiver<PyCommand>) {
5561
});
5662
}
5763

58-
fn handle_run_file(
59-
vm: &VirtualMachine,
60-
scope: Scope,
61-
file: &std::path::PathBuf,
62-
) -> PyResult<()> {
63-
let dir = file.parent().unwrap().to_str().unwrap();
64-
vm.insert_sys_path(vm.new_pyobj(dir))?;
65-
vm.run_script(scope, file.to_str().unwrap())
66-
}
67-
6864
fn read_variable(vm: &VirtualMachine, scope: Scope, var_name: &str) -> PyResult<PyObjectRef> {
6965
let parts: Vec<String> = var_name.split('.').map(|s| s.to_owned()).collect();
7066
let mut obj = scope.globals.get_item(&parts[0], vm)?;
@@ -161,6 +157,7 @@ fn py_to_json(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Value> {
161157
}
162158
return Ok(Value::Object(map));
163159
}
160+
// fallback for types that can convert to primitives
164161
if let Some(b) = obj.clone().try_into_value::<bool>(vm).ok() {
165162
return Ok(Value::Bool(b));
166163
}
@@ -174,6 +171,7 @@ fn py_to_json(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Value> {
174171
}
175172

176173
// Fallback for other types
174+
177175
let fallback = obj.str(vm)?;
178176
Ok(Value::String(fallback.to_string()))
179177
}

src/set_venv.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import sys
2+
3+
def add_venv_libs_to_syspath(site_packages, with_pth=False):
4+
"""
5+
Adds the site-packages folder (and .pth entries) from a virtual environment to sys.path.
6+
7+
Args:
8+
venv_path (str): Path to the root of the virtual environment.
9+
"""
10+
11+
# Add site-packages itself
12+
if site_packages not in sys.path:
13+
sys.path.insert(0, site_packages)
14+
15+
# Process .pth files inside site-packages
16+
if with_pth:
17+
import os
18+
for entry in os.listdir(site_packages):
19+
if entry.endswith(".pth"):
20+
pth_file = os.path.join(site_packages, entry)
21+
try:
22+
with open(pth_file, "r") as f:
23+
for line in f:
24+
line = line.strip()
25+
if not line or line.startswith("#"):
26+
continue
27+
if line.startswith("import "):
28+
# Execute import statements inside .pth files
29+
exec(line, globals(), locals())
30+
else:
31+
# Treat as a path
32+
extra_path = os.path.join(site_packages, line)
33+
if extra_path not in sys.path:
34+
sys.path.insert(0, extra_path)
35+
except Exception as e:
36+
print(f"Warning: Could not process {pth_file}: {e}")
37+
38+
return site_packages

0 commit comments

Comments
 (0)