use std::{ffi::CStr, sync::Once};
#[allow(deprecated)]
use pyo3::GILPool;
use pyo3::{ffi::*, PyErr, Python};
#[derive(Debug)]
pub struct SubInterpreter {
state: *mut PyThreadState,
}
unsafe impl Send for SubInterpreter {}
unsafe impl Sync for SubInterpreter {}
static GLOBAL_INIT: Once = Once::new();
impl SubInterpreter {
pub fn new() -> Result<Self, PyError> {
GLOBAL_INIT.call_once_force(|_| {
unsafe {
if Py_IsInitialized() == 0 {
Py_InitializeEx(0);
PyEval_SaveThread();
}
}
Python::with_gil(|py| {
py.import_bound("decimal").unwrap();
});
});
unsafe {
let state = PyInterpreterState_ThreadHead(PyInterpreterState_Main());
PyEval_RestoreThread(state);
}
let config = PyInterpreterConfig {
use_main_obmalloc: 0,
allow_fork: 0,
allow_exec: 0,
allow_threads: 0,
allow_daemon_threads: 0,
check_multi_interp_extensions: 1,
gil: PyInterpreterConfig_OWN_GIL, };
let mut state: *mut PyThreadState = std::ptr::null_mut();
let status: PyStatus = unsafe { Py_NewInterpreterFromConfig(&mut state, &config) };
if unsafe { PyStatus_IsError(status) } == 1 {
let msg = unsafe { CStr::from_ptr(status.err_msg) };
return Err(anyhow::anyhow!(
"failed to create sub-interpreter: {}",
msg.to_string_lossy()
)
.into());
}
assert_eq!(state, unsafe { PyThreadState_Get() });
unsafe { PyEval_SaveThread() };
Ok(Self { state })
}
pub fn with_gil<F, R>(&self, f: F) -> Result<R, PyError>
where
F: for<'py> FnOnce(Python<'py>) -> Result<R, PyError>,
{
unsafe { PyEval_RestoreThread(self.state) };
#[allow(deprecated)]
let pool = unsafe { GILPool::new() };
let ret = f(pool.python());
drop(pool);
unsafe { PyEval_SaveThread() };
ret
}
pub fn run(&self, code: &str) -> Result<(), PyError> {
self.with_gil(|py| py.run_bound(code, None, None).map_err(|e| e.into()))
}
}
impl Drop for SubInterpreter {
fn drop(&mut self) {
unsafe {
PyEval_RestoreThread(self.state);
assert_eq!(self.state, PyThreadState_GET());
Py_EndInterpreter(self.state);
}
}
}
#[derive(Debug)]
pub struct PyError {
anyhow: anyhow::Error,
}
impl From<PyErr> for PyError {
fn from(err: PyErr) -> Self {
Self {
anyhow: anyhow::anyhow!(err.to_string()),
}
}
}
impl From<anyhow::Error> for PyError {
fn from(err: anyhow::Error) -> Self {
Self { anyhow: err }
}
}
impl From<PyError> for anyhow::Error {
fn from(err: PyError) -> Self {
err.anyhow
}
}