Implement `eval` and `run` to execute Python code
This commit is contained in:
parent
74cdb841c6
commit
376ce21f2d
|
@ -12,6 +12,10 @@ pub const PyCF_SOURCE_IS_UTF8 : c_int = 0x0100;
|
|||
pub const PyCF_DONT_IMPLY_DEDENT : c_int = 0x0200;
|
||||
pub const PyCF_ONLY_AST : c_int = 0x0400;
|
||||
|
||||
pub const Py_single_input: c_int = 256;
|
||||
pub const Py_file_input: c_int = 257;
|
||||
pub const Py_eval_input: c_int = 258;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PyCompilerFlags {
|
||||
|
|
|
@ -2,6 +2,16 @@ use libc::{c_char, c_int, wchar_t};
|
|||
use object::*;
|
||||
use pystate::PyThreadState;
|
||||
|
||||
pub const Py_single_input: c_int = 256;
|
||||
pub const Py_file_input: c_int = 257;
|
||||
pub const Py_eval_input: c_int = 258;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PyCompilerFlags {
|
||||
cf_flags : c_int
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn Py_SetProgramName(arg1: *mut wchar_t) -> ();
|
||||
pub fn Py_GetProgramName() -> *mut wchar_t;
|
||||
|
@ -40,7 +50,11 @@ extern "C" {
|
|||
// arg3: c_int,
|
||||
// arg4: c_int)
|
||||
// -> *mut _node;
|
||||
|
||||
|
||||
pub fn PyRun_StringFlags(code: *const c_char, start: c_int,
|
||||
globals: *mut PyObject, locals: *mut PyObject,
|
||||
flags: *mut PyCompilerFlags) -> *mut PyObject;
|
||||
|
||||
pub fn Py_CompileString(arg1: *const c_char,
|
||||
arg2: *const c_char, arg3: c_int)
|
||||
-> *mut PyObject;
|
||||
|
|
|
@ -17,10 +17,12 @@
|
|||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use std;
|
||||
use std::ffi::CString;
|
||||
use std::marker::PhantomData;
|
||||
use libc::c_int;
|
||||
use ffi;
|
||||
use objects::{PyObject, PyType, PyBool, PyModule};
|
||||
use err::PyResult;
|
||||
use objects::{PyObject, PyType, PyBool, PyDict, PyModule};
|
||||
use err::{self, PyErr, PyResult};
|
||||
use pythonrun::GILGuard;
|
||||
|
||||
/// Marker type that indicates that the GIL is currently held.
|
||||
|
@ -150,6 +152,62 @@ impl<'p> Python<'p> {
|
|||
result
|
||||
}
|
||||
|
||||
/// Evaluates a Python expression in the given context and returns the result.
|
||||
///
|
||||
/// If `globals` is `None`, it defaults to Python module `__main__`.
|
||||
/// If `locals` is `None`, it defaults to the value of `globals`.
|
||||
pub fn eval(self, code: &str, globals: Option<&PyDict<'p>>,
|
||||
locals: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>> {
|
||||
self.run_code(code, ffi::Py_eval_input, globals, locals)
|
||||
}
|
||||
|
||||
/// Executes one or more Python statements in the given context.
|
||||
///
|
||||
/// If `globals` is `None`, it defaults to Python module `__main__`.
|
||||
/// If `locals` is `None`, it defaults to the value of `globals`.
|
||||
pub fn run(self, code: &str, globals: Option<&PyDict<'p>>,
|
||||
locals: Option<&PyDict<'p>>) -> PyResult<'p, ()> {
|
||||
try!(self.run_code(code, ffi::Py_file_input, globals, locals));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs code in the given context.
|
||||
/// `start` indicates the type of input expected:
|
||||
/// one of `Py_single_input`, `Py_file_input`, or `Py_eval_input`.
|
||||
///
|
||||
/// If `globals` is `None`, it defaults to Python module `__main__`.
|
||||
/// If `locals` is `None`, it defaults to the value of `globals`.
|
||||
fn run_code(self, code: &str, start: c_int,
|
||||
globals: Option<&PyDict<'p>>, locals: Option<&PyDict<'p>>)
|
||||
-> PyResult<'p, PyObject<'p>> {
|
||||
let code = CString::new(code).unwrap();
|
||||
|
||||
unsafe {
|
||||
let mptr = ffi::PyImport_AddModule("__main__\0".as_ptr() as *const _);
|
||||
|
||||
if mptr.is_null() {
|
||||
return Err(PyErr::fetch(self));
|
||||
}
|
||||
|
||||
let mdict = ffi::PyModule_GetDict(mptr);
|
||||
|
||||
let globals = match globals {
|
||||
Some(g) => g.as_ptr(),
|
||||
None => mdict,
|
||||
};
|
||||
|
||||
let locals = match locals {
|
||||
Some(l) => l.as_ptr(),
|
||||
None => globals
|
||||
};
|
||||
|
||||
let res_ptr = ffi::PyRun_StringFlags(code.as_ptr(),
|
||||
start, globals, locals, 0 as *mut _);
|
||||
|
||||
err::result_from_owned_ptr(self, res_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the Python builtin value `None`.
|
||||
#[allow(non_snake_case)] // the Python keyword starts with uppercase
|
||||
#[inline]
|
||||
|
@ -188,3 +246,27 @@ impl <'p> std::fmt::Debug for PythonObjectDowncastError<'p> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {Python, PyDict};
|
||||
|
||||
#[test]
|
||||
fn test_eval() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
// Make sure builtin names are accessible
|
||||
let v: i32 = py.eval("min(1, 2)", None, None).unwrap().extract().unwrap();
|
||||
|
||||
assert_eq!(v, 1);
|
||||
|
||||
let d = PyDict::new(py);
|
||||
|
||||
d.set_item("foo", 13).unwrap();
|
||||
|
||||
// Inject our own local namespace
|
||||
let v: i32 = py.eval("foo + 29", None, Some(&d)).unwrap().extract().unwrap();
|
||||
|
||||
assert_eq!(v, 42);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue