1use super::PyAnyMethods as _;
2use super::PyDict;
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::py_result_ext::PyResultExt;
5#[cfg(any(Py_LIMITED_API, PyPy))]
6use crate::sync::PyOnceLock;
7#[cfg(any(Py_LIMITED_API, PyPy))]
8use crate::types::{PyType, PyTypeMethods};
9#[cfg(any(Py_LIMITED_API, PyPy))]
10use crate::Py;
11use crate::{ffi, Bound, PyAny, PyErr, PyResult, Python};
12use std::ffi::CStr;
13
14#[repr(transparent)]
19pub struct PyCode(PyAny);
20
21#[cfg(not(any(Py_LIMITED_API, PyPy)))]
22pyobject_native_type_core!(
23 PyCode,
24 pyobject_native_static_type_object!(ffi::PyCode_Type),
25 "types",
26 "CodeType",
27 #checkfunction=ffi::PyCode_Check
28);
29
30#[cfg(any(Py_LIMITED_API, PyPy))]
31pyobject_native_type_core!(
32 PyCode,
33 |py| {
34 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
35 TYPE.import(py, "types", "CodeType").unwrap().as_type_ptr()
36 },
37 "types",
38 "CodeType"
39);
40
41pub enum PyCodeInput {
43 Eval,
45 File,
47}
48
49impl PyCode {
50 pub fn compile<'py>(
56 py: Python<'py>,
57 code: &CStr,
58 filename: &CStr,
59 input: PyCodeInput,
60 ) -> PyResult<Bound<'py, PyCode>> {
61 let start = match input {
62 PyCodeInput::Eval => ffi::Py_eval_input,
63 PyCodeInput::File => ffi::Py_file_input,
64 };
65 unsafe {
66 ffi::Py_CompileString(code.as_ptr(), filename.as_ptr(), start)
67 .assume_owned_or_err(py)
68 .cast_into_unchecked()
69 }
70 }
71
72 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
73 pub(crate) fn empty<'py>(
74 py: Python<'py>,
75 file_name: &CStr,
76 func_name: &CStr,
77 first_line_number: i32,
78 ) -> Bound<'py, PyCode> {
79 unsafe {
80 ffi::PyCode_NewEmpty(file_name.as_ptr(), func_name.as_ptr(), first_line_number)
81 .cast::<ffi::PyObject>()
82 .assume_owned(py)
83 .cast_into_unchecked()
84 }
85 }
86}
87
88pub trait PyCodeMethods<'py> {
94 fn run(
99 &self,
100 globals: Option<&Bound<'py, PyDict>>,
101 locals: Option<&Bound<'py, PyDict>>,
102 ) -> PyResult<Bound<'py, PyAny>>;
103}
104
105impl<'py> PyCodeMethods<'py> for Bound<'py, PyCode> {
106 fn run(
107 &self,
108 globals: Option<&Bound<'py, PyDict>>,
109 locals: Option<&Bound<'py, PyDict>>,
110 ) -> PyResult<Bound<'py, PyAny>> {
111 let mptr = unsafe {
112 ffi::compat::PyImport_AddModuleRef(c"__main__".as_ptr())
113 .assume_owned_or_err(self.py())?
114 };
115 let attr = mptr.getattr(crate::intern!(self.py(), "__dict__"))?;
116 let globals = match globals {
117 Some(globals) => globals,
118 None => attr.cast::<PyDict>()?,
119 };
120 let locals = locals.unwrap_or(globals);
121
122 let builtins_s = crate::intern!(self.py(), "__builtins__");
130 let has_builtins = globals.contains(builtins_s)?;
131 if !has_builtins {
132 crate::sync::critical_section::with_critical_section(globals, || {
133 let has_builtins = globals.contains(builtins_s)?;
135 if !has_builtins {
136 let builtins = unsafe { ffi::PyEval_GetBuiltins() };
138
139 if unsafe {
142 ffi::PyDict_SetItem(globals.as_ptr(), builtins_s.as_ptr(), builtins)
143 } == -1
144 {
145 return Err(PyErr::fetch(self.py()));
146 }
147 }
148 Ok(())
149 })?;
150 }
151
152 unsafe {
153 ffi::PyEval_EvalCode(self.as_ptr(), globals.as_ptr(), locals.as_ptr())
154 .assume_owned_or_err(self.py())
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 #[test]
162 fn test_type_object() {
163 use crate::types::PyTypeMethods;
164 use crate::{PyTypeInfo, Python};
165
166 Python::attach(|py| {
167 assert_eq!(super::PyCode::type_object(py).name().unwrap(), "code");
168 })
169 }
170}