1#[cfg(feature = "gil-refs")]
2use crate::derive_utils::PyFunctionArguments;
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::py_result_ext::PyResultExt;
5use crate::types::capsule::PyCapsuleMethods;
6use crate::types::module::PyModuleMethods;
7#[cfg(feature = "gil-refs")]
8use crate::PyNativeType;
9use crate::{
10 ffi,
11 impl_::pymethods::{self, PyMethodDef},
12 types::{PyCapsule, PyDict, PyModule, PyString, PyTuple},
13};
14use crate::{Bound, IntoPy, Py, PyAny, PyResult, Python};
15use std::cell::UnsafeCell;
16use std::ffi::CStr;
17
18#[repr(transparent)]
23pub struct PyCFunction(PyAny);
24
25pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), #checkfunction=ffi::PyCFunction_Check);
26
27impl PyCFunction {
28 #[cfg(feature = "gil-refs")]
30 #[deprecated(
31 since = "0.21.0",
32 note = "`PyCFunction::new_with_keywords` will be replaced by `PyCFunction::new_with_keywords_bound` in a future PyO3 version"
33 )]
34 pub fn new_with_keywords<'a>(
35 fun: ffi::PyCFunctionWithKeywords,
36 name: &'static CStr,
37 doc: &'static CStr,
38 py_or_module: PyFunctionArguments<'a>,
39 ) -> PyResult<&'a Self> {
40 let (py, module) = py_or_module.into_py_and_maybe_module();
41 Self::internal_new(
42 py,
43 &PyMethodDef::cfunction_with_keywords(name, fun, doc),
44 module.map(PyNativeType::as_borrowed).as_deref(),
45 )
46 .map(Bound::into_gil_ref)
47 }
48
49 pub fn new_with_keywords_bound<'py>(
54 py: Python<'py>,
55 fun: ffi::PyCFunctionWithKeywords,
56 name: &'static CStr,
57 doc: &'static CStr,
58 module: Option<&Bound<'py, PyModule>>,
59 ) -> PyResult<Bound<'py, Self>> {
60 Self::internal_new(
61 py,
62 &PyMethodDef::cfunction_with_keywords(name, fun, doc),
63 module,
64 )
65 }
66
67 #[cfg(feature = "gil-refs")]
69 #[deprecated(
70 since = "0.21.0",
71 note = "`PyCFunction::new` will be replaced by `PyCFunction::new_bound` in a future PyO3 version"
72 )]
73 pub fn new<'a>(
74 fun: ffi::PyCFunction,
75 name: &'static CStr,
76 doc: &'static CStr,
77 py_or_module: PyFunctionArguments<'a>,
78 ) -> PyResult<&'a Self> {
79 let (py, module) = py_or_module.into_py_and_maybe_module();
80 Self::internal_new(
81 py,
82 &PyMethodDef::noargs(name, fun, doc),
83 module.map(PyNativeType::as_borrowed).as_deref(),
84 )
85 .map(Bound::into_gil_ref)
86 }
87
88 pub fn new_bound<'py>(
93 py: Python<'py>,
94 fun: ffi::PyCFunction,
95 name: &'static CStr,
96 doc: &'static CStr,
97 module: Option<&Bound<'py, PyModule>>,
98 ) -> PyResult<Bound<'py, Self>> {
99 Self::internal_new(py, &PyMethodDef::noargs(name, fun, doc), module)
100 }
101
102 #[cfg(feature = "gil-refs")]
104 #[deprecated(
105 since = "0.21.0",
106 note = "`PyCFunction::new_closure` will be replaced by `PyCFunction::new_closure_bound` in a future PyO3 version"
107 )]
108 pub fn new_closure<'a, F, R>(
109 py: Python<'a>,
110 name: Option<&'static CStr>,
111 doc: Option<&'static CStr>,
112 closure: F,
113 ) -> PyResult<&'a PyCFunction>
114 where
115 F: Fn(&PyTuple, Option<&PyDict>) -> R + Send + 'static,
116 R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
117 {
118 Self::new_closure_bound(py, name, doc, move |args, kwargs| {
119 closure(args.as_gil_ref(), kwargs.map(Bound::as_gil_ref))
120 })
121 .map(Bound::into_gil_ref)
122 }
123
124 pub fn new_closure_bound<'py, F, R>(
142 py: Python<'py>,
143 name: Option<&'static CStr>,
144 doc: Option<&'static CStr>,
145 closure: F,
146 ) -> PyResult<Bound<'py, Self>>
147 where
148 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
149 R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
150 {
151 let name = name.unwrap_or(ffi::c_str!("pyo3-closure"));
152 let doc = doc.unwrap_or(ffi::c_str!(""));
153 let method_def =
154 pymethods::PyMethodDef::cfunction_with_keywords(name, run_closure::<F, R>, doc);
155 let def = method_def.as_method_def();
156
157 let capsule = PyCapsule::new_bound(
158 py,
159 ClosureDestructor::<F> {
160 closure,
161 def: UnsafeCell::new(def),
162 },
163 Some(CLOSURE_CAPSULE_NAME.to_owned()),
164 )?;
165
166 let data = unsafe { capsule.reference::<ClosureDestructor<F>>() };
168
169 unsafe {
170 ffi::PyCFunction_NewEx(data.def.get(), capsule.as_ptr(), std::ptr::null_mut())
171 .assume_owned_or_err(py)
172 .downcast_into_unchecked()
173 }
174 }
175
176 #[doc(hidden)]
177 pub fn internal_new<'py>(
178 py: Python<'py>,
179 method_def: &PyMethodDef,
180 module: Option<&Bound<'py, PyModule>>,
181 ) -> PyResult<Bound<'py, Self>> {
182 let (mod_ptr, module_name): (_, Option<Py<PyString>>) = if let Some(m) = module {
183 let mod_ptr = m.as_ptr();
184 (mod_ptr, Some(m.name()?.into_py(py)))
185 } else {
186 (std::ptr::null_mut(), None)
187 };
188 let def = method_def.as_method_def();
189
190 let def = Box::into_raw(Box::new(def));
192
193 let module_name_ptr = module_name
194 .as_ref()
195 .map_or(std::ptr::null_mut(), Py::as_ptr);
196
197 unsafe {
198 ffi::PyCFunction_NewEx(def, mod_ptr, module_name_ptr)
199 .assume_owned_or_err(py)
200 .downcast_into_unchecked()
201 }
202 }
203}
204
205static CLOSURE_CAPSULE_NAME: &CStr = ffi::c_str!("pyo3-closure");
206
207unsafe extern "C" fn run_closure<F, R>(
208 capsule_ptr: *mut ffi::PyObject,
209 args: *mut ffi::PyObject,
210 kwargs: *mut ffi::PyObject,
211) -> *mut ffi::PyObject
212where
213 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
214 R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
215{
216 use crate::types::any::PyAnyMethods;
217
218 crate::impl_::trampoline::cfunction_with_keywords(
219 capsule_ptr,
220 args,
221 kwargs,
222 |py, capsule_ptr, args, kwargs| {
223 let boxed_fn: &ClosureDestructor<F> =
224 &*(ffi::PyCapsule_GetPointer(capsule_ptr, CLOSURE_CAPSULE_NAME.as_ptr())
225 as *mut ClosureDestructor<F>);
226 let args = Bound::ref_from_ptr(py, &args).downcast_unchecked::<PyTuple>();
227 let kwargs = Bound::ref_from_ptr_or_opt(py, &kwargs)
228 .as_ref()
229 .map(|b| b.downcast_unchecked::<PyDict>());
230 let result = (boxed_fn.closure)(args, kwargs);
231 crate::callback::convert(py, result)
232 },
233 )
234}
235
236struct ClosureDestructor<F> {
237 closure: F,
238 def: UnsafeCell<ffi::PyMethodDef>,
241}
242
243unsafe impl<F: Send> Send for ClosureDestructor<F> {}
245
246#[repr(transparent)]
251#[cfg(all(not(Py_LIMITED_API), not(all(PyPy, not(Py_3_8)))))]
252pub struct PyFunction(PyAny);
253
254#[cfg(all(not(Py_LIMITED_API), not(all(PyPy, not(Py_3_8)))))]
255pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), #checkfunction=ffi::PyFunction_Check);