1use super::PyAnyMethods as _;
2use super::PyDict;
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::py_result_ext::PyResultExt;
5use crate::{ffi, Bound, PyAny, PyErr, PyResult, Python};
6use std::ffi::CStr;
7
8#[repr(transparent)]
13pub struct PyCode(PyAny);
14
15#[cfg(not(any(Py_LIMITED_API, PyPy)))]
16pyobject_native_type_core!(
17 PyCode,
18 pyobject_native_static_type_object!(ffi::PyCode_Type),
19 #checkfunction=ffi::PyCode_Check
20);
21
22#[cfg(any(Py_LIMITED_API, PyPy))]
23pyobject_native_type_named!(PyCode);
24
25#[cfg(any(Py_LIMITED_API, PyPy))]
26impl crate::PyTypeCheck for PyCode {
27 const NAME: &'static str = "PyCode";
28 #[cfg(feature = "experimental-inspect")]
29 const PYTHON_TYPE: &'static str = "types.CodeType";
30
31 fn type_check(object: &Bound<'_, PyAny>) -> bool {
32 let py = object.py();
33 static TYPE: crate::sync::PyOnceLock<crate::Py<super::PyType>> =
34 crate::sync::PyOnceLock::new();
35
36 TYPE.import(py, "types", "CodeType")
37 .and_then(|ty| object.is_instance(ty))
38 .unwrap_or_default()
39 }
40}
41
42pub enum PyCodeInput {
44 Eval,
46 File,
48}
49
50impl PyCode {
51 pub fn compile<'py>(
57 py: Python<'py>,
58 code: &CStr,
59 filename: &CStr,
60 input: PyCodeInput,
61 ) -> PyResult<Bound<'py, PyCode>> {
62 let start = match input {
63 PyCodeInput::Eval => ffi::Py_eval_input,
64 PyCodeInput::File => ffi::Py_file_input,
65 };
66 unsafe {
67 ffi::Py_CompileString(code.as_ptr(), filename.as_ptr(), start)
68 .assume_owned_or_err(py)
69 .cast_into_unchecked()
70 }
71 }
72}
73
74pub trait PyCodeMethods<'py> {
80 fn run(
85 &self,
86 globals: Option<&Bound<'py, PyDict>>,
87 locals: Option<&Bound<'py, PyDict>>,
88 ) -> PyResult<Bound<'py, PyAny>>;
89}
90
91impl<'py> PyCodeMethods<'py> for Bound<'py, PyCode> {
92 fn run(
93 &self,
94 globals: Option<&Bound<'py, PyDict>>,
95 locals: Option<&Bound<'py, PyDict>>,
96 ) -> PyResult<Bound<'py, PyAny>> {
97 let mptr = unsafe {
98 ffi::compat::PyImport_AddModuleRef(ffi::c_str!("__main__").as_ptr())
99 .assume_owned_or_err(self.py())?
100 };
101 let attr = mptr.getattr(crate::intern!(self.py(), "__dict__"))?;
102 let globals = match globals {
103 Some(globals) => globals,
104 None => attr.cast::<PyDict>()?,
105 };
106 let locals = locals.unwrap_or(globals);
107
108 let builtins_s = crate::intern!(self.py(), "__builtins__");
116 let has_builtins = globals.contains(builtins_s)?;
117 if !has_builtins {
118 crate::sync::with_critical_section(globals, || {
119 let has_builtins = globals.contains(builtins_s)?;
121 if !has_builtins {
122 let builtins = unsafe { ffi::PyEval_GetBuiltins() };
124
125 if unsafe {
128 ffi::PyDict_SetItem(globals.as_ptr(), builtins_s.as_ptr(), builtins)
129 } == -1
130 {
131 return Err(PyErr::fetch(self.py()));
132 }
133 }
134 Ok(())
135 })?;
136 }
137
138 unsafe {
139 ffi::PyEval_EvalCode(self.as_ptr(), globals.as_ptr(), locals.as_ptr())
140 .assume_owned_or_err(self.py())
141 }
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 #[test]
148 #[cfg(not(any(Py_LIMITED_API, PyPy)))]
149 fn test_type_object() {
150 use crate::types::PyTypeMethods;
151 use crate::{PyTypeInfo, Python};
152
153 Python::attach(|py| {
154 assert_eq!(super::PyCode::type_object(py).name().unwrap(), "code");
155 })
156 }
157}