Support passing name and doc to PyCFunction::new_closure. Fixes #2665

This commit is contained in:
Dexter Chua 2022-10-15 11:06:56 +08:00
parent 125af9b7de
commit 9201a7dd48
5 changed files with 20 additions and 11 deletions

View File

@ -0,0 +1 @@
Change PyCFunction::new_closure to take name and doc arguments.

View File

@ -102,11 +102,16 @@ impl PyCFunction {
/// let i = args.extract::<(i64,)>()?.0;
/// Ok(i+1)
/// };
/// let add_one = types::PyCFunction::new_closure(add_one, py).unwrap();
/// let add_one = types::PyCFunction::new_closure(py, None, None, add_one).unwrap();
/// py_run!(py, add_one, "assert add_one(42) == 43");
/// });
/// ```
pub fn new_closure<F, R>(f: F, py: Python<'_>) -> PyResult<&PyCFunction>
pub fn new_closure<'a, F, R>(
py: Python<'a>,
name: Option<&'static str>,
doc: Option<&'static str>,
f: F,
) -> PyResult<&'a PyCFunction>
where
F: Fn(&types::PyTuple, Option<&types::PyDict>) -> R + Send + 'static,
R: crate::callback::IntoPyCallbackOutput<*mut ffi::PyObject>,
@ -123,9 +128,9 @@ impl PyCFunction {
)?
};
let method_def = pymethods::PyMethodDef::cfunction_with_keywords(
"pyo3-closure",
name.unwrap_or("pyo3-closure\0"),
pymethods::PyCFunctionWithKeywords(run_closure::<F, R>),
"",
doc.unwrap_or("\0"),
);
Self::internal_new_from_pointers(&method_def, py, capsule.as_ptr(), std::ptr::null_mut())
}

View File

@ -418,9 +418,12 @@ fn test_closure() {
Ok(res)
})
};
let closure_py = PyCFunction::new_closure(f, py).unwrap();
let closure_py =
PyCFunction::new_closure(py, Some("test_fn"), Some("test_fn doc"), f).unwrap();
py_assert!(py, closure_py, "closure_py(42) == [43]");
py_assert!(py, closure_py, "closure_py.__name__ == 'test_fn'");
py_assert!(py, closure_py, "closure_py.__doc__ == 'test_fn doc'");
py_assert!(
py,
closure_py,
@ -439,7 +442,7 @@ fn test_closure_counter() {
*counter += 1;
Ok(*counter)
};
let counter_py = PyCFunction::new_closure(counter_fn, py).unwrap();
let counter_py = PyCFunction::new_closure(py, None, None, counter_fn).unwrap();
py_assert!(py, counter_py, "counter_py() == 1");
py_assert!(py, counter_py, "counter_py() == 2");

View File

@ -10,7 +10,7 @@ fn main() {
println!("This is five: {:?}", ref_.len());
Ok(())
};
PyCFunction::new_closure(closure_fn, py).unwrap().into()
PyCFunction::new_closure(py, None, None, closure_fn).unwrap().into()
});
Python::with_gil(|py| {

View File

@ -4,8 +4,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(closure_fn, py).unwrap().into()
| ---------------------------------------- argument requires that `local_data` is borrowed for `'static`
13 | PyCFunction::new_closure(py, None, None, closure_fn).unwrap().into()
| ---------------------------------------------------- argument requires that `local_data` is borrowed for `'static`
14 | });
| - `local_data` dropped here while still borrowed
@ -20,8 +20,8 @@ error[E0373]: closure may outlive the current function, but it borrows `ref_`, w
note: function requires argument type to outlive `'static`
--> tests/ui/invalid_closure.rs:13:9
|
13 | PyCFunction::new_closure(closure_fn, py).unwrap().into()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13 | PyCFunction::new_closure(py, None, None, closure_fn).unwrap().into()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `ref_` (and any other referenced variables), use the `move` keyword
|
9 | let closure_fn = move |_args: &PyTuple, _kwargs: Option<&PyDict>| -> PyResult<()> {