Update new_closure_bound closure signature (#3883)

* Update new_closure_bound closure signature

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Use anonymous lifetimes in closure bounds

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Take &Bound in PyCFunction closures

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
This commit is contained in:
Lily Foote 2024-02-23 14:07:54 +00:00 committed by GitHub
parent fbf2e91914
commit e145ae851a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 35 additions and 23 deletions

View File

@ -101,7 +101,10 @@ impl PyCFunction {
F: Fn(&PyTuple, Option<&PyDict>) -> R + Send + 'static,
R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
{
Self::new_closure_bound(py, name, doc, closure).map(Bound::into_gil_ref)
Self::new_closure_bound(py, name, doc, move |args, kwargs| {
closure(args.as_gil_ref(), kwargs.map(Bound::as_gil_ref))
})
.map(Bound::into_gil_ref)
}
/// Create a new function from a closure.
@ -113,7 +116,7 @@ impl PyCFunction {
/// # use pyo3::{py_run, types::{PyCFunction, PyDict, PyTuple}};
///
/// Python::with_gil(|py| {
/// let add_one = |args: &PyTuple, _kwargs: Option<&PyDict>| -> PyResult<_> {
/// let add_one = |args: &Bound<'_, PyTuple>, _kwargs: Option<&Bound<'_, PyDict>>| -> PyResult<_> {
/// let i = args.extract::<(i64,)>()?.0;
/// Ok(i+1)
/// };
@ -121,14 +124,14 @@ impl PyCFunction {
/// py_run!(py, add_one, "assert add_one(42) == 43");
/// });
/// ```
pub fn new_closure_bound<'a, F, R>(
py: Python<'a>,
pub fn new_closure_bound<'py, F, R>(
py: Python<'py>,
name: Option<&'static str>,
doc: Option<&'static str>,
closure: F,
) -> PyResult<Bound<'a, Self>>
) -> PyResult<Bound<'py, Self>>
where
F: Fn(&PyTuple, Option<&PyDict>) -> R + Send + 'static,
F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
{
let method_def = pymethods::PyMethodDef::cfunction_with_keywords(
@ -199,9 +202,11 @@ unsafe extern "C" fn run_closure<F, R>(
kwargs: *mut ffi::PyObject,
) -> *mut ffi::PyObject
where
F: Fn(&PyTuple, Option<&PyDict>) -> R + Send + 'static,
F: Fn(&Bound<'_, PyTuple>, Option<&Bound<'_, PyDict>>) -> R + Send + 'static,
R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
{
use crate::types::any::PyAnyMethods;
crate::impl_::trampoline::cfunction_with_keywords(
capsule_ptr,
args,
@ -210,9 +215,12 @@ where
let boxed_fn: &ClosureDestructor<F> =
&*(ffi::PyCapsule_GetPointer(capsule_ptr, closure_capsule_name().as_ptr())
as *mut ClosureDestructor<F>);
let args = py.from_borrowed_ptr::<PyTuple>(args);
let kwargs = py.from_borrowed_ptr_or_opt::<PyDict>(kwargs);
crate::callback::convert(py, (boxed_fn.closure)(args, kwargs))
let args = Bound::ref_from_ptr(py, &args).downcast_unchecked::<PyTuple>();
let kwargs = Bound::ref_from_ptr_or_opt(py, &kwargs)
.as_ref()
.map(|b| b.downcast_unchecked::<PyDict>());
let result = (boxed_fn.closure)(args, kwargs);
crate::callback::convert(py, result)
},
)
}

View File

@ -401,7 +401,9 @@ fn test_pycfunction_new_with_keywords() {
#[test]
fn test_closure() {
Python::with_gil(|py| {
let f = |args: &types::PyTuple, _kwargs: Option<&types::PyDict>| -> PyResult<_> {
let f = |args: &Bound<'_, types::PyTuple>,
_kwargs: Option<&Bound<'_, types::PyDict>>|
-> PyResult<_> {
Python::with_gil(|py| {
let res: Vec<_> = args
.iter()
@ -439,12 +441,13 @@ fn test_closure() {
fn test_closure_counter() {
Python::with_gil(|py| {
let counter = std::cell::RefCell::new(0);
let counter_fn =
move |_args: &types::PyTuple, _kwargs: Option<&types::PyDict>| -> PyResult<i32> {
let mut counter = counter.borrow_mut();
*counter += 1;
Ok(*counter)
};
let counter_fn = move |_args: &Bound<'_, types::PyTuple>,
_kwargs: Option<&Bound<'_, types::PyDict>>|
-> PyResult<i32> {
let mut counter = counter.borrow_mut();
*counter += 1;
Ok(*counter)
};
let counter_py = PyCFunction::new_closure_bound(py, None, None, counter_fn).unwrap();
py_assert!(py, counter_py, "counter_py() == 1");

View File

@ -6,10 +6,11 @@ fn main() {
let local_data = vec![0, 1, 2, 3, 4];
let ref_: &[u8] = &local_data;
let closure_fn = |_args: &PyTuple, _kwargs: Option<&PyDict>| -> PyResult<()> {
println!("This is five: {:?}", ref_.len());
Ok(())
};
let closure_fn =
|_args: &Bound<'_, PyTuple>, _kwargs: Option<&Bound<'_, PyDict>>| -> PyResult<()> {
println!("This is five: {:?}", ref_.len());
Ok(())
};
PyCFunction::new_closure_bound(py, None, None, closure_fn)
.unwrap()
.into()

View File

@ -6,8 +6,8 @@ error[E0597]: `local_data` does not live long enough
7 | let ref_: &[u8] = &local_data;
| ^^^^^^^^^^^ borrowed value does not live long enough
...
13 | PyCFunction::new_closure_bound(py, None, None, closure_fn)
14 | PyCFunction::new_closure_bound(py, None, None, closure_fn)
| ---------------------------------------------------------- argument requires that `local_data` is borrowed for `'static`
...
16 | });
17 | });
| - `local_data` dropped here while still borrowed