Skip to main content

pyo3/types/
frame.rs

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/// Represents a Python frame.
10///
11/// Values of this type are accessed via PyO3's smart pointers, e.g. as
12/// [`Py<PyFrame>`][crate::Py] or [`Bound<'py, PyFrame>`][crate::Bound].
13#[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    /// Creates a new frame object.
26    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        // Safety: Thread is attached because we have a python token
33        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/// Implementation of functionality for [`PyFrame`].
53///
54/// These methods are defined for the `Bound<'py, PyFrame>` smart pointer, so to use method call
55/// syntax these methods are separated into a trait, because stable Rust does not yet support
56/// `arbitrary_self_types`.
57#[doc(alias = "PyFrame")]
58pub trait PyFrameMethods<'py>: Sealed {
59    /// Returns the line number of the current instruction in the frame.
60    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}