1use crate::derive_utils::PyFunctionArguments;
2use crate::methods::PyMethodDefDestructor;
3use crate::prelude::*;
4use crate::{
5 ffi,
6 impl_::pymethods::{self, PyMethodDef},
7 types::{PyCapsule, PyDict, PyTuple},
8};
9use std::cell::UnsafeCell;
10use std::ffi::CStr;
11
12/// Represents a builtin Python function object.
13#[repr(transparent)]
14pub struct PyCFunction(PyAny);
15
16pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), #checkfunction=ffi::PyCFunction_Check);
17
18impl PyCFunction {
19 /// Create a new built-in function with keywords (*args and/or **kwargs).
20 pub fn new_with_keywords<'a>(
21 fun: ffi::PyCFunctionWithKeywords,
22 name: &'static str,
23 doc: &'static str,
24 py_or_module: PyFunctionArguments<'a>,
25 ) -> PyResult<&'a Self> {
26 Self::internal_new(
27 &PyMethodDef::cfunction_with_keywords(
28 name,
29 pymethods::PyCFunctionWithKeywords(fun),
30 doc,
31 ),
32 py_or_module,
33 )
34 }
35
36 /// Create a new built-in function which takes no arguments.
37 pub fn new<'a>(
38 fun: ffi::PyCFunction,
39 name: &'static str,
40 doc: &'static str,
41 py_or_module: PyFunctionArguments<'a>,
42 ) -> PyResult<&'a Self> {
43 Self::internal_new(
44 &PyMethodDef::noargs(name, pymethods::PyCFunction(fun), doc),
45 py_or_module,
46 )
47 }
48
49 /// Create a new function from a closure.
50 ///
51 /// # Examples
52 ///
53 /// ```
54 /// # use pyo3::prelude::*;
55 /// # use pyo3::{py_run, types::{PyCFunction, PyDict, PyTuple}};
56 ///
57 /// Python::with_gil(|py| {
58 /// let add_one = |args: &PyTuple, _kwargs: Option<&PyDict>| -> PyResult<_> {
59 /// let i = args.extract::<(i64,)>()?.0;
60 /// Ok(i+1)
61 /// };
62 /// let add_one = PyCFunction::new_closure(py, None, None, add_one).unwrap();
63 /// py_run!(py, add_one, "assert add_one(42) == 43");
64 /// });
65 /// ```
66 pub fn new_closure<'a, F, R>(
67 py: Python<'a>,
68 name: Option<&'static str>,
69 doc: Option<&'static str>,
70 closure: F,
71 ) -> PyResult<&'a PyCFunction>
72 where
73 F: Fn(&PyTuple, Option<&PyDict>) -> R + Send + 'static,
74 R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
75 {
76 let method_def = pymethods::PyMethodDef::cfunction_with_keywords(
77 name.unwrap_or("pyo3-closure\0"),
78 pymethods::PyCFunctionWithKeywords(run_closure::<F, R>),
79 doc.unwrap_or("\0"),
80 );
81 let (def, def_destructor) = method_def.as_method_def()?;
82
83 let capsule = PyCapsule::new(
84 py,
85 ClosureDestructor::<F> {
86 closure,
87 def: UnsafeCell::new(def),
88 def_destructor,
89 },
90 Some(closure_capsule_name().to_owned()),
91 )?;
92
93 // Safety: just created the capsule with type ClosureDestructor<F> above
94 let data = unsafe { capsule.reference::<ClosureDestructor<F>>() };
95
96 unsafe {
97 py.from_owned_ptr_or_err::<PyCFunction>(ffi::PyCFunction_NewEx(
98 data.def.get(),
99 capsule.as_ptr(),
100 std::ptr::null_mut(),
101 ))
102 }
103 }
104
105 #[doc(hidden)]
106 pub fn internal_new<'py>(
107 method_def: &PyMethodDef,
108 py_or_module: PyFunctionArguments<'py>,
109 ) -> PyResult<&'py Self> {
110 let (py, module) = py_or_module.into_py_and_maybe_module();
111 let (mod_ptr, module_name) = if let Some(m) = module {
112 let mod_ptr = m.as_ptr();
113 let name: Py<PyAny> = m.name()?.into_py(py);
114 (mod_ptr, name.as_ptr())
115 } else {
116 (std::ptr::null_mut(), std::ptr::null_mut())
117 };
118 let (def, destructor) = method_def.as_method_def()?;
119
120 // FIXME: stop leaking the def and destructor
121 let def = Box::into_raw(Box::new(def));
122 std::mem::forget(destructor);
123
124 unsafe {
125 py.from_owned_ptr_or_err::<PyCFunction>(ffi::PyCFunction_NewEx(
126 def,
127 mod_ptr,
128 module_name,
129 ))
130 }
131 }
132}
133
134fn closure_capsule_name() -> &'static CStr {
135 // TODO replace this with const CStr once MSRV new enough
136 CStr::from_bytes_with_nul(bytes:b"pyo3-closure\0").unwrap()
137}
138
139unsafe extern "C" fn run_closure<F, R>(
140 capsule_ptr: *mut ffi::PyObject,
141 args: *mut ffi::PyObject,
142 kwargs: *mut ffi::PyObject,
143) -> *mut ffi::PyObject
144where
145 F: Fn(&PyTuple, Option<&PyDict>) -> R + Send + 'static,
146 R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
147{
148 crate::impl_::trampoline::cfunction_with_keywords(
149 slf:capsule_ptr,
150 args,
151 kwargs,
152 |py: Python<'_>, capsule_ptr: *mut PyObject, args: *mut PyObject, kwargs: *mut PyObject| {
153 let boxed_fn: &ClosureDestructor<F> =
154 &*(ffi::PyCapsule_GetPointer(capsule_ptr, name:closure_capsule_name().as_ptr())
155 as *mut ClosureDestructor<F>);
156 let args: &PyTuple = py.from_borrowed_ptr::<PyTuple>(args);
157 let kwargs: Option<&PyDict> = py.from_borrowed_ptr_or_opt::<PyDict>(ptr:kwargs);
158 crate::callback::convert(py, (boxed_fn.closure)(args, kwargs))
159 },
160 )
161}
162
163struct ClosureDestructor<F> {
164 closure: F,
165 // Wrapped in UnsafeCell because Python C-API wants a *mut pointer
166 // to this member.
167 def: UnsafeCell<ffi::PyMethodDef>,
168 // Used to destroy the cstrings in `def`, if necessary.
169 #[allow(dead_code)]
170 def_destructor: PyMethodDefDestructor,
171}
172
173// Safety: F is send and none of the fields are ever mutated
174unsafe impl<F: Send> Send for ClosureDestructor<F> {}
175
176/// Represents a Python function object.
177#[repr(transparent)]
178#[cfg(all(not(Py_LIMITED_API), not(all(PyPy, not(Py_3_8)))))]
179pub struct PyFunction(PyAny);
180
181#[cfg(all(not(Py_LIMITED_API), not(all(PyPy, not(Py_3_8)))))]
182pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), #checkfunction=ffi::PyFunction_Check);
183