use std::os::raw::c_char;
pub use inline_python_macros::python;
pub use pyo3;
use pyo3::{
ffi,
types::{PyAny, PyDict},
AsPyPointer, FromPyObject, IntoPy, PyErr, PyObject, PyResult, Python,
};
#[doc(hidden)]
pub use std::ffi::CStr;
pub struct Context {
globals: PyObject,
}
impl Context {
pub fn new() -> Self {
let gil = Python::acquire_gil();
let py = gil.python();
match Self::new_with_gil(py) {
Ok(x) => x,
Err(error) => {
error.print(py);
panic!("failed to create python context");
}
}
}
pub fn new_checked() -> PyResult<Self> {
let gil = Python::acquire_gil();
let py = gil.python();
Self::new_with_gil(py)
}
pub fn new_with_gil(py: Python) -> PyResult<Self> {
let main_mod = unsafe { ffi::PyImport_AddModule("__main__\0".as_ptr() as *const _) };
if main_mod.is_null() {
return Err(PyErr::fetch(py));
};
let globals = PyDict::new(py);
if unsafe { ffi::PyDict_Merge(globals.as_ptr(), ffi::PyModule_GetDict(main_mod), 0) != 0 } {
return Err(PyErr::fetch(py));
}
Ok(Self {
globals: globals.into_py(py),
})
}
pub fn globals<'p>(&self, py: Python<'p>) -> &'p PyDict {
unsafe { py.from_borrowed_ptr(self.globals.as_ptr()) }
}
pub fn get_global<T: for<'p> FromPyObject<'p>>(&self, name: &str) -> PyResult<Option<T>> {
self.get_global_with_gil(Python::acquire_gil().python(), name)
}
pub fn get_global_with_gil<'p, T: FromPyObject<'p>>(&self, py: Python<'p>, name: &str) -> PyResult<Option<T>> {
match self.globals(py).get_item(name) {
None => Ok(None),
Some(value) => FromPyObject::extract(value).map(Some),
}
}
}
#[doc(hidden)]
pub fn run_python_code<'p>(py: Python<'p>, context: &Context, compiled_code: &[u8], rust_vars: Option<&PyDict>) -> PyResult<&'p PyAny> {
unsafe {
let rust_vars = rust_vars.unwrap_or_else(|| PyDict::new(py)).as_ptr();
if ffi::PyDict_SetItemString(context.globals.as_ptr(), "RUST\0".as_ptr() as *const _, rust_vars) != 0 {
return Err(PyErr::fetch(py));
}
let compiled_code = python_unmarshal_object_from_bytes(py, compiled_code)?;
let result = ffi::PyEval_EvalCode(compiled_code.as_ptr(), context.globals.as_ptr(), std::ptr::null_mut());
py.from_owned_ptr_or_err(result)
}
}
extern "C" {
fn PyMarshal_ReadObjectFromString(data: *const c_char, len: isize) -> *mut ffi::PyObject;
}
fn python_unmarshal_object_from_bytes(py: Python, data: &[u8]) -> pyo3::PyResult<PyObject> {
unsafe {
let object = PyMarshal_ReadObjectFromString(data.as_ptr() as *const c_char, data.len() as isize);
if object.is_null() {
return Err(PyErr::fetch(py));
}
Ok(PyObject::from_owned_ptr(py, object))
}
}