diff --git a/newsfragments/2686.changed.md b/newsfragments/2686.changed.md new file mode 100644 index 00000000000..a1c54ea07a0 --- /dev/null +++ b/newsfragments/2686.changed.md @@ -0,0 +1 @@ +Change PyCFunction::new_closure to take name and doc arguments. diff --git a/src/types/function.rs b/src/types/function.rs index d01b7ddadcb..4feb6386045 100644 --- a/src/types/function.rs +++ b/src/types/function.rs @@ -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: 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::), - "", + doc.unwrap_or("\0"), ); Self::internal_new_from_pointers(&method_def, py, capsule.as_ptr(), std::ptr::null_mut()) } diff --git a/tests/test_pyfunction.rs b/tests/test_pyfunction.rs index f42c6f7e922..cbb5233de8e 100644 --- a/tests/test_pyfunction.rs +++ b/tests/test_pyfunction.rs @@ -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"); diff --git a/tests/ui/invalid_closure.rs b/tests/ui/invalid_closure.rs index 58f7148c36d..b724f31b97e 100644 --- a/tests/ui/invalid_closure.rs +++ b/tests/ui/invalid_closure.rs @@ -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| { diff --git a/tests/ui/invalid_closure.stderr b/tests/ui/invalid_closure.stderr index 897cbb6cea1..4844f766887 100644 --- a/tests/ui/invalid_closure.stderr +++ b/tests/ui/invalid_closure.stderr @@ -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<()> {