use super::PyDict;
use super::{PyAnyMethods as _, PyDictMethods as _};
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::py_result_ext::PyResultExt;
#[cfg(any(Py_LIMITED_API, PyPy))]
use crate::sync::PyOnceLock;
#[cfg(any(Py_LIMITED_API, PyPy))]
use crate::types::{PyType, PyTypeMethods};
#[cfg(any(Py_LIMITED_API, PyPy))]
use crate::Py;
use crate::{ffi, Bound, PyAny, PyResult, Python};
use core::ffi::CStr;
#[repr(transparent)]
pub struct PyCode(PyAny);
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
pyobject_native_type_core!(
PyCode,
pyobject_native_static_type_object!(ffi::PyCode_Type),
"types",
"CodeType",
#checkfunction=ffi::PyCode_Check
);
#[cfg(any(Py_LIMITED_API, PyPy))]
pyobject_native_type_core!(
PyCode,
|py| {
static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
TYPE.import(py, "types", "CodeType").unwrap().as_type_ptr()
},
"types",
"CodeType"
);
pub enum PyCodeInput {
Eval,
File,
}
impl PyCode {
pub fn compile<'py>(
py: Python<'py>,
code: &CStr,
filename: &CStr,
input: PyCodeInput,
) -> PyResult<Bound<'py, PyCode>> {
let start = match input {
PyCodeInput::Eval => ffi::Py_eval_input,
PyCodeInput::File => ffi::Py_file_input,
};
unsafe {
ffi::Py_CompileString(code.as_ptr(), filename.as_ptr(), start)
.assume_owned_or_err(py)
.cast_into_unchecked()
}
}
#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
pub(crate) fn empty<'py>(
py: Python<'py>,
file_name: &CStr,
func_name: &CStr,
first_line_number: i32,
) -> Bound<'py, PyCode> {
unsafe {
ffi::PyCode_NewEmpty(file_name.as_ptr(), func_name.as_ptr(), first_line_number)
.cast::<ffi::PyObject>()
.assume_owned(py)
.cast_into_unchecked()
}
}
}
pub trait PyCodeMethods<'py> {
fn run(
&self,
globals: Option<&Bound<'py, PyDict>>,
locals: Option<&Bound<'py, PyDict>>,
) -> PyResult<Bound<'py, PyAny>>;
}
impl<'py> PyCodeMethods<'py> for Bound<'py, PyCode> {
fn run(
&self,
globals: Option<&Bound<'py, PyDict>>,
locals: Option<&Bound<'py, PyDict>>,
) -> PyResult<Bound<'py, PyAny>> {
let mptr = unsafe {
ffi::compat::PyImport_AddModuleRef(c"__main__".as_ptr())
.assume_owned_or_err(self.py())?
};
let attr = mptr.getattr(crate::intern!(self.py(), "__dict__"))?;
let globals = match globals {
Some(globals) => globals,
None => attr.cast::<PyDict>()?,
};
let locals = locals.unwrap_or(globals);
let builtins_s = crate::intern!(self.py(), "__builtins__");
let builtins = unsafe { ffi::PyEval_GetBuiltins().assume_borrowed_unchecked(self.py()) };
globals.set_default(builtins_s, builtins)?;
unsafe {
ffi::PyEval_EvalCode(self.as_ptr(), globals.as_ptr(), locals.as_ptr())
.assume_owned_or_err(self.py())
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_type_object() {
use crate::types::PyTypeMethods;
use crate::{PyTypeInfo, Python};
Python::attach(|py| {
assert_eq!(super::PyCode::type_object(py).name().unwrap(), "code");
})
}
}