From 89e7801b3f0897d0e5a9893d9b3d6a63deb9bb1c Mon Sep 17 00:00:00 2001
From: Icxolu <10486322+Icxolu@users.noreply.github.com>
Date: Sat, 24 Aug 2024 01:10:13 +0200
Subject: [PATCH 1/2] initial migration of the trait bounds to `IntoPyObject`
---
guide/src/migration.md | 65 +++++++
src/conversions/chrono.rs | 17 +-
src/conversions/std/num.rs | 10 +
src/instance.rs | 61 +++---
src/prelude.rs | 2 +-
src/types/any.rs | 387 ++++++++++++++++++++++++++-----------
src/types/bytes.rs | 8 +-
src/types/datetime.rs | 6 +-
src/types/mapping.rs | 33 ++--
9 files changed, 425 insertions(+), 164 deletions(-)
diff --git a/guide/src/migration.md b/guide/src/migration.md
index 8a6ffedc73c..b1dfc9e30c4 100644
--- a/guide/src/migration.md
+++ b/guide/src/migration.md
@@ -133,6 +133,71 @@ This is purely additional and should just extend the possible return types.
+### Python API trait bounds changed
+
+Click to expand
+
+PyO3 0.23 introduces a new unified `IntoPyObject` trait to convert Rust types into Python objects.
+Notable features of this new trait:
+- conversions can now return an error
+- compared to `IntoPy` the generic `T` moved into an associated type, so
+ - there is now only one way to convert a given type
+ - the output type is stronger typed and may return any Python type instead of just `PyAny`
+- byte collections are special handled and convert into `PyBytes` now, see [above](#macro-conversion-changed-for-byte-collections-vecu8-u8-n-and-smallvecu8-n)
+- `()` (unit) is now only special handled in return position and otherwise converts into an empty `PyTuple`
+
+All PyO3 provided types as well as `#[pyclass]`es already implement `IntoPyObject`. Other types will
+need to adapt an implementation of `IntoPyObject` to stay compatible with the Python APIs.
+
+
+Before:
+```rust
+# use pyo3::prelude::*;
+# #[allow(dead_code)]
+struct MyPyObjectWrapper(PyObject);
+
+impl IntoPy for MyPyObjectWrapper {
+ fn into_py(self, py: Python<'_>) -> PyObject {
+ self.0
+ }
+}
+
+impl ToPyObject for MyPyObjectWrapper {
+ fn to_object(&self, py: Python<'_>) -> PyObject {
+ self.0.clone_ref(py)
+ }
+}
+```
+
+After:
+```rust
+# use pyo3::prelude::*;
+# #[allow(dead_code)]
+# struct MyPyObjectWrapper(PyObject);
+
+impl<'py> IntoPyObject<'py> for MyPyObjectWrapper {
+ type Target = PyAny; // the Python type
+ type Output = Bound<'py, Self::Target>; // in most cases this will be `Bound`
+ type Error = std::convert::Infallible;
+
+ fn into_pyobject(self, py: Python<'py>) -> Result {
+ Ok(self.0.into_bound(py))
+ }
+}
+
+// `ToPyObject` implementations should be converted to implementations on reference types
+impl<'a, 'py> IntoPyObject<'py> for &'a MyPyObjectWrapper {
+ type Target = PyAny;
+ type Output = Borrowed<'a, 'py, Self::Target>; // `Borrowed` can be used to optimized reference counting
+ type Error = std::convert::Infallible;
+
+ fn into_pyobject(self, py: Python<'py>) -> Result {
+ Ok(self.0.bind_borrowed(py))
+ }
+}
+```
+
+
## from 0.21.* to 0.22
### Deprecation of `gil-refs` feature continues
diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs
index 48246d827db..64ce4b23a4b 100644
--- a/src/conversions/chrono.rs
+++ b/src/conversions/chrono.rs
@@ -47,6 +47,7 @@ use crate::sync::GILOnceCell;
use crate::types::any::PyAnyMethods;
#[cfg(not(Py_LIMITED_API))]
use crate::types::datetime::timezone_from_offset;
+use crate::types::PyNone;
#[cfg(not(Py_LIMITED_API))]
use crate::types::{
timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess,
@@ -551,12 +552,12 @@ impl FromPyObject<'_> for FixedOffset {
#[cfg(Py_LIMITED_API)]
check_type(ob, &DatetimeTypes::get(ob.py()).tzinfo, "PyTzInfo")?;
- // Passing `()` (so Python's None) to the `utcoffset` function will only
+ // Passing Python's None to the `utcoffset` function will only
// work for timezones defined as fixed offsets in Python.
// Any other timezone would require a datetime as the parameter, and return
// None if the datetime is not provided.
// Trying to convert None to a PyDelta in the next line will then fail.
- let py_timedelta = ob.call_method1("utcoffset", ((),))?;
+ let py_timedelta = ob.call_method1("utcoffset", (PyNone::get(ob.py()),))?;
if py_timedelta.is_none() {
return Err(PyTypeError::new_err(format!(
"{:?} is not a fixed offset timezone",
@@ -810,7 +811,7 @@ fn timezone_utc(py: Python<'_>) -> Bound<'_, PyAny> {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{types::PyTuple, Py};
+ use crate::types::PyTuple;
use std::{cmp::Ordering, panic};
#[test]
@@ -1318,11 +1319,11 @@ mod tests {
})
}
- fn new_py_datetime_ob<'py>(
- py: Python<'py>,
- name: &str,
- args: impl IntoPy>,
- ) -> Bound<'py, PyAny> {
+ fn new_py_datetime_ob<'py, A>(py: Python<'py>, name: &str, args: A) -> Bound<'py, PyAny>
+ where
+ A: IntoPyObject<'py, Target = PyTuple>,
+ A::Error: Into,
+ {
py.import("datetime")
.unwrap()
.getattr(name)
diff --git a/src/conversions/std/num.rs b/src/conversions/std/num.rs
index 954aebb14a3..80bc678fddf 100644
--- a/src/conversions/std/num.rs
+++ b/src/conversions/std/num.rs
@@ -46,6 +46,16 @@ macro_rules! int_fits_larger_int {
}
}
+ impl<'py> IntoPyObject<'py> for &$rust_type {
+ type Target = PyInt;
+ type Output = Bound<'py, Self::Target>;
+ type Error = Infallible;
+
+ fn into_pyobject(self, py: Python<'py>) -> Result {
+ (*self).into_pyobject(py)
+ }
+ }
+
impl FromPyObject<'_> for $rust_type {
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult {
let val: $larger_type = obj.extract()?;
diff --git a/src/instance.rs b/src/instance.rs
index 6e4e9ab23e7..ed0c133cc18 100644
--- a/src/instance.rs
+++ b/src/instance.rs
@@ -1,3 +1,4 @@
+use crate::conversion::IntoPyObject;
use crate::err::{self, PyErr, PyResult};
use crate::impl_::pycell::PyClassObject;
use crate::internal_tricks::ptr_from_ref;
@@ -1426,9 +1427,10 @@ impl Py {
/// # version(sys, py).unwrap();
/// # });
/// ```
- pub fn getattr(&self, py: Python<'_>, attr_name: N) -> PyResult
+ pub fn getattr<'py, N>(&self, py: Python<'py>, attr_name: N) -> PyResult
where
- N: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into,
{
self.bind(py).as_any().getattr(attr_name).map(Bound::unbind)
}
@@ -1455,32 +1457,40 @@ impl Py {
/// # set_answer(ob, py).unwrap();
/// # });
/// ```
- pub fn setattr(&self, py: Python<'_>, attr_name: N, value: V) -> PyResult<()>
+ pub fn setattr<'py, N, V>(&self, py: Python<'py>, attr_name: N, value: V) -> PyResult<()>
where
- N: IntoPy>,
- V: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ V: IntoPyObject<'py>,
+ N::Error: Into,
+ V::Error: Into,
{
- self.bind(py)
- .as_any()
- .setattr(attr_name, value.into_py(py).into_bound(py))
+ self.bind(py).as_any().setattr(attr_name, value)
}
/// Calls the object.
///
/// This is equivalent to the Python expression `self(*args, **kwargs)`.
- pub fn call_bound(
+ pub fn call_bound<'py, N>(
&self,
- py: Python<'_>,
- args: impl IntoPy>,
- kwargs: Option<&Bound<'_, PyDict>>,
- ) -> PyResult {
+ py: Python<'py>,
+ args: N,
+ kwargs: Option<&Bound<'py, PyDict>>,
+ ) -> PyResult
+ where
+ N: IntoPyObject<'py, Target = PyTuple>,
+ N::Error: Into,
+ {
self.bind(py).as_any().call(args, kwargs).map(Bound::unbind)
}
/// Calls the object with only positional arguments.
///
/// This is equivalent to the Python expression `self(*args)`.
- pub fn call1(&self, py: Python<'_>, args: impl IntoPy>) -> PyResult {
+ pub fn call1<'py, N>(&self, py: Python<'py>, args: N) -> PyResult
+ where
+ N: IntoPyObject<'py, Target = PyTuple>,
+ N::Error: Into,
+ {
self.bind(py).as_any().call1(args).map(Bound::unbind)
}
@@ -1497,16 +1507,18 @@ impl Py {
///
/// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern)
/// macro can be used to intern `name`.
- pub fn call_method_bound(
+ pub fn call_method_bound<'py, N, A>(
&self,
- py: Python<'_>,
+ py: Python<'py>,
name: N,
args: A,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult
where
- N: IntoPy>,
- A: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ A: IntoPyObject<'py, Target = PyTuple>,
+ N::Error: Into,
+ A::Error: Into,
{
self.bind(py)
.as_any()
@@ -1520,10 +1532,12 @@ impl Py {
///
/// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern)
/// macro can be used to intern `name`.
- pub fn call_method1(&self, py: Python<'_>, name: N, args: A) -> PyResult
+ pub fn call_method1<'py, N, A>(&self, py: Python<'py>, name: N, args: A) -> PyResult
where
- N: IntoPy>,
- A: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ A: IntoPyObject<'py, Target = PyTuple>,
+ N::Error: Into,
+ A::Error: Into,
{
self.bind(py)
.as_any()
@@ -1537,9 +1551,10 @@ impl Py {
///
/// To avoid repeated temporary allocations of Python strings, the [`intern!`](crate::intern)
/// macro can be used to intern `name`.
- pub fn call_method0(&self, py: Python<'_>, name: N) -> PyResult
+ pub fn call_method0<'py, N>(&self, py: Python<'py>, name: N) -> PyResult
where
- N: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into,
{
self.bind(py).as_any().call_method0(name).map(Bound::unbind)
}
diff --git a/src/prelude.rs b/src/prelude.rs
index 6182b21c2d1..b2b86c8449d 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -8,7 +8,7 @@
//! use pyo3::prelude::*;
//! ```
-pub use crate::conversion::{FromPyObject, IntoPy, ToPyObject};
+pub use crate::conversion::{FromPyObject, IntoPy, IntoPyObject, ToPyObject};
pub use crate::err::{PyErr, PyResult};
pub use crate::instance::{Borrowed, Bound, Py, PyObject};
pub use crate::marker::Python;
diff --git a/src/types/any.rs b/src/types/any.rs
index 6dff9a1264d..72030e6041a 100644
--- a/src/types/any.rs
+++ b/src/types/any.rs
@@ -1,5 +1,5 @@
use crate::class::basic::CompareOp;
-use crate::conversion::{AsPyPointer, FromPyObjectBound, IntoPy, ToPyObject};
+use crate::conversion::{AsPyPointer, FromPyObjectBound, IntoPyObject};
use crate::err::{DowncastError, DowncastIntoError, PyErr, PyResult};
use crate::exceptions::{PyAttributeError, PyTypeError};
use crate::ffi_ptr_ext::FfiPtrExt;
@@ -10,7 +10,7 @@ use crate::type_object::{PyTypeCheck, PyTypeInfo};
#[cfg(not(any(PyPy, GraalPy)))]
use crate::types::PySuper;
use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
-use crate::{err, ffi, Py, Python};
+use crate::{err, ffi, BoundObject, Python};
use std::cell::UnsafeCell;
use std::cmp::Ordering;
use std::os::raw::c_int;
@@ -81,7 +81,8 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// ```
fn hasattr(&self, attr_name: N) -> PyResult
where
- N: IntoPy>;
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into;
/// Retrieves an attribute value.
///
@@ -107,7 +108,8 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// ```
fn getattr(&self, attr_name: N) -> PyResult>
where
- N: IntoPy>;
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into;
/// Sets an attribute value.
///
@@ -133,8 +135,10 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// ```
fn setattr(&self, attr_name: N, value: V) -> PyResult<()>
where
- N: IntoPy>,
- V: ToPyObject;
+ N: IntoPyObject<'py, Target = PyString>,
+ V: IntoPyObject<'py>,
+ N::Error: Into,
+ V::Error: Into;
/// Deletes an attribute.
///
@@ -144,7 +148,8 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// to intern `attr_name`.
fn delattr(&self, attr_name: N) -> PyResult<()>
where
- N: IntoPy>;
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into;
/// Returns an [`Ordering`] between `self` and `other`.
///
@@ -194,7 +199,8 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// ```
fn compare(&self, other: O) -> PyResult
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Tests whether two Python objects obey a given [`CompareOp`].
///
@@ -232,7 +238,8 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// ```
fn rich_compare(&self, other: O, compare_op: CompareOp) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes the negative of self.
///
@@ -257,114 +264,135 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// This is equivalent to the Python expression `self < other`.
fn lt(&self, other: O) -> PyResult
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Tests whether this object is less than or equal to another.
///
/// This is equivalent to the Python expression `self <= other`.
fn le(&self, other: O) -> PyResult
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Tests whether this object is equal to another.
///
/// This is equivalent to the Python expression `self == other`.
fn eq(&self, other: O) -> PyResult
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Tests whether this object is not equal to another.
///
/// This is equivalent to the Python expression `self != other`.
fn ne(&self, other: O) -> PyResult
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Tests whether this object is greater than another.
///
/// This is equivalent to the Python expression `self > other`.
fn gt(&self, other: O) -> PyResult
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Tests whether this object is greater than or equal to another.
///
/// This is equivalent to the Python expression `self >= other`.
fn ge(&self, other: O) -> PyResult
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self + other`.
fn add(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self - other`.
fn sub(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self * other`.
fn mul(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self @ other`.
fn matmul(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self / other`.
fn div(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self // other`.
fn floor_div(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self % other`.
fn rem(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `divmod(self, other)`.
fn divmod(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self << other`.
fn lshift(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self >> other`.
fn rshift(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self ** other % modulus` (`pow(self, other, modulus)`).
/// `py.None()` may be passed for the `modulus`.
fn pow(&self, other: O1, modulus: O2) -> PyResult>
where
- O1: ToPyObject,
- O2: ToPyObject;
+ O1: IntoPyObject<'py>,
+ O2: IntoPyObject<'py>,
+ O1::Error: Into,
+ O2::Error: Into;
/// Computes `self & other`.
fn bitand(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self | other`.
fn bitor(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Computes `self ^ other`.
fn bitxor(&self, other: O) -> PyResult>
where
- O: ToPyObject;
+ O: IntoPyObject<'py>,
+ O::Error: Into;
/// Determines whether this object appears callable.
///
@@ -427,11 +455,10 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// })
/// # }
/// ```
- fn call(
- &self,
- args: impl IntoPy>,
- kwargs: Option<&Bound<'_, PyDict>>,
- ) -> PyResult>;
+ fn call(&self, args: A, kwargs: Option<&Bound<'_, PyDict>>) -> PyResult>
+ where
+ A: IntoPyObject<'py, Target = PyTuple>,
+ A::Error: Into;
/// Calls the object without arguments.
///
@@ -484,7 +511,10 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// })
/// # }
/// ```
- fn call1(&self, args: impl IntoPy>) -> PyResult>;
+ fn call1(&self, args: A) -> PyResult>
+ where
+ A: IntoPyObject<'py, Target = PyTuple>,
+ A::Error: Into;
/// Calls a method on the object.
///
@@ -530,8 +560,10 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult>
where
- N: IntoPy>,
- A: IntoPy>;
+ N: IntoPyObject<'py, Target = PyString>,
+ A: IntoPyObject<'py, Target = PyTuple>,
+ N::Error: Into,
+ A::Error: Into;
/// Calls a method on the object without arguments.
///
@@ -568,7 +600,8 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// ```
fn call_method0(&self, name: N) -> PyResult>
where
- N: IntoPy>;
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into;
/// Calls a method on the object with only positional arguments.
///
@@ -606,8 +639,10 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// ```
fn call_method1(&self, name: N, args: A) -> PyResult>
where
- N: IntoPy>,
- A: IntoPy>;
+ N: IntoPyObject<'py, Target = PyString>,
+ A: IntoPyObject<'py, Target = PyTuple>,
+ N::Error: Into,
+ A::Error: Into;
/// Returns whether the object is considered to be true.
///
@@ -635,22 +670,26 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// This is equivalent to the Python expression `self[key]`.
fn get_item(&self, key: K) -> PyResult>
where
- K: ToPyObject;
+ K: IntoPyObject<'py>,
+ K::Error: Into;
/// Sets a collection item value.
///
/// This is equivalent to the Python expression `self[key] = value`.
fn set_item(&self, key: K, value: V) -> PyResult<()>
where
- K: ToPyObject,
- V: ToPyObject;
+ K: IntoPyObject<'py>,
+ V: IntoPyObject<'py>,
+ K::Error: Into,
+ V::Error: Into;
/// Deletes an item from the collection.
///
/// This is equivalent to the Python expression `del self[key]`.
fn del_item(&self, key: K) -> PyResult<()>
where
- K: ToPyObject;
+ K: IntoPyObject<'py>,
+ K::Error: Into;
/// Takes an object and returns an iterator for it.
///
@@ -862,7 +901,8 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// This is equivalent to the Python expression `value in self`.
fn contains(&self, value: V) -> PyResult
where
- V: ToPyObject;
+ V: IntoPyObject<'py>,
+ V::Error: Into;
/// Return a proxy object that delegates method calls to a parent or sibling class of type.
///
@@ -876,17 +916,25 @@ macro_rules! implement_binop {
#[doc = concat!("Computes `self ", $op, " other`.")]
fn $name(&self, other: O) -> PyResult>
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
fn inner<'py>(
any: &Bound<'py, PyAny>,
- other: Bound<'_, PyAny>,
+ other: &Bound<'_, PyAny>,
) -> PyResult> {
unsafe { ffi::$c_api(any.as_ptr(), other.as_ptr()).assume_owned_or_err(any.py()) }
}
let py = self.py();
- inner(self, other.to_object(py).into_bound(py))
+ inner(
+ self,
+ &other
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .into_any()
+ .as_borrowed(),
+ )
}
};
}
@@ -899,7 +947,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn hasattr(&self, attr_name: N) -> PyResult
where
- N: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into,
{
// PyObject_HasAttr suppresses all exceptions, which was the behaviour of `hasattr` in Python 2.
// Use an implementation which suppresses only AttributeError, which is consistent with `hasattr` in Python 3.
@@ -911,16 +960,17 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
}
}
- inner(self.py(), self.getattr(attr_name))
+ inner(self.py(), self.getattr(attr_name).map_err(Into::into))
}
fn getattr(&self, attr_name: N) -> PyResult>
where
- N: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into,
{
fn inner<'py>(
any: &Bound<'py, PyAny>,
- attr_name: Bound<'_, PyString>,
+ attr_name: &Bound<'_, PyString>,
) -> PyResult> {
unsafe {
ffi::PyObject_GetAttr(any.as_ptr(), attr_name.as_ptr())
@@ -928,19 +978,26 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
}
}
- let py = self.py();
- inner(self, attr_name.into_py(self.py()).into_bound(py))
+ inner(
+ self,
+ &attr_name
+ .into_pyobject(self.py())
+ .map_err(Into::into)?
+ .as_borrowed(),
+ )
}
fn setattr(&self, attr_name: N, value: V) -> PyResult<()>
where
- N: IntoPy>,
- V: ToPyObject,
+ N: IntoPyObject<'py, Target = PyString>,
+ V: IntoPyObject<'py>,
+ N::Error: Into,
+ V::Error: Into,
{
fn inner(
any: &Bound<'_, PyAny>,
- attr_name: Bound<'_, PyString>,
- value: Bound<'_, PyAny>,
+ attr_name: &Bound<'_, PyString>,
+ value: &Bound<'_, PyAny>,
) -> PyResult<()> {
err::error_on_minusone(any.py(), unsafe {
ffi::PyObject_SetAttr(any.as_ptr(), attr_name.as_ptr(), value.as_ptr())
@@ -950,30 +1007,45 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
let py = self.py();
inner(
self,
- attr_name.into_py(py).into_bound(py),
- value.to_object(py).into_bound(py),
+ &attr_name
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .as_borrowed(),
+ &value
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .into_any()
+ .as_borrowed(),
)
}
fn delattr(&self, attr_name: N) -> PyResult<()>
where
- N: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into,
{
- fn inner(any: &Bound<'_, PyAny>, attr_name: Bound<'_, PyString>) -> PyResult<()> {
+ fn inner(any: &Bound<'_, PyAny>, attr_name: &Bound<'_, PyString>) -> PyResult<()> {
err::error_on_minusone(any.py(), unsafe {
ffi::PyObject_DelAttr(any.as_ptr(), attr_name.as_ptr())
})
}
let py = self.py();
- inner(self, attr_name.into_py(py).into_bound(py))
+ inner(
+ self,
+ &attr_name
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .as_borrowed(),
+ )
}
fn compare(&self, other: O) -> PyResult
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
- fn inner(any: &Bound<'_, PyAny>, other: Bound<'_, PyAny>) -> PyResult {
+ fn inner(any: &Bound<'_, PyAny>, other: &Bound<'_, PyAny>) -> PyResult {
let other = other.as_ptr();
// Almost the same as ffi::PyObject_RichCompareBool, but this one doesn't try self == other.
// See https://github.com/PyO3/pyo3/issues/985 for more.
@@ -996,16 +1068,24 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
}
let py = self.py();
- inner(self, other.to_object(py).into_bound(py))
+ inner(
+ self,
+ &other
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .into_any()
+ .as_borrowed(),
+ )
}
fn rich_compare(&self, other: O, compare_op: CompareOp) -> PyResult>
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
fn inner<'py>(
any: &Bound<'py, PyAny>,
- other: Bound<'_, PyAny>,
+ other: &Bound<'_, PyAny>,
compare_op: CompareOp,
) -> PyResult> {
unsafe {
@@ -1015,7 +1095,15 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
}
let py = self.py();
- inner(self, other.to_object(py).into_bound(py), compare_op)
+ inner(
+ self,
+ &other
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .into_any()
+ .as_borrowed(),
+ compare_op,
+ )
}
fn neg(&self) -> PyResult> {
@@ -1048,7 +1136,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn lt(&self, other: O) -> PyResult
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
self.rich_compare(other, CompareOp::Lt)
.and_then(|any| any.is_truthy())
@@ -1056,7 +1145,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn le(&self, other: O) -> PyResult
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
self.rich_compare(other, CompareOp::Le)
.and_then(|any| any.is_truthy())
@@ -1064,7 +1154,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn eq(&self, other: O) -> PyResult
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
self.rich_compare(other, CompareOp::Eq)
.and_then(|any| any.is_truthy())
@@ -1072,7 +1163,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn ne(&self, other: O) -> PyResult
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
self.rich_compare(other, CompareOp::Ne)
.and_then(|any| any.is_truthy())
@@ -1080,7 +1172,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn gt(&self, other: O) -> PyResult
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
self.rich_compare(other, CompareOp::Gt)
.and_then(|any| any.is_truthy())
@@ -1088,7 +1181,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn ge(&self, other: O) -> PyResult
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
self.rich_compare(other, CompareOp::Ge)
.and_then(|any| any.is_truthy())
@@ -1110,11 +1204,12 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
/// Computes `divmod(self, other)`.
fn divmod(&self, other: O) -> PyResult>
where
- O: ToPyObject,
+ O: IntoPyObject<'py>,
+ O::Error: Into,
{
fn inner<'py>(
any: &Bound<'py, PyAny>,
- other: Bound<'_, PyAny>,
+ other: &Bound<'_, PyAny>,
) -> PyResult> {
unsafe {
ffi::PyNumber_Divmod(any.as_ptr(), other.as_ptr()).assume_owned_or_err(any.py())
@@ -1122,20 +1217,29 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
}
let py = self.py();
- inner(self, other.to_object(py).into_bound(py))
+ inner(
+ self,
+ &other
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .into_any()
+ .as_borrowed(),
+ )
}
/// Computes `self ** other % modulus` (`pow(self, other, modulus)`).
/// `py.None()` may be passed for the `modulus`.
fn pow(&self, other: O1, modulus: O2) -> PyResult>
where
- O1: ToPyObject,
- O2: ToPyObject,
+ O1: IntoPyObject<'py>,
+ O2: IntoPyObject<'py>,
+ O1::Error: Into,
+ O2::Error: Into,
{
fn inner<'py>(
any: &Bound<'py, PyAny>,
- other: Bound<'_, PyAny>,
- modulus: Bound<'_, PyAny>,
+ other: &Bound<'_, PyAny>,
+ modulus: &Bound<'_, PyAny>,
) -> PyResult> {
unsafe {
ffi::PyNumber_Power(any.as_ptr(), other.as_ptr(), modulus.as_ptr())
@@ -1146,8 +1250,16 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
let py = self.py();
inner(
self,
- other.to_object(py).into_bound(py),
- modulus.to_object(py).into_bound(py),
+ &other
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .into_any()
+ .as_borrowed(),
+ &modulus
+ .into_pyobject(py)
+ .map_err(Into::into)?
+ .into_any()
+ .as_borrowed(),
)
}
@@ -1155,14 +1267,14 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
unsafe { ffi::PyCallable_Check(self.as_ptr()) != 0 }
}
- fn call(
- &self,
- args: impl IntoPy>,
- kwargs: Option<&Bound<'_, PyDict>>,
- ) -> PyResult> {
+ fn call(&self, args: A, kwargs: Option<&Bound<'_, PyDict>>) -> PyResult>
+ where
+ A: IntoPyObject<'py, Target = PyTuple>,
+ A::Error: Into,
+ {
fn inner<'py>(
any: &Bound<'py, PyAny>,
- args: Bound<'_, PyTuple>,
+ args: &Bound<'_, PyTuple>,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult> {
unsafe {
@@ -1176,14 +1288,22 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
}
let py = self.py();
- inner(self, args.into_py(py).into_bound(py), kwargs)
+ inner(
+ self,
+ &args.into_pyobject(py).map_err(Into::into)?.as_borrowed(),
+ kwargs,
+ )
}
fn call0(&self) -> PyResult> {
unsafe { ffi::compat::PyObject_CallNoArgs(self.as_ptr()).assume_owned_or_err(self.py()) }
}
- fn call1(&self, args: impl IntoPy>) -> PyResult> {
+ fn call1(&self, args: A) -> PyResult>
+ where
+ A: IntoPyObject<'py, Target = PyTuple>,
+ A::Error: Into,
+ {
self.call(args, None)
}
@@ -1194,8 +1314,10 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult>
where
- N: IntoPy>,
- A: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ A: IntoPyObject<'py, Target = PyTuple>,
+ N::Error: Into,
+ A::Error: Into,
{
self.getattr(name)
.and_then(|method| method.call(args, kwargs))
@@ -1203,10 +1325,11 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn call_method0(&self, name: N) -> PyResult>
where
- N: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ N::Error: Into,
{
let py = self.py();
- let name = name.into_py(py).into_bound(py);
+ let name = name.into_pyobject(py).map_err(Into::into)?.into_bound();
unsafe {
ffi::compat::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr())
.assume_owned_or_err(py)
@@ -1215,8 +1338,10 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn call_method1(&self, name: N, args: A) -> PyResult>
where
- N: IntoPy>,
- A: IntoPy>,
+ N: IntoPyObject<'py, Target = PyString>,
+ A: IntoPyObject<'py, Target = PyTuple>,
+ N::Error: Into,
+ A::Error: Into,
{
self.call_method(name, args, None)
}
@@ -1242,11 +1367,12 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
fn get_item(&self, key: K) -> PyResult>
where
- K: ToPyObject,
+ K: IntoPyObject<'py>,
+ K::Error: Into