Skip to content

Commit de06fc0

Browse files
Add eval function to c-api (#7927)
* Add eval function to c-api * Extract `Py_CompileString` start values into constants
1 parent ae3804f commit de06fc0

3 files changed

Lines changed: 97 additions & 1 deletion

File tree

crates/capi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ license.workspace = true
1212
crate-type = ["cdylib", "rlib"]
1313

1414
[dependencies]
15-
rustpython-vm = { workspace = true, features = ["threading"] }
15+
rustpython-vm = { workspace = true, features = ["threading", "compiler"] }
1616
rustpython-stdlib = {workspace = true, features = ["threading"] }
1717

1818
[dev-dependencies]

crates/capi/src/ceval.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use crate::pystate::with_vm;
2+
use core::ffi::{CStr, c_char, c_int};
3+
use core::ptr::NonNull;
4+
use rustpython_vm::builtins::{PyCode, PyDict};
5+
use rustpython_vm::compiler::Mode;
6+
use rustpython_vm::function::ArgMapping;
7+
use rustpython_vm::scope::Scope;
8+
use rustpython_vm::{AsObject, PyObject, TryFromObject};
9+
10+
const PY_SINGLE_INPUT: c_int = 256;
11+
const PY_FILE_INPUT: c_int = 257;
12+
const PY_EVAL_INPUT: c_int = 258;
13+
const PY_FUNC_TYPE_INPUT: c_int = 345;
14+
15+
#[unsafe(no_mangle)]
16+
pub unsafe extern "C" fn Py_CompileString(
17+
code: *const c_char,
18+
filename: *const c_char,
19+
start: c_int,
20+
) -> *mut PyObject {
21+
with_vm(|vm| {
22+
let code = unsafe { CStr::from_ptr(code) }.to_str().map_err(|_| {
23+
vm.new_system_error("Py_CompileString called with non UTF-8 code string")
24+
})?;
25+
let filename = unsafe { CStr::from_ptr(filename) }
26+
.to_str()
27+
.map_err(|_| vm.new_system_error("Py_CompileString called with non UTF-8 filename"))?;
28+
29+
let mode = match start {
30+
PY_SINGLE_INPUT => Mode::Single,
31+
PY_FILE_INPUT => Mode::Exec,
32+
PY_EVAL_INPUT => Mode::Eval,
33+
PY_FUNC_TYPE_INPUT => Mode::BlockExpr,
34+
_ => {
35+
return Err(
36+
vm.new_system_error("Invalid start argument passed to Py_CompileString")
37+
);
38+
}
39+
};
40+
41+
vm.compile(code, mode, filename.to_owned())
42+
.map_err(|err| vm.new_syntax_error(&err, Some(code)))
43+
})
44+
}
45+
46+
#[unsafe(no_mangle)]
47+
pub unsafe extern "C" fn PyEval_EvalCode(
48+
co: *mut PyObject,
49+
globals: *mut PyObject,
50+
locals: *mut PyObject,
51+
) -> *mut PyObject {
52+
with_vm(|vm| {
53+
let code = unsafe { &*co }.try_downcast_ref::<PyCode>(vm)?;
54+
let globals = unsafe { &*globals }.try_downcast_ref::<PyDict>(vm)?;
55+
let locals = NonNull::new(locals)
56+
.map(|ptr| ArgMapping::try_from_object(vm, unsafe { ptr.as_ref() }.to_owned()))
57+
.transpose()?;
58+
59+
let scope = Scope::with_builtins(locals, globals.to_owned(), vm);
60+
61+
vm.run_code_obj(code.to_owned(), scope)
62+
})
63+
}
64+
65+
#[unsafe(no_mangle)]
66+
pub extern "C" fn PyEval_GetBuiltins() -> *mut PyObject {
67+
with_vm(|vm| {
68+
vm.current_frame().map_or_else(
69+
|| vm.builtins.as_object().as_raw(),
70+
|frame| frame.builtins.as_object().as_raw(),
71+
)
72+
})
73+
}
74+
75+
#[cfg(false)]
76+
mod tests {
77+
use pyo3::exceptions::PyException;
78+
use pyo3::prelude::*;
79+
80+
#[test]
81+
fn test_code_eval() {
82+
Python::attach(|py| {
83+
let result = py.eval(c"1 + 1", None, None).unwrap();
84+
assert_eq!(result.extract::<u32>().unwrap(), 2);
85+
})
86+
}
87+
88+
#[test]
89+
fn test_code_run_exception() {
90+
Python::attach(|py| {
91+
let err = py.run(c"raise Exception()", None, None).unwrap_err();
92+
assert!(err.is_instance_of::<PyException>(py));
93+
})
94+
}
95+
}

crates/capi/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern crate alloc;
1010

1111
pub mod abstract_;
1212
pub mod bytesobject;
13+
pub mod ceval;
1314
pub mod import;
1415
pub mod longobject;
1516
pub mod object;

0 commit comments

Comments
 (0)