1use crate::ffi_ptr_ext::FfiPtrExt;
2use crate::py_result_ext::PyResultExt;
3use crate::types::capsule::PyCapsuleMethods;
4use crate::types::module::PyModuleMethods;
5use crate::{
6 ffi,
7 impl_::pymethods::{self, PyMethodDef},
8 types::{PyCapsule, PyDict, PyModule, PyString, PyTuple},
9};
10use crate::{Bound, Py, PyAny, PyResult, Python};
11use std::cell::UnsafeCell;
12use std::ffi::CStr;
13
14#[repr(transparent)]
19pub struct PyCFunction(PyAny);
20
21pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), #checkfunction=ffi::PyCFunction_Check);
22
23impl PyCFunction {
24 pub fn new_with_keywords<'py>(
29 py: Python<'py>,
30 fun: ffi::PyCFunctionWithKeywords,
31 name: &'static CStr,
32 doc: &'static CStr,
33 module: Option<&Bound<'py, PyModule>>,
34 ) -> PyResult<Bound<'py, Self>> {
35 Self::internal_new(
36 py,
37 &PyMethodDef::cfunction_with_keywords(name, fun, doc),
38 module,
39 )
40 }
41
42 #[deprecated(since = "0.23.0", note = "renamed to `PyCFunction::new_with_keywords`")]
44 #[inline]
45 pub fn new_with_keywords_bound<'py>(
46 py: Python<'py>,
47 fun: ffi::PyCFunctionWithKeywords,
48 name: &'static CStr,
49 doc: &'static CStr,
50 module: Option<&Bound<'py, PyModule>>,
51 ) -> PyResult<Bound<'py, Self>> {
52 Self::new_with_keywords(py, fun, name, doc, module)
53 }
54
55 pub fn new<'py>(
60 py: Python<'py>,
61 fun: ffi::PyCFunction,
62 name: &'static CStr,
63 doc: &'static CStr,
64 module: Option<&Bound<'py, PyModule>>,
65 ) -> PyResult<Bound<'py, Self>> {
66 Self::internal_new(py, &PyMethodDef::noargs(name, fun, doc), module)
67 }
68
69 #[deprecated(since = "0.23.0", note = "renamed to `PyCFunction::new`")]
71 #[inline]
72 pub fn new_bound<'py>(
73 py: Python<'py>,
74 fun: ffi::PyCFunction,
75 name: &'static CStr,
76 doc: &'static CStr,
77 module: Option<&Bound<'py, PyModule>>,
78 ) -> PyResult<Bound<'py, Self>> {
79 Self::new(py, fun, name, doc, module)
80 }
81
82 pub fn new_closure<'py, F, R>(
100 py: Python<'py>,
101 name: Option<&'static CStr>,
102 doc: Option<&'static CStr>,
103 closure: F,
104 ) -> PyResult<Bound<'py, Self>>
105 where
106 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
107 for<'p> R: crate::impl_::callback::IntoPyCallbackOutput<'p, *mut ffi::PyObject>,
108 {
109 let name = name.unwrap_or(ffi::c_str!("pyo3-closure"));
110 let doc = doc.unwrap_or(ffi::c_str!(""));
111 let method_def =
112 pymethods::PyMethodDef::cfunction_with_keywords(name, run_closure::<F, R>, doc);
113 let def = method_def.as_method_def();
114
115 let capsule = PyCapsule::new(
116 py,
117 ClosureDestructor::<F> {
118 closure,
119 def: UnsafeCell::new(def),
120 },
121 Some(CLOSURE_CAPSULE_NAME.to_owned()),
122 )?;
123
124 let data = unsafe { capsule.reference::<ClosureDestructor<F>>() };
126
127 unsafe {
128 ffi::PyCFunction_NewEx(data.def.get(), capsule.as_ptr(), std::ptr::null_mut())
129 .assume_owned_or_err(py)
130 .downcast_into_unchecked()
131 }
132 }
133
134 #[deprecated(since = "0.23.0", note = "renamed to `PyCFunction::new_closure`")]
136 #[inline]
137 pub fn new_closure_bound<'py, F, R>(
138 py: Python<'py>,
139 name: Option<&'static CStr>,
140 doc: Option<&'static CStr>,
141 closure: F,
142 ) -> PyResult<Bound<'py, Self>>
143 where
144 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
145 for<'p> R: crate::impl_::callback::IntoPyCallbackOutput<'p, *mut ffi::PyObject>,
146 {
147 Self::new_closure(py, name, doc, closure)
148 }
149
150 #[doc(hidden)]
151 pub fn internal_new<'py>(
152 py: Python<'py>,
153 method_def: &PyMethodDef,
154 module: Option<&Bound<'py, PyModule>>,
155 ) -> PyResult<Bound<'py, Self>> {
156 let (mod_ptr, module_name): (_, Option<Py<PyString>>) = if let Some(m) = module {
157 let mod_ptr = m.as_ptr();
158 (mod_ptr, Some(m.name()?.unbind()))
159 } else {
160 (std::ptr::null_mut(), None)
161 };
162 let def = method_def.as_method_def();
163
164 let def = Box::into_raw(Box::new(def));
166
167 let module_name_ptr = module_name
168 .as_ref()
169 .map_or(std::ptr::null_mut(), Py::as_ptr);
170
171 unsafe {
172 ffi::PyCFunction_NewEx(def, mod_ptr, module_name_ptr)
173 .assume_owned_or_err(py)
174 .downcast_into_unchecked()
175 }
176 }
177}
178
179static CLOSURE_CAPSULE_NAME: &CStr = ffi::c_str!("pyo3-closure");
180
181unsafe extern "C" fn run_closure<F, R>(
182 capsule_ptr: *mut ffi::PyObject,
183 args: *mut ffi::PyObject,
184 kwargs: *mut ffi::PyObject,
185) -> *mut ffi::PyObject
186where
187 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
188 for<'py> R: crate::impl_::callback::IntoPyCallbackOutput<'py, *mut ffi::PyObject>,
189{
190 use crate::types::any::PyAnyMethods;
191
192 crate::impl_::trampoline::cfunction_with_keywords(
193 capsule_ptr,
194 args,
195 kwargs,
196 |py, capsule_ptr, args, kwargs| {
197 let boxed_fn: &ClosureDestructor<F> =
198 &*(ffi::PyCapsule_GetPointer(capsule_ptr, CLOSURE_CAPSULE_NAME.as_ptr())
199 as *mut ClosureDestructor<F>);
200 let args = Bound::ref_from_ptr(py, &args).downcast_unchecked::<PyTuple>();
201 let kwargs = Bound::ref_from_ptr_or_opt(py, &kwargs)
202 .as_ref()
203 .map(|b| b.downcast_unchecked::<PyDict>());
204 let result = (boxed_fn.closure)(args, kwargs);
205 crate::impl_::callback::convert(py, result)
206 },
207 )
208}
209
210struct ClosureDestructor<F> {
211 closure: F,
212 def: UnsafeCell<ffi::PyMethodDef>,
215}
216
217unsafe impl<F: Send> Send for ClosureDestructor<F> {}
219
220#[repr(transparent)]
225#[cfg(not(Py_LIMITED_API))]
226pub struct PyFunction(PyAny);
227
228#[cfg(not(Py_LIMITED_API))]
229pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), #checkfunction=ffi::PyFunction_Check);