add `call_bound` and `call_method_bound`
This commit is contained in:
parent
f2eb121f76
commit
50e33d86c7
|
@ -41,7 +41,7 @@ impl PyCounter {
|
||||||
&self,
|
&self,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
args: &PyTuple,
|
args: &PyTuple,
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<Bound<'_, PyDict>>,
|
||||||
) -> PyResult<Py<PyAny>> {
|
) -> PyResult<Py<PyAny>> {
|
||||||
let old_count = self.count.get();
|
let old_count = self.count.get();
|
||||||
let new_count = old_count + 1;
|
let new_count = old_count + 1;
|
||||||
|
@ -51,7 +51,7 @@ impl PyCounter {
|
||||||
println!("{} has been called {} time(s).", name, new_count);
|
println!("{} has been called {} time(s).", name, new_count);
|
||||||
|
|
||||||
// After doing something, we finally forward the call to the wrapped function
|
// 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
|
// We could do something with the return value of
|
||||||
// the function before returning it
|
// the function before returning it
|
||||||
|
|
|
@ -10,13 +10,13 @@ Any Python-native object reference (such as `&PyAny`, `&PyList`, or `&PyCell<MyC
|
||||||
|
|
||||||
PyO3 offers two APIs to make function calls:
|
PyO3 offers two APIs to make function calls:
|
||||||
|
|
||||||
* [`call`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.call) - call any callable Python object.
|
* [`call`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call) - call any callable Python object.
|
||||||
* [`call_method`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.call_method) - call a method on the Python object.
|
* [`call_method`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call_method) - call a method on the Python object.
|
||||||
|
|
||||||
Both of these APIs take `args` and `kwargs` arguments (for positional and keyword arguments respectively). There are variants for less complex calls:
|
Both of these APIs take `args` and `kwargs` arguments (for positional and keyword arguments respectively). There are variants for less complex calls:
|
||||||
|
|
||||||
* [`call1`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.call1) and [`call_method1`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.call_method1) to call only with positional `args`.
|
* [`call1`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call1) and [`call_method1`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call_method1) to call only with positional `args`.
|
||||||
* [`call0`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.call0) and [`call_method0`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyAny.html#method.call_method0) to call with no arguments.
|
* [`call0`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call0) and [`call_method0`]({{#PYO3_DOCS_URL}}/pyo3/prelude/trait.PyAnyMethods#tymethod.call_method0) to call with no arguments.
|
||||||
|
|
||||||
For convenience the [`Py<T>`](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.
|
For convenience the [`Py<T>`](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
|
// call object with PyDict
|
||||||
let kwargs = [(key1, val1)].into_py_dict(py);
|
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
|
// pass arguments as Vec
|
||||||
let kwargs = vec![(key1, val1), (key2, val2)];
|
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
|
// pass arguments as HashMap
|
||||||
let mut kwargs = HashMap::<&str, i32>::new();
|
let mut kwargs = HashMap::<&str, i32>::new();
|
||||||
kwargs.insert(key1, 1);
|
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(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
|
||||||
|
During PyO3's [migration from "GIL Refs" to the `Bound<T>` smart pointer](./migration.md#migrating-from-the-gil-refs-api-to-boundt), [`Py<T>::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<T>` smart pointer. The methods on the `&PyAny` GIL Ref such as `call` have not been given replacements, and the methods on the `Bound<PyAny>` smart pointer such as [`Bound<PyAny>::call`]({#PYO3_DOCS_URL}}/pyo3/prelude/trait.pyanymethods#tymethod.call) already use follow the newest API conventions.)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
## Executing existing Python code
|
## 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:
|
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:
|
||||||
|
|
|
@ -1016,14 +1016,30 @@ impl<T> Py<T> {
|
||||||
.setattr(attr_name, value.into_py(py).into_bound(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<A>(&self, py: Python<'_>, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
|
||||||
|
where
|
||||||
|
A: IntoPy<Py<PyTuple>>,
|
||||||
|
{
|
||||||
|
self.call_bound(py, args, kwargs.map(PyDict::as_borrowed).as_deref())
|
||||||
|
}
|
||||||
|
|
||||||
/// Calls the object.
|
/// Calls the object.
|
||||||
///
|
///
|
||||||
/// This is equivalent to the Python expression `self(*args, **kwargs)`.
|
/// This is equivalent to the Python expression `self(*args, **kwargs)`.
|
||||||
pub fn call(
|
pub fn call_bound(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
args: impl IntoPy<Py<PyTuple>>,
|
args: impl IntoPy<Py<PyTuple>>,
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<&Bound<'_, PyDict>>,
|
||||||
) -> PyResult<PyObject> {
|
) -> PyResult<PyObject> {
|
||||||
self.bind(py).as_any().call(args, kwargs).map(Bound::unbind)
|
self.bind(py).as_any().call(args, kwargs).map(Bound::unbind)
|
||||||
}
|
}
|
||||||
|
@ -1042,12 +1058,15 @@ impl<T> Py<T> {
|
||||||
self.bind(py).as_any().call0().map(Bound::unbind)
|
self.bind(py).as_any().call0().map(Bound::unbind)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a method on the object.
|
/// Deprecated form of [`call_method_bound`][Py::call_method_bound].
|
||||||
///
|
#[cfg_attr(
|
||||||
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`.
|
not(feature = "gil-refs"),
|
||||||
///
|
deprecated(
|
||||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern)
|
since = "0.21.0",
|
||||||
/// macro can be used to intern `name`.
|
note = "`call_method` will be replaced by `call_method_bound` in a future PyO3 version"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
#[inline]
|
||||||
pub fn call_method<N, A>(
|
pub fn call_method<N, A>(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
|
@ -1055,6 +1074,26 @@ impl<T> Py<T> {
|
||||||
args: A,
|
args: A,
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<&PyDict>,
|
||||||
) -> PyResult<PyObject>
|
) -> PyResult<PyObject>
|
||||||
|
where
|
||||||
|
N: IntoPy<Py<PyString>>,
|
||||||
|
A: IntoPy<Py<PyTuple>>,
|
||||||
|
{
|
||||||
|
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<N, A>(
|
||||||
|
&self,
|
||||||
|
py: Python<'_>,
|
||||||
|
name: N,
|
||||||
|
args: A,
|
||||||
|
kwargs: Option<&Bound<'_, PyDict>>,
|
||||||
|
) -> PyResult<PyObject>
|
||||||
where
|
where
|
||||||
N: IntoPy<Py<PyString>>,
|
N: IntoPy<Py<PyString>>,
|
||||||
A: IntoPy<Py<PyTuple>>,
|
A: IntoPy<Py<PyTuple>>,
|
||||||
|
@ -1490,23 +1529,34 @@ impl PyObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Bound, Py, PyObject};
|
use super::{Bound, Py, PyObject};
|
||||||
use crate::types::{PyDict, PyString};
|
use crate::types::{dict::IntoPyDict, PyDict, PyString};
|
||||||
use crate::{PyAny, PyNativeType, PyResult, Python, ToPyObject};
|
use crate::{PyAny, PyNativeType, PyResult, Python, ToPyObject};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_call0() {
|
fn test_call() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let obj = py.get_type::<PyDict>().to_object(py);
|
let obj = py.get_type::<PyDict>().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()
|
.unwrap()
|
||||||
.as_ref(py)
|
.as_ref(py),
|
||||||
.repr()
|
"{'x': 1}",
|
||||||
.unwrap()
|
|
||||||
.to_string_lossy(),
|
|
||||||
"{}"
|
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -447,7 +447,7 @@ impl PyAny {
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<&PyDict>,
|
||||||
) -> PyResult<&PyAny> {
|
) -> PyResult<&PyAny> {
|
||||||
self.as_borrowed()
|
self.as_borrowed()
|
||||||
.call(args, kwargs)
|
.call(args, kwargs.map(PyDict::as_borrowed).as_deref())
|
||||||
.map(Bound::into_gil_ref)
|
.map(Bound::into_gil_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@ impl PyAny {
|
||||||
A: IntoPy<Py<PyTuple>>,
|
A: IntoPy<Py<PyTuple>>,
|
||||||
{
|
{
|
||||||
self.as_borrowed()
|
self.as_borrowed()
|
||||||
.call_method(name, args, kwargs)
|
.call_method(name, args, kwargs.map(PyDict::as_borrowed).as_deref())
|
||||||
.map(Bound::into_gil_ref)
|
.map(Bound::into_gil_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1322,7 +1322,7 @@ pub trait PyAnyMethods<'py> {
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
args: impl IntoPy<Py<PyTuple>>,
|
args: impl IntoPy<Py<PyTuple>>,
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<&Bound<'_, PyDict>>,
|
||||||
) -> PyResult<Bound<'py, PyAny>>;
|
) -> PyResult<Bound<'py, PyAny>>;
|
||||||
|
|
||||||
/// Calls the object without arguments.
|
/// Calls the object without arguments.
|
||||||
|
@ -1415,7 +1415,7 @@ pub trait PyAnyMethods<'py> {
|
||||||
&self,
|
&self,
|
||||||
name: N,
|
name: N,
|
||||||
args: A,
|
args: A,
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<&Bound<'_, PyDict>>,
|
||||||
) -> PyResult<Bound<'py, PyAny>>
|
) -> PyResult<Bound<'py, PyAny>>
|
||||||
where
|
where
|
||||||
N: IntoPy<Py<PyString>>,
|
N: IntoPy<Py<PyString>>,
|
||||||
|
@ -1970,12 +1970,12 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
|
||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
args: impl IntoPy<Py<PyTuple>>,
|
args: impl IntoPy<Py<PyTuple>>,
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<&Bound<'_, PyDict>>,
|
||||||
) -> PyResult<Bound<'py, PyAny>> {
|
) -> PyResult<Bound<'py, PyAny>> {
|
||||||
fn inner<'py>(
|
fn inner<'py>(
|
||||||
any: &Bound<'py, PyAny>,
|
any: &Bound<'py, PyAny>,
|
||||||
args: Bound<'_, PyTuple>,
|
args: Bound<'_, PyTuple>,
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<&Bound<'_, PyDict>>,
|
||||||
) -> PyResult<Bound<'py, PyAny>> {
|
) -> PyResult<Bound<'py, PyAny>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::PyObject_Call(
|
ffi::PyObject_Call(
|
||||||
|
@ -2015,7 +2015,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
|
||||||
&self,
|
&self,
|
||||||
name: N,
|
name: N,
|
||||||
args: A,
|
args: A,
|
||||||
kwargs: Option<&PyDict>,
|
kwargs: Option<&Bound<'_, PyDict>>,
|
||||||
) -> PyResult<Bound<'py, PyAny>>
|
) -> PyResult<Bound<'py, PyAny>>
|
||||||
where
|
where
|
||||||
N: IntoPy<Py<PyString>>,
|
N: IntoPy<Py<PyString>>,
|
||||||
|
@ -2292,6 +2292,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
basic::CompareOp,
|
basic::CompareOp,
|
||||||
|
|
|
@ -6,8 +6,8 @@ error[E0277]: the trait bound `PyErr: From<MyError>` is not satisfied
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `From<T>`:
|
= help: the following other types implement trait `From<T>`:
|
||||||
<PyErr as From<PyBorrowError>>
|
<PyErr as From<PyBorrowError>>
|
||||||
<PyErr as From<PyBorrowMutError>>
|
|
||||||
<PyErr as From<std::io::Error>>
|
<PyErr as From<std::io::Error>>
|
||||||
|
<PyErr as From<PyBorrowMutError>>
|
||||||
<PyErr as From<PyDowncastError<'a>>>
|
<PyErr as From<PyDowncastError<'a>>>
|
||||||
<PyErr as From<DowncastError<'_, '_>>>
|
<PyErr as From<DowncastError<'_, '_>>>
|
||||||
<PyErr as From<DowncastIntoError<'_>>>
|
<PyErr as From<DowncastIntoError<'_>>>
|
||||||
|
|
Loading…
Reference in New Issue