1 | use crate::ffi_ptr_ext::FfiPtrExt; |
2 | use crate::py_result_ext::PyResultExt; |
3 | use crate::types::capsule::PyCapsuleMethods; |
4 | use crate::types::module::PyModuleMethods; |
5 | use crate::{ |
6 | ffi, |
7 | impl_::pymethods::{self, PyMethodDef}, |
8 | types::{PyCapsule, PyDict, PyModule, PyString, PyTuple}, |
9 | }; |
10 | use crate::{Bound, Py, PyAny, PyResult, Python}; |
11 | use std::cell::UnsafeCell; |
12 | use std::ffi::CStr; |
13 | |
14 | /// Represents a builtin Python function object. |
15 | /// |
16 | /// Values of this type are accessed via PyO3's smart pointers, e.g. as |
17 | /// [`Py<PyCFunction>`][crate::Py] or [`Bound<'py, PyCFunction>`][Bound]. |
18 | #[repr (transparent)] |
19 | pub struct PyCFunction(PyAny); |
20 | |
21 | pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), #checkfunction=ffi::PyCFunction_Check); |
22 | |
23 | impl PyCFunction { |
24 | /// Create a new built-in function with keywords (*args and/or **kwargs). |
25 | /// |
26 | /// To create `name` and `doc` static strings on Rust versions older than 1.77 (which added c"" literals), |
27 | /// use the [`c_str!`](crate::ffi::c_str) macro. |
28 | 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 name for [`PyCFunction::new_with_keywords`]. |
43 | #[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 | /// Create a new built-in function which takes no arguments. |
56 | /// |
57 | /// To create `name` and `doc` static strings on Rust versions older than 1.77 (which added c"" literals), |
58 | /// use the [`c_str!`](crate::ffi::c_str) macro. |
59 | 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 name for [`PyCFunction::new`]. |
70 | #[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 | /// Create a new function from a closure. |
83 | /// |
84 | /// # Examples |
85 | /// |
86 | /// ``` |
87 | /// # use pyo3::prelude::*; |
88 | /// # use pyo3::{py_run, types::{PyCFunction, PyDict, PyTuple}}; |
89 | /// |
90 | /// Python::with_gil(|py| { |
91 | /// let add_one = |args: &Bound<'_, PyTuple>, _kwargs: Option<&Bound<'_, PyDict>>| -> PyResult<_> { |
92 | /// let i = args.extract::<(i64,)>()?.0; |
93 | /// Ok(i+1) |
94 | /// }; |
95 | /// let add_one = PyCFunction::new_closure(py, None, None, add_one).unwrap(); |
96 | /// py_run!(py, add_one, "assert add_one(42) == 43" ); |
97 | /// }); |
98 | /// ``` |
99 | 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 | // Safety: just created the capsule with type ClosureDestructor<F> above |
125 | 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 name for [`PyCFunction::new_closure`]. |
135 | #[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 | // FIXME: stop leaking the def |
165 | 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 | |
179 | static CLOSURE_CAPSULE_NAME: &CStr = ffi::c_str!("pyo3-closure" ); |
180 | |
181 | unsafe 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 |
186 | where |
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 | unsafe { |
193 | crate::impl_::trampoline::cfunction_with_keywords( |
194 | slf:capsule_ptr, |
195 | args, |
196 | kwargs, |
197 | |py: Python<'_>, capsule_ptr: *mut PyObject, args: *mut PyObject, kwargs: *mut PyObject| { |
198 | let boxed_fn: &ClosureDestructor<F> = |
199 | &*(ffi::PyCapsule_GetPointer(capsule_ptr, name:CLOSURE_CAPSULE_NAME.as_ptr()) |
200 | as *mut ClosureDestructor<F>); |
201 | let args: &Bound<'_, PyTuple> = Bound::ref_from_ptr(py, &args).downcast_unchecked::<PyTuple>(); |
202 | let kwargs: Option<&Bound<'_, PyDict>> = BoundOption<&Bound<'_, PyAny>>::ref_from_ptr_or_opt(py, &kwargs) |
203 | .as_ref() |
204 | .map(|b: &Bound<'_, PyAny>| b.downcast_unchecked::<PyDict>()); |
205 | let result: R = (boxed_fn.closure)(args, kwargs); |
206 | crate::impl_::callback::convert(py, value:result) |
207 | }, |
208 | ) |
209 | } |
210 | } |
211 | |
212 | struct ClosureDestructor<F> { |
213 | closure: F, |
214 | // Wrapped in UnsafeCell because Python C-API wants a *mut pointer |
215 | // to this member. |
216 | def: UnsafeCell<ffi::PyMethodDef>, |
217 | } |
218 | |
219 | // Safety: F is send and none of the fields are ever mutated |
220 | unsafe impl<F: Send> Send for ClosureDestructor<F> {} |
221 | |
222 | /// Represents a Python function object. |
223 | /// |
224 | /// Values of this type are accessed via PyO3's smart pointers, e.g. as |
225 | /// [`Py<PyFunction>`][crate::Py] or [`Bound<'py, PyFunction>`][Bound]. |
226 | #[repr (transparent)] |
227 | #[cfg (not(Py_LIMITED_API))] |
228 | pub struct PyFunction(PyAny); |
229 | |
230 | #[cfg (not(Py_LIMITED_API))] |
231 | pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), #checkfunction=ffi::PyFunction_Check); |
232 | |