Implement `eval` and `run` to execute Python code

This commit is contained in:
Murarth 2015-08-02 09:29:47 -07:00
parent 74cdb841c6
commit 376ce21f2d
3 changed files with 103 additions and 3 deletions

View File

@ -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 {

View File

@ -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;

View File

@ -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);
}
}