1use super::PyDict;
4use super::{PyAnyMethods as _, PyDictMethods as _};
5use crate::ffi_ptr_ext::FfiPtrExt;
6use crate::py_result_ext::PyResultExt;
7#[cfg(any(Py_LIMITED_API, PyPy))]
8use crate::sync::PyOnceLock;
9#[cfg(any(Py_LIMITED_API, PyPy))]
10use crate::types::{PyType, PyTypeMethods};
11#[cfg(any(Py_LIMITED_API, PyPy))]
12use crate::Py;
13use crate::{ffi, Bound, PyAny, PyResult, Python};
14use core::ffi::CStr;
15
16#[repr(transparent)]
21pub struct PyCode(PyAny);
22
23#[cfg(not(any(Py_LIMITED_API, PyPy)))]
24pyobject_native_type_core!(
25 PyCode,
26 pyobject_native_static_type_object!(ffi::PyCode_Type),
27 "types",
28 "CodeType",
29 #checkfunction=ffi::PyCode_Check
30);
31
32#[cfg(any(Py_LIMITED_API, PyPy))]
33pyobject_native_type_core!(
34 PyCode,
35 |py| {
36 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
37 TYPE.import(py, "types", "CodeType").unwrap().as_type_ptr()
38 },
39 "types",
40 "CodeType"
41);
42
43pub enum PyCodeInput {
45 Eval,
47 File,
49}
50
51impl PyCode {
52 pub fn compile<'py>(
58 py: Python<'py>,
59 code: &CStr,
60 filename: &CStr,
61 input: PyCodeInput,
62 ) -> PyResult<Bound<'py, PyCode>> {
63 let start = match input {
64 PyCodeInput::Eval => ffi::Py_eval_input,
65 PyCodeInput::File => ffi::Py_file_input,
66 };
67 unsafe {
68 ffi::Py_CompileString(code.as_ptr(), filename.as_ptr(), start)
69 .assume_owned_or_err(py)
70 .cast_into_unchecked()
71 }
72 }
73
74 #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))]
75 pub(crate) fn empty<'py>(
76 py: Python<'py>,
77 file_name: &CStr,
78 func_name: &CStr,
79 first_line_number: i32,
80 ) -> Bound<'py, PyCode> {
81 unsafe {
82 ffi::PyCode_NewEmpty(file_name.as_ptr(), func_name.as_ptr(), first_line_number)
83 .cast::<ffi::PyObject>()
84 .assume_owned(py)
85 .cast_into_unchecked()
86 }
87 }
88}
89
90pub trait PyCodeMethods<'py> {
96 fn run(
101 &self,
102 globals: Option<&Bound<'py, PyDict>>,
103 locals: Option<&Bound<'py, PyDict>>,
104 ) -> PyResult<Bound<'py, PyAny>>;
105}
106
107impl<'py> PyCodeMethods<'py> for Bound<'py, PyCode> {
108 fn run(
109 &self,
110 globals: Option<&Bound<'py, PyDict>>,
111 locals: Option<&Bound<'py, PyDict>>,
112 ) -> PyResult<Bound<'py, PyAny>> {
113 let mptr = unsafe {
114 ffi::compat::PyImport_AddModuleRef(c"__main__".as_ptr())
115 .assume_owned_or_err(self.py())?
116 };
117 let attr = mptr.getattr(crate::intern!(self.py(), "__dict__"))?;
118 let globals = match globals {
119 Some(globals) => globals,
120 None => attr.cast::<PyDict>()?,
121 };
122 let locals = locals.unwrap_or(globals);
123
124 let builtins_s = crate::intern!(self.py(), "__builtins__");
132 let builtins = unsafe { ffi::PyEval_GetBuiltins().assume_borrowed_unchecked(self.py()) };
133 globals.set_default(builtins_s, builtins)?;
134 unsafe {
135 ffi::PyEval_EvalCode(self.as_ptr(), globals.as_ptr(), locals.as_ptr())
136 .assume_owned_or_err(self.py())
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 #[test]
144 fn test_type_object() {
145 use crate::types::PyTypeMethods;
146 use crate::{PyTypeInfo, Python};
147
148 Python::attach(|py| {
149 assert_eq!(super::PyCode::type_object(py).name().unwrap(), "code");
150 })
151 }
152}