use crate::ffi_ptr_ext::FfiPtrExt;
use crate::sealed::Sealed;
use crate::types::{PyCode, PyDict};
use crate::PyAny;
use crate::{ffi, Bound, PyResult, Python};
use pyforge_ffi::PyObject;
use std::ffi::CStr;
#[repr(transparent)]
pub struct PyFrame(PyAny);
pyobject_native_type_core!(
PyFrame,
pyobject_native_static_type_object!(ffi::PyFrame_Type),
"types",
"FrameType",
#checkfunction=ffi::PyFrame_Check
);
impl PyFrame {
pub fn new<'py>(
py: Python<'py>,
file_name: &CStr,
func_name: &CStr,
line_number: i32,
) -> PyResult<Bound<'py, PyFrame>> {
let state = unsafe { ffi::compat::PyThreadState_GetUnchecked() };
let code = PyCode::empty(py, file_name, func_name, line_number);
let globals = PyDict::new(py);
let locals = PyDict::new(py);
unsafe {
Ok(ffi::PyFrame_New(
state,
code.into_ptr().cast(),
globals.as_ptr(),
locals.as_ptr(),
)
.cast::<PyObject>()
.assume_owned_or_err(py)?
.cast_into_unchecked::<PyFrame>())
}
}
}
#[doc(alias = "PyFrame")]
pub trait PyFrameMethods<'py>: Sealed {
fn line_number(&self) -> i32;
}
impl<'py> PyFrameMethods<'py> for Bound<'py, PyFrame> {
fn line_number(&self) -> i32 {
unsafe { ffi::PyFrame_GetLineNumber(self.as_ptr().cast()) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_frame_creation() {
Python::attach(|py| {
let frame = PyFrame::new(py, c"file.py", c"func", 42).unwrap();
assert_eq!(frame.line_number(), 42);
});
}
}