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
73pub trait PyCodeMethods<'py> {
79 fn run(
84 &self,
85 globals: Option<&Bound<'py, PyDict>>,
86 locals: Option<&Bound<'py, PyDict>>,
87 ) -> PyResult<Bound<'py, PyAny>>;
88}
89
90impl<'py> PyCodeMethods<'py> for Bound<'py, PyCode> {
91 fn run(
92 &self,
93 globals: Option<&Bound<'py, PyDict>>,
94 locals: Option<&Bound<'py, PyDict>>,
95 ) -> PyResult<Bound<'py, PyAny>> {
96 let mptr = unsafe {
97 ffi::compat::PyImport_AddModuleRef(c"__main__".as_ptr())
98 .assume_owned_or_err(self.py())?
99 };
100 let attr = mptr.getattr(crate::intern!(self.py(), "__dict__"))?;
101 let globals = match globals {
102 Some(globals) => globals,
103 None => attr.cast::<PyDict>()?,
104 };
105 let locals = locals.unwrap_or(globals);
106
107 let builtins_s = crate::intern!(self.py(), "__builtins__");
115 let has_builtins = globals.contains(builtins_s)?;
116 if !has_builtins {
117 crate::sync::critical_section::with_critical_section(globals, || {
118 let has_builtins = globals.contains(builtins_s)?;
120 if !has_builtins {
121 let builtins = unsafe { ffi::PyEval_GetBuiltins() };
123
124 if unsafe {
127 ffi::PyDict_SetItem(globals.as_ptr(), builtins_s.as_ptr(), builtins)
128 } == -1
129 {
130 return Err(PyErr::fetch(self.py()));
131 }
132 }
133 Ok(())
134 })?;
135 }
136
137 unsafe {
138 ffi::PyEval_EvalCode(self.as_ptr(), globals.as_ptr(), locals.as_ptr())
139 .assume_owned_or_err(self.py())
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 #[test]
147 fn test_type_object() {
148 use crate::types::PyTypeMethods;
149 use crate::{PyTypeInfo, Python};
150
151 Python::attach(|py| {
152 assert_eq!(super::PyCode::type_object(py).name().unwrap(), "code");
153 })
154 }
155}