diff --git a/examples/decorator/src/lib.rs b/examples/decorator/src/lib.rs index bc369c62..fb2f2932 100644 --- a/examples/decorator/src/lib.rs +++ b/examples/decorator/src/lib.rs @@ -41,7 +41,7 @@ impl PyCounter { &self, py: Python<'_>, args: &PyTuple, - kwargs: Option<&PyDict>, + kwargs: Option>, ) -> PyResult> { let old_count = self.count.get(); let new_count = old_count + 1; @@ -51,7 +51,7 @@ impl PyCounter { println!("{} has been called {} time(s).", name, new_count); // After doing something, we finally forward the call to the wrapped function - let ret = self.wraps.call(py, args, kwargs)?; + let ret = self.wraps.call_bound(py, args, kwargs.as_ref())?; // We could do something with the return value of // the function before returning it diff --git a/guide/src/python_from_rust.md b/guide/src/python_from_rust.md index 6d403482..b81ba919 100644 --- a/guide/src/python_from_rust.md +++ b/guide/src/python_from_rust.md @@ -10,13 +10,13 @@ Any Python-native object reference (such as `&PyAny`, `&PyList`, or `&PyCell`](types.html#pyt-and-pyobject) smart pointer also exposes these same six API methods, but needs a `Python` token as an additional first argument to prove the GIL is held. @@ -95,22 +95,30 @@ fn main() -> PyResult<()> { // call object with PyDict let kwargs = [(key1, val1)].into_py_dict(py); - fun.call(py, (), Some(kwargs))?; + fun.call_bound(py, (), Some(&kwargs.as_borrowed()))?; // pass arguments as Vec let kwargs = vec![(key1, val1), (key2, val2)]; - fun.call(py, (), Some(kwargs.into_py_dict(py)))?; + fun.call_bound(py, (), Some(&kwargs.into_py_dict(py).as_borrowed()))?; // pass arguments as HashMap let mut kwargs = HashMap::<&str, i32>::new(); kwargs.insert(key1, 1); - fun.call(py, (), Some(kwargs.into_py_dict(py)))?; + fun.call_bound(py, (), Some(&kwargs.into_py_dict(py).as_borrowed()))?; Ok(()) }) } ``` +
+ +During PyO3's [migration from "GIL Refs" to the `Bound` smart pointer](./migration.md#migrating-from-the-gil-refs-api-to-boundt), [`Py::call`]({{#PYO3_DOCS_URL}}/pyo3/struct.py#method.call) is temporarily named `call_bound` (and `call_method` is temporarily `call_method_bound`). + +(This temporary naming is only the case for the `Py` smart pointer. The methods on the `&PyAny` GIL Ref such as `call` have not been given replacements, and the methods on the `Bound` smart pointer such as [`Bound::call`]({#PYO3_DOCS_URL}}/pyo3/prelude/trait.pyanymethods#tymethod.call) already use follow the newest API conventions.) + +
+ ## Executing existing Python code If you already have some existing Python code that you need to execute from Rust, the following FAQs can help you select the right PyO3 functionality for your situation: diff --git a/src/instance.rs b/src/instance.rs index 11936d6c..5779e9ad 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1016,14 +1016,30 @@ impl Py { .setattr(attr_name, value.into_py(py).into_bound(py)) } + /// Deprecated form of [`call_bound`][Py::call_bound]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`call` will be replaced by `call_bound` in a future PyO3 version" + ) + )] + #[inline] + pub fn call(&self, py: Python<'_>, args: A, kwargs: Option<&PyDict>) -> PyResult + where + A: IntoPy>, + { + self.call_bound(py, args, kwargs.map(PyDict::as_borrowed).as_deref()) + } + /// Calls the object. /// /// This is equivalent to the Python expression `self(*args, **kwargs)`. - pub fn call( + pub fn call_bound( &self, py: Python<'_>, args: impl IntoPy>, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyResult { self.bind(py).as_any().call(args, kwargs).map(Bound::unbind) } @@ -1042,12 +1058,15 @@ impl Py { self.bind(py).as_any().call0().map(Bound::unbind) } - /// Calls a method on the object. - /// - /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. - /// - /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) - /// macro can be used to intern `name`. + /// Deprecated form of [`call_method_bound`][Py::call_method_bound]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`call_method` will be replaced by `call_method_bound` in a future PyO3 version" + ) + )] + #[inline] pub fn call_method( &self, py: Python<'_>, @@ -1055,6 +1074,26 @@ impl Py { args: A, kwargs: Option<&PyDict>, ) -> PyResult + where + N: IntoPy>, + A: IntoPy>, + { + self.call_method_bound(py, name, args, kwargs.map(PyDict::as_borrowed).as_deref()) + } + + /// Calls a method on the object. + /// + /// This is equivalent to the Python expression `self.name(*args, **kwargs)`. + /// + /// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern) + /// macro can be used to intern `name`. + pub fn call_method_bound( + &self, + py: Python<'_>, + name: N, + args: A, + kwargs: Option<&Bound<'_, PyDict>>, + ) -> PyResult where N: IntoPy>, A: IntoPy>, @@ -1490,23 +1529,34 @@ impl PyObject { } #[cfg(test)] +#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))] mod tests { use super::{Bound, Py, PyObject}; - use crate::types::{PyDict, PyString}; + use crate::types::{dict::IntoPyDict, PyDict, PyString}; use crate::{PyAny, PyNativeType, PyResult, Python, ToPyObject}; #[test] - fn test_call0() { + fn test_call() { Python::with_gil(|py| { let obj = py.get_type::().to_object(py); - assert_eq!( - obj.call0(py) + + let assert_repr = |obj: &PyAny, expected: &str| { + assert_eq!(obj.repr().unwrap().to_str().unwrap(), expected); + }; + + assert_repr(obj.call0(py).unwrap().as_ref(py), "{}"); + assert_repr(obj.call1(py, ()).unwrap().as_ref(py), "{}"); + assert_repr(obj.call(py, (), None).unwrap().as_ref(py), "{}"); + + assert_repr( + obj.call1(py, ((('x', 1),),)).unwrap().as_ref(py), + "{'x': 1}", + ); + assert_repr( + obj.call(py, (), Some([('x', 1)].into_py_dict(py))) .unwrap() - .as_ref(py) - .repr() - .unwrap() - .to_string_lossy(), - "{}" + .as_ref(py), + "{'x': 1}", ); }) } diff --git a/src/types/any.rs b/src/types/any.rs index 7d5f226a..9cdf78d6 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -447,7 +447,7 @@ impl PyAny { kwargs: Option<&PyDict>, ) -> PyResult<&PyAny> { self.as_borrowed() - .call(args, kwargs) + .call(args, kwargs.map(PyDict::as_borrowed).as_deref()) .map(Bound::into_gil_ref) } @@ -547,7 +547,7 @@ impl PyAny { A: IntoPy>, { self.as_borrowed() - .call_method(name, args, kwargs) + .call_method(name, args, kwargs.map(PyDict::as_borrowed).as_deref()) .map(Bound::into_gil_ref) } @@ -1322,7 +1322,7 @@ pub trait PyAnyMethods<'py> { fn call( &self, args: impl IntoPy>, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyResult>; /// Calls the object without arguments. @@ -1415,7 +1415,7 @@ pub trait PyAnyMethods<'py> { &self, name: N, args: A, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyResult> where N: IntoPy>, @@ -1970,12 +1970,12 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { fn call( &self, args: impl IntoPy>, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyResult> { fn inner<'py>( any: &Bound<'py, PyAny>, args: Bound<'_, PyTuple>, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyResult> { unsafe { ffi::PyObject_Call( @@ -2015,7 +2015,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { &self, name: N, args: A, - kwargs: Option<&PyDict>, + kwargs: Option<&Bound<'_, PyDict>>, ) -> PyResult> where N: IntoPy>, @@ -2292,6 +2292,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { } #[cfg(test)] +#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))] mod tests { use crate::{ basic::CompareOp, diff --git a/tests/ui/invalid_result_conversion.stderr b/tests/ui/invalid_result_conversion.stderr index f1a429a5..2720c71d 100644 --- a/tests/ui/invalid_result_conversion.stderr +++ b/tests/ui/invalid_result_conversion.stderr @@ -6,8 +6,8 @@ error[E0277]: the trait bound `PyErr: From` is not satisfied | = help: the following other types implement trait `From`: > - > > + > >> >> >>