use crate::derive_utils::PyFunctionArguments;
use crate::methods::PyMethodDefDestructor;
use crate::prelude::*;
use crate::{
ffi,
impl_::pymethods::{self, PyMethodDef},
types::{PyCapsule, PyDict, PyTuple},
AsPyPointer,
};
use std::cell::UnsafeCell;
use std::ffi::CStr;
#[repr(transparent)]
pub struct PyCFunction(PyAny);
pyobject_native_type_core!(PyCFunction, ffi::PyCFunction_Type, #checkfunction=ffi::PyCFunction_Check);
impl PyCFunction {
pub fn new_with_keywords<'a>(
fun: ffi::PyCFunctionWithKeywords,
name: &'static str,
doc: &'static str,
py_or_module: PyFunctionArguments<'a>,
) -> PyResult<&'a Self> {
Self::internal_new(
&PyMethodDef::cfunction_with_keywords(
name,
pymethods::PyCFunctionWithKeywords(fun),
doc,
),
py_or_module,
)
}
pub fn new<'a>(
fun: ffi::PyCFunction,
name: &'static str,
doc: &'static str,
py_or_module: PyFunctionArguments<'a>,
) -> PyResult<&'a Self> {
Self::internal_new(
&PyMethodDef::noargs(name, pymethods::PyCFunction(fun), doc),
py_or_module,
)
}
pub fn new_closure<'a, F, R>(
py: Python<'a>,
name: Option<&'static str>,
doc: Option<&'static str>,
closure: F,
) -> PyResult<&'a PyCFunction>
where
F: Fn(&PyTuple, Option<&PyDict>) -> R + Send + 'static,
R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
{
let method_def = pymethods::PyMethodDef::cfunction_with_keywords(
name.unwrap_or("pyo3-closure\0"),
pymethods::PyCFunctionWithKeywords(run_closure::<F, R>),
doc.unwrap_or("\0"),
);
let (def, def_destructor) = method_def.as_method_def()?;
let capsule = PyCapsule::new(
py,
ClosureDestructor::<F> {
closure,
def: UnsafeCell::new(def),
def_destructor,
},
Some(closure_capsule_name().to_owned()),
)?;
let data = unsafe { capsule.reference::<ClosureDestructor<F>>() };
unsafe {
py.from_owned_ptr_or_err::<PyCFunction>(ffi::PyCFunction_NewEx(
data.def.get(),
capsule.as_ptr(),
std::ptr::null_mut(),
))
}
}
#[doc(hidden)]
pub fn internal_new<'py>(
method_def: &PyMethodDef,
py_or_module: PyFunctionArguments<'py>,
) -> PyResult<&'py Self> {
let (py, module) = py_or_module.into_py_and_maybe_module();
let (mod_ptr, module_name) = if let Some(m) = module {
let mod_ptr = m.as_ptr();
let name: Py<PyAny> = m.name()?.into_py(py);
(mod_ptr, name.as_ptr())
} else {
(std::ptr::null_mut(), std::ptr::null_mut())
};
let (def, destructor) = method_def.as_method_def()?;
let def = Box::into_raw(Box::new(def));
std::mem::forget(destructor);
unsafe {
py.from_owned_ptr_or_err::<PyCFunction>(ffi::PyCFunction_NewEx(
def,
mod_ptr,
module_name,
))
}
}
}
fn closure_capsule_name() -> &'static CStr {
CStr::from_bytes_with_nul(b"pyo3-closure\0").unwrap()
}
unsafe extern "C" fn run_closure<F, R>(
capsule_ptr: *mut ffi::PyObject,
args: *mut ffi::PyObject,
kwargs: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
F: Fn(&PyTuple, Option<&PyDict>) -> R + Send + 'static,
R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
{
crate::impl_::trampoline::cfunction_with_keywords(
capsule_ptr,
args,
kwargs,
|py, capsule_ptr, args, kwargs| {
let boxed_fn: &ClosureDestructor<F> =
&*(ffi::PyCapsule_GetPointer(capsule_ptr, closure_capsule_name().as_ptr())
as *mut ClosureDestructor<F>);
let args = py.from_borrowed_ptr::<PyTuple>(args);
let kwargs = py.from_borrowed_ptr_or_opt::<PyDict>(kwargs);
crate::callback::convert(py, (boxed_fn.closure)(args, kwargs))
},
)
}
struct ClosureDestructor<F> {
closure: F,
def: UnsafeCell<ffi::PyMethodDef>,
#[allow(dead_code)]
def_destructor: PyMethodDefDestructor,
}
unsafe impl<F: Send> Send for ClosureDestructor<F> {}
#[repr(transparent)]
#[cfg(all(not(Py_LIMITED_API), not(all(PyPy, not(Py_3_8)))))]
pub struct PyFunction(PyAny);
#[cfg(all(not(Py_LIMITED_API), not(all(PyPy, not(Py_3_8)))))]
pyobject_native_type_core!(PyFunction, ffi::PyFunction_Type, #checkfunction=ffi::PyFunction_Check);