| 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 | |