1use crate::ffi_ptr_ext::FfiPtrExt;
2use crate::impl_::pyfunction::create_py_c_function;
3use crate::platform::prelude::*;
4use crate::py_result_ext::PyResultExt;
5use crate::types::capsule::PyCapsuleMethods;
6use crate::{
7 ffi,
8 impl_::pymethods::{self, PyMethodDef},
9 types::{PyCapsule, PyDict, PyModule, PyTuple},
10};
11#[cfg(Py_LIMITED_API)]
12use crate::{
13 sync::PyOnceLock,
14 types::{PyType, PyTypeMethods},
15 Py,
16};
17use crate::{Bound, PyAny, PyResult, Python};
18use core::cell::UnsafeCell;
19use core::ffi::CStr;
20use core::ptr::NonNull;
21
22#[repr(transparent)]
27pub struct PyCFunction(PyAny);
28
29#[cfg(not(RustPython))]
30pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), "builtins", "builtin_function_or_method", #checkfunction=ffi::PyCFunction_Check);
31
32#[cfg(RustPython)]
33pyobject_native_type_core!(
34 PyCFunction,
35 |py| {
36 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
37 TYPE.import(py, "builtins", "builtin_function_or_method")
38 .unwrap()
39 .as_type_ptr()
40 },
41 "builtins",
42 "builtin_function_or_method",
43 #checkfunction=ffi::PyCFunction_Check
44);
45
46impl PyCFunction {
47 pub fn new_with_keywords<'py>(
52 py: Python<'py>,
53 fun: ffi::PyCFunctionWithKeywords,
54 name: &'static CStr,
55 doc: &'static CStr,
56 module: Option<&Bound<'py, PyModule>>,
57 ) -> PyResult<Bound<'py, Self>> {
58 let def = PyMethodDef::cfunction_with_keywords(name, fun, doc).into_raw();
59 let def = Box::leak(Box::new(def));
61 unsafe { create_py_c_function(py, def, module) }
63 }
64
65 pub fn new<'py>(
70 py: Python<'py>,
71 fun: ffi::PyCFunction,
72 name: &'static CStr,
73 doc: &'static CStr,
74 module: Option<&Bound<'py, PyModule>>,
75 ) -> PyResult<Bound<'py, Self>> {
76 let def = PyMethodDef::noargs(name, fun, doc).into_raw();
77 let def = Box::leak(Box::new(def));
79 unsafe { create_py_c_function(py, def, module) }
81 }
82
83 pub fn new_closure<'py, F, R>(
101 py: Python<'py>,
102 name: Option<&'static CStr>,
103 doc: Option<&'static CStr>,
104 closure: F,
105 ) -> PyResult<Bound<'py, Self>>
106 where
107 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + Sync + 'static,
108 for<'p> R: crate::impl_::callback::IntoPyCallbackOutput<'p, *mut ffi::PyObject>,
109 {
110 let name = name.unwrap_or(c"pyo3-closure");
111 let doc = doc.unwrap_or(c"");
112 let method_def =
113 pymethods::PyMethodDef::cfunction_with_keywords(name, run_closure::<F, R>, doc);
114 let def = method_def.into_raw();
115
116 let capsule = PyCapsule::new_with_value(
117 py,
118 ClosureDestructor::<F> {
119 closure,
120 def: UnsafeCell::new(def),
121 },
122 CLOSURE_CAPSULE_NAME,
123 )?;
124
125 let data: NonNull<ClosureDestructor<F>> =
126 capsule.pointer_checked(Some(CLOSURE_CAPSULE_NAME))?.cast();
127
128 let method_def = unsafe { data.as_ref().def.get() };
131
132 unsafe {
135 ffi::PyCFunction_NewEx(method_def, capsule.as_ptr(), core::ptr::null_mut())
136 .assume_owned_or_err(py)
137 .cast_into_unchecked()
138 }
139 }
140}
141
142static CLOSURE_CAPSULE_NAME: &CStr = c"pyo3-closure";
143
144unsafe extern "C" fn run_closure<F, R>(
145 capsule_ptr: *mut ffi::PyObject,
146 args: *mut ffi::PyObject,
147 kwargs: *mut ffi::PyObject,
148) -> *mut ffi::PyObject
149where
150 F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
151 for<'py> R: crate::impl_::callback::IntoPyCallbackOutput<'py, *mut ffi::PyObject>,
152{
153 unsafe {
154 crate::impl_::trampoline::cfunction_with_keywords::inner(
155 capsule_ptr,
156 args,
157 kwargs,
158 |py, capsule_ptr, args, kwargs| {
159 let boxed_fn: &ClosureDestructor<F> =
160 &*(ffi::PyCapsule_GetPointer(capsule_ptr, CLOSURE_CAPSULE_NAME.as_ptr())
161 as *mut ClosureDestructor<F>);
162 let args = Bound::ref_from_ptr(py, &args).cast_unchecked::<PyTuple>();
163 let kwargs = Bound::ref_from_ptr_or_opt(py, &kwargs)
164 .as_ref()
165 .map(|b| b.cast_unchecked::<PyDict>());
166 let result = (boxed_fn.closure)(args, kwargs);
167 crate::impl_::callback::convert(py, result)
168 },
169 )
170 }
171}
172
173struct ClosureDestructor<F> {
174 closure: F,
175 def: UnsafeCell<ffi::PyMethodDef>,
178}
179
180unsafe impl<F: Send> Send for ClosureDestructor<F> {}
182
183#[repr(transparent)]
188pub struct PyFunction(PyAny);
189
190#[cfg(not(Py_LIMITED_API))]
191pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), "builtins", "function", #checkfunction=ffi::PyFunction_Check);
192
193#[cfg(Py_LIMITED_API)]
194pyobject_native_type_core!(
195 PyFunction,
196 |py| {
197 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
198 TYPE.import(py, "types", "FunctionType")
199 .unwrap()
200 .as_type_ptr()
201 },
202 "builtins",
203 "function"
204);