1use super::PyDict;
2use super::{PyAnyMethods as _, PyDictMethods as _};
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, PyResult, Python};
12use core::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 builtins = unsafe { ffi::PyEval_GetBuiltins().assume_borrowed_unchecked(self.py()) };
131 globals.set_default(builtins_s, builtins)?;
132 unsafe {
133 ffi::PyEval_EvalCode(self.as_ptr(), globals.as_ptr(), locals.as_ptr())
134 .assume_owned_or_err(self.py())
135 }
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 #[test]
142 fn test_type_object() {
143 use crate::types::PyTypeMethods;
144 use crate::{PyTypeInfo, Python};
145
146 Python::attach(|py| {
147 assert_eq!(super::PyCode::type_object(py).name().unwrap(), "code");
148 })
149 }
150}