1use crate::ffi_ptr_ext::FfiPtrExt;
2use crate::impl_::pyfunction::create_py_c_function;
3use crate::py_result_ext::PyResultExt;
4use crate::types::capsule::PyCapsuleMethods;
5use crate::{
6 ffi,
7 impl_::pymethods::{self, PyMethodDef},
8 types::{PyCapsule, PyDict, PyModule, PyTuple},
9};
10use crate::{Bound, PyAny, PyResult, Python};
11use std::cell::UnsafeCell;
12use std::ffi::CStr;
13use std::ptr::NonNull;
14
15#[repr(transparent)]
20pub struct PyCFunction(PyAny);
21
22pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), "builtins", "builtin_function_or_method", #checkfunction=ffi::PyCFunction_Check);
23
24impl PyCFunction {
25 pub fn new_with_keywords<'py>(
30 py: Python<'py>,
31 fun: ffi::PyCFunctionWithKeywords,
32 name: &'static CStr,
33 doc: &'static CStr,
34 module: Option<&Bound<'py, PyModule>>,
35 ) -> PyResult<Bound<'py, Self>> {
36 let def = PyMethodDef::cfunction_with_keywords(name, fun, doc).into_raw();
37 let def = Box::leak(Box::new(def));
39 unsafe { create_py_c_function(py, def, module) }
41 }
42
43 pub fn new<'py>(
48 py: Python<'py>,
49 fun: ffi::PyCFunction,
50 name: &'static CStr,
51 doc: &'static CStr,
52 module: Option<&Bound<'py, PyModule>>,
53 ) -> PyResult<Bound<'py, Self>> {
54 let def = PyMethodDef::noargs(name, fun, doc).into_raw();
55 let def = Box::leak(Box::new(def));
57 unsafe { create_py_c_function(py, def, module) }
59 }
60
61 pub fn new_closure<'py, F, R>(
79 py: Python<'py>,
80 name: Option<&'static CStr>,
81 doc: Option<&'static CStr>,
82 closure: F,
83 ) -> PyResult<Bound<'py, Self>>
84 where
85 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
86 for<'p> R: crate::impl_::callback::IntoPyCallbackOutput<'p, *mut ffi::PyObject>,
87 {
88 let name = name.unwrap_or(c"pyo3-closure");
89 let doc = doc.unwrap_or(c"");
90 let method_def =
91 pymethods::PyMethodDef::cfunction_with_keywords(name, run_closure::<F, R>, doc);
92 let def = method_def.into_raw();
93
94 let capsule = PyCapsule::new_with_value(
95 py,
96 ClosureDestructor::<F> {
97 closure,
98 def: UnsafeCell::new(def),
99 },
100 CLOSURE_CAPSULE_NAME,
101 )?;
102
103 let data: NonNull<ClosureDestructor<F>> =
104 capsule.pointer_checked(Some(CLOSURE_CAPSULE_NAME))?.cast();
105
106 let method_def = unsafe { data.as_ref().def.get() };
109
110 unsafe {
113 ffi::PyCFunction_NewEx(method_def, capsule.as_ptr(), std::ptr::null_mut())
114 .assume_owned_or_err(py)
115 .cast_into_unchecked()
116 }
117 }
118}
119
120static CLOSURE_CAPSULE_NAME: &CStr = c"pyo3-closure";
121
122unsafe extern "C" fn run_closure<F, R>(
123 capsule_ptr: *mut ffi::PyObject,
124 args: *mut ffi::PyObject,
125 kwargs: *mut ffi::PyObject,
126) -> *mut ffi::PyObject
127where
128 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
129 for<'py> R: crate::impl_::callback::IntoPyCallbackOutput<'py, *mut ffi::PyObject>,
130{
131 unsafe {
132 crate::impl_::trampoline::cfunction_with_keywords::inner(
133 capsule_ptr,
134 args,
135 kwargs,
136 |py, capsule_ptr, args, kwargs| {
137 let boxed_fn: &ClosureDestructor<F> =
138 &*(ffi::PyCapsule_GetPointer(capsule_ptr, CLOSURE_CAPSULE_NAME.as_ptr())
139 as *mut ClosureDestructor<F>);
140 let args = Bound::ref_from_ptr(py, &args).cast_unchecked::<PyTuple>();
141 let kwargs = Bound::ref_from_ptr_or_opt(py, &kwargs)
142 .as_ref()
143 .map(|b| b.cast_unchecked::<PyDict>());
144 let result = (boxed_fn.closure)(args, kwargs);
145 crate::impl_::callback::convert(py, result)
146 },
147 )
148 }
149}
150
151struct ClosureDestructor<F> {
152 closure: F,
153 def: UnsafeCell<ffi::PyMethodDef>,
156}
157
158unsafe impl<F: Send> Send for ClosureDestructor<F> {}
160
161#[repr(transparent)]
166#[cfg(not(Py_LIMITED_API))]
167pub struct PyFunction(PyAny);
168
169#[cfg(not(Py_LIMITED_API))]
170pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), "builtins", "function", #checkfunction=ffi::PyFunction_Check);