use std::mem;
use crate::ffi;
use crate::function::AbortOnDrop;
use crate::objects::PyObject;
use crate::python::{PyDrop, Python, PythonObject, ToPythonPointer};
pub struct TraverseError(libc::c_int);
#[derive(Copy, Clone)]
pub struct VisitProc<'a> {
visit: ffi::visitproc,
arg: *mut libc::c_void,
_py: Python<'a>,
}
impl<'a> VisitProc<'a> {
pub fn call<T>(&self, obj: &T) -> Result<(), TraverseError>
where
T: PythonObject,
{
let r = unsafe { (self.visit)(obj.as_ptr(), self.arg) };
if r == 0 {
Ok(())
} else {
Err(TraverseError(r))
}
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_tp_traverse {
($class_name:ident,
/* gc: */ {
/* traverse_proc: */ None,
/* traverse_data: */ [ ]
}) => {
None
};
($class_name:ident,
/* gc: */ {
$traverse_proc: expr,
/* traverse_data: */ []
}) => {{
unsafe extern "C" fn tp_traverse(
slf: *mut $crate::_detail::ffi::PyObject,
visit: $crate::_detail::ffi::visitproc,
arg: *mut $crate::_detail::libc::c_void,
) -> $crate::_detail::libc::c_int {
$crate::py_class::gc::tp_traverse::<$class_name, _>(
concat!(stringify!($class_name), ".__traverse__"),
slf,
visit,
arg,
$traverse_proc,
)
}
Some(tp_traverse)
}};
}
#[doc(hidden)]
pub unsafe fn tp_traverse<C, F>(
location: &str,
slf: *mut ffi::PyObject,
visit: ffi::visitproc,
arg: *mut libc::c_void,
callback: F,
) -> libc::c_int
where
C: PythonObject,
F: FnOnce(&C, Python, VisitProc) -> Result<(), TraverseError>,
{
let guard = AbortOnDrop(location);
let py = Python::assume_gil_acquired();
let visit = VisitProc {
visit,
arg,
_py: py,
};
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<C>();
let ret = match callback(&slf, py, visit) {
Ok(()) => 0,
Err(TraverseError(code)) => code,
};
slf.release_ref(py);
mem::forget(guard);
ret
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_tp_clear {
($class_name:ident) => {{
unsafe extern "C" fn tp_clear(
slf: *mut $crate::_detail::ffi::PyObject,
) -> $crate::_detail::libc::c_int {
$crate::py_class::gc::tp_clear::<$class_name, _>(
concat!(stringify!($class_name), ".__clear__"),
slf,
$class_name::__clear__,
)
}
Some(tp_clear)
}};
}
#[doc(hidden)]
pub unsafe fn tp_clear<C, F>(location: &str, slf: *mut ffi::PyObject, callback: F) -> libc::c_int
where
C: PythonObject,
F: FnOnce(&C, Python),
{
let guard = AbortOnDrop(location);
let py = Python::assume_gil_acquired();
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<C>();
callback(&slf, py);
slf.release_ref(py);
mem::forget(guard);
0
}