1use crate::ffi_ptr_ext::FfiPtrExt;
2use crate::sealed::Sealed;
3use crate::types::{PyCode, PyDict};
4use crate::PyAny;
5use crate::{ffi, Bound, PyResult, Python};
6use pyo3_ffi::PyObject;
7use std::ffi::CStr;
8
9#[repr(transparent)]
14pub struct PyFrame(PyAny);
15
16pyobject_native_type_core!(
17 PyFrame,
18 pyobject_native_static_type_object!(ffi::PyFrame_Type),
19 "types",
20 "FrameType",
21 #checkfunction=ffi::PyFrame_Check
22);
23
24impl PyFrame {
25 pub fn new<'py>(
27 py: Python<'py>,
28 file_name: &CStr,
29 func_name: &CStr,
30 line_number: i32,
31 ) -> PyResult<Bound<'py, PyFrame>> {
32 let state = unsafe { ffi::compat::PyThreadState_GetUnchecked() };
34 let code = PyCode::empty(py, file_name, func_name, line_number);
35 let globals = PyDict::new(py);
36 let locals = PyDict::new(py);
37
38 unsafe {
39 Ok(ffi::PyFrame_New(
40 state,
41 code.into_ptr().cast(),
42 globals.as_ptr(),
43 locals.as_ptr(),
44 )
45 .cast::<PyObject>()
46 .assume_owned_or_err(py)?
47 .cast_into_unchecked::<PyFrame>())
48 }
49 }
50}
51
52#[doc(alias = "PyFrame")]
58pub trait PyFrameMethods<'py>: Sealed {
59 fn line_number(&self) -> i32;
61}
62
63impl<'py> PyFrameMethods<'py> for Bound<'py, PyFrame> {
64 fn line_number(&self) -> i32 {
65 unsafe { ffi::PyFrame_GetLineNumber(self.as_ptr().cast()) }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_frame_creation() {
75 Python::attach(|py| {
76 let frame = PyFrame::new(py, c"file.py", c"func", 42).unwrap();
77 assert_eq!(frame.line_number(), 42);
78 });
79 }
80}