pyo3/types/
function.rs

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/// 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)]
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    /// 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
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    // Wrapped in UnsafeCell because Python C-API wants a *mut pointer
213    // to this member.
214    def: UnsafeCell<ffi::PyMethodDef>,
215}
216
217// Safety: F is send and none of the fields are ever mutated
218unsafe impl<F: Send> Send for ClosureDestructor<F> {}
219
220/// Represents a Python function object.
221///
222/// Values of this type are accessed via PyO3's smart pointers, e.g. as
223/// [`Py<PyFunction>`][crate::Py] or [`Bound<'py, PyFunction>`][Bound].
224#[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);