Skip to main content

pyo3/types/
function.rs

1use crate::ffi_ptr_ext::FfiPtrExt;
2use crate::impl_::pyfunction::create_py_c_function;
3use crate::py_result_ext::PyResultExt;
4use crate::types::capsule::PyCapsuleMethods;
5use crate::{
6    ffi,
7    impl_::pymethods::{self, PyMethodDef},
8    types::{PyCapsule, PyDict, PyModule, PyTuple},
9};
10use crate::{Bound, PyAny, PyResult, Python};
11use std::cell::UnsafeCell;
12use std::ffi::CStr;
13use std::ptr::NonNull;
14
15/// Represents a builtin Python function object.
16///
17/// Values of this type are accessed via PyO3's smart pointers, e.g. as
18/// [`Py<PyCFunction>`][crate::Py] or [`Bound<'py, PyCFunction>`][Bound].
19#[repr(transparent)]
20pub struct PyCFunction(PyAny);
21
22pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), "builtins", "builtin_function_or_method", #checkfunction=ffi::PyCFunction_Check);
23
24impl PyCFunction {
25    /// Create a new built-in function with keywords (*args and/or **kwargs).
26    ///
27    /// To create `name` and `doc` static strings on Rust versions older than 1.77 (which added c"" literals),
28    /// use the [`c_str!`](crate::ffi::c_str) macro.
29    pub fn new_with_keywords<'py>(
30        py: Python<'py>,
31        fun: ffi::PyCFunctionWithKeywords,
32        name: &'static CStr,
33        doc: &'static CStr,
34        module: Option<&Bound<'py, PyModule>>,
35    ) -> PyResult<Bound<'py, Self>> {
36        let def = PyMethodDef::cfunction_with_keywords(name, fun, doc).into_raw();
37        // FIXME: stop leaking the def
38        let def = Box::leak(Box::new(def));
39        // Safety: def is static
40        unsafe { create_py_c_function(py, def, module) }
41    }
42
43    /// Create a new built-in function which takes no arguments.
44    ///
45    /// To create `name` and `doc` static strings on Rust versions older than 1.77 (which added c"" literals),
46    /// use the [`c_str!`](crate::ffi::c_str) macro.
47    pub fn new<'py>(
48        py: Python<'py>,
49        fun: ffi::PyCFunction,
50        name: &'static CStr,
51        doc: &'static CStr,
52        module: Option<&Bound<'py, PyModule>>,
53    ) -> PyResult<Bound<'py, Self>> {
54        let def = PyMethodDef::noargs(name, fun, doc).into_raw();
55        // FIXME: stop leaking the def
56        let def = Box::leak(Box::new(def));
57        // Safety: def is static
58        unsafe { create_py_c_function(py, def, module) }
59    }
60
61    /// Create a new function from a closure.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// # use pyo3::prelude::*;
67    /// # use pyo3::{py_run, types::{PyCFunction, PyDict, PyTuple}};
68    ///
69    /// Python::attach(|py| {
70    ///     let add_one = |args: &Bound<'_, PyTuple>, _kwargs: Option<&Bound<'_, PyDict>>| -> PyResult<_> {
71    ///         let i = args.extract::<(i64,)>()?.0;
72    ///         Ok(i+1)
73    ///     };
74    ///     let add_one = PyCFunction::new_closure(py, None, None, add_one).unwrap();
75    ///     py_run!(py, add_one, "assert add_one(42) == 43");
76    /// });
77    /// ```
78    pub fn new_closure<'py, F, R>(
79        py: Python<'py>,
80        name: Option<&'static CStr>,
81        doc: Option<&'static CStr>,
82        closure: F,
83    ) -> PyResult<Bound<'py, Self>>
84    where
85        F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
86        for<'p> R: crate::impl_::callback::IntoPyCallbackOutput<'p, *mut ffi::PyObject>,
87    {
88        let name = name.unwrap_or(c"pyo3-closure");
89        let doc = doc.unwrap_or(c"");
90        let method_def =
91            pymethods::PyMethodDef::cfunction_with_keywords(name, run_closure::<F, R>, doc);
92        let def = method_def.into_raw();
93
94        let capsule = PyCapsule::new_with_value(
95            py,
96            ClosureDestructor::<F> {
97                closure,
98                def: UnsafeCell::new(def),
99            },
100            CLOSURE_CAPSULE_NAME,
101        )?;
102
103        let data: NonNull<ClosureDestructor<F>> =
104            capsule.pointer_checked(Some(CLOSURE_CAPSULE_NAME))?.cast();
105
106        // SAFETY: The capsule has just been created with the value, and will exist as long as
107        // the function object exists.
108        let method_def = unsafe { data.as_ref().def.get() };
109
110        // SAFETY: The arguments to `PyCFunction_NewEx` are valid, we are attached to the
111        // interpreter and we know the function either returns a new reference or errors.
112        unsafe {
113            ffi::PyCFunction_NewEx(method_def, capsule.as_ptr(), std::ptr::null_mut())
114                .assume_owned_or_err(py)
115                .cast_into_unchecked()
116        }
117    }
118}
119
120static CLOSURE_CAPSULE_NAME: &CStr = c"pyo3-closure";
121
122unsafe extern "C" fn run_closure<F, R>(
123    capsule_ptr: *mut ffi::PyObject,
124    args: *mut ffi::PyObject,
125    kwargs: *mut ffi::PyObject,
126) -> *mut ffi::PyObject
127where
128    F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
129    for<'py> R: crate::impl_::callback::IntoPyCallbackOutput<'py, *mut ffi::PyObject>,
130{
131    unsafe {
132        crate::impl_::trampoline::cfunction_with_keywords::inner(
133            capsule_ptr,
134            args,
135            kwargs,
136            |py, capsule_ptr, args, kwargs| {
137                let boxed_fn: &ClosureDestructor<F> =
138                    &*(ffi::PyCapsule_GetPointer(capsule_ptr, CLOSURE_CAPSULE_NAME.as_ptr())
139                        as *mut ClosureDestructor<F>);
140                let args = Bound::ref_from_ptr(py, &args).cast_unchecked::<PyTuple>();
141                let kwargs = Bound::ref_from_ptr_or_opt(py, &kwargs)
142                    .as_ref()
143                    .map(|b| b.cast_unchecked::<PyDict>());
144                let result = (boxed_fn.closure)(args, kwargs);
145                crate::impl_::callback::convert(py, result)
146            },
147        )
148    }
149}
150
151struct ClosureDestructor<F> {
152    closure: F,
153    // Wrapped in UnsafeCell because Python C-API wants a *mut pointer
154    // to this member.
155    def: UnsafeCell<ffi::PyMethodDef>,
156}
157
158// Safety: F is send and none of the fields are ever mutated
159unsafe impl<F: Send> Send for ClosureDestructor<F> {}
160
161/// Represents a Python function object.
162///
163/// Values of this type are accessed via PyO3's smart pointers, e.g. as
164/// [`Py<PyFunction>`][crate::Py] or [`Bound<'py, PyFunction>`][Bound].
165#[repr(transparent)]
166#[cfg(not(Py_LIMITED_API))]
167pub struct PyFunction(PyAny);
168
169#[cfg(not(Py_LIMITED_API))]
170pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), "builtins", "function", #checkfunction=ffi::PyFunction_Check);
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here