diff --git a/guide/src/conversions/traits.md b/guide/src/conversions/traits.md index 5e3da6ea1b0..c4e8f14866c 100644 --- a/guide/src/conversions/traits.md +++ b/guide/src/conversions/traits.md @@ -490,9 +490,11 @@ If the input is neither a string nor an integer, the error message will be: - the function signature must be `fn(&Bound) -> PyResult` where `T` is the Rust type of the argument. ### `IntoPyObject` -This trait defines the to-python conversion for a Rust type. All types in PyO3 implement this trait, +The ['IntoPyObject'] trait defines the to-python conversion for a Rust type. All types in PyO3 implement this trait, as does a `#[pyclass]` which doesn't use `extends`. +This trait defines a single method, `into_pyobject()`, which returns a [`Result`] with `Ok` and `Err` types depending on the input value. For convenience, there is a companion [`IntoPyObjectExt`] trait which adds methods such as `into_py_any()` which converts the `Ok` and `Err` types to commonly used types (in the case of `into_py_any()`, `Py` and `PyErr` respectively). + Occasionally you may choose to implement this for custom types which are mapped to Python types _without_ having a unique python type. @@ -510,7 +512,7 @@ into `PyTuple` with the fields in declaration order. // structs convert into `PyDict` with field names as keys #[derive(IntoPyObject)] -struct Struct { +struct Struct { count: usize, obj: Py, } @@ -532,11 +534,11 @@ forward the implementation to the inner type. // newtype tuple structs are implicitly `transparent` #[derive(IntoPyObject)] -struct TransparentTuple(PyObject); +struct TransparentTuple(PyObject); #[derive(IntoPyObject)] #[pyo3(transparent)] -struct TransparentStruct<'py> { +struct TransparentStruct<'py> { inner: Bound<'py, PyAny>, // `'py` lifetime will be used as the Python lifetime } ``` @@ -582,7 +584,7 @@ impl<'py> IntoPyObject<'py> for MyPyObjectWrapper { } } -// equivalent to former `ToPyObject` implementations +// equivalent to former `ToPyObject` implementations 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 @@ -594,38 +596,6 @@ impl<'a, 'py> IntoPyObject<'py> for &'a MyPyObjectWrapper { } ``` -### `IntoPy` - -
- -⚠️ Warning: API update in progress 🛠️ - -PyO3 0.23 has introduced `IntoPyObject` as the new trait for to-python conversions. While `#[pymethods]` and `#[pyfunction]` contain a compatibility layer to allow `IntoPy` as a return type, all Python API have been migrated to use `IntoPyObject`. To migrate implement `IntoPyObject` for your type. -
- - -This trait defines the to-python conversion for a Rust type. It is usually implemented as -`IntoPy`, which is the trait needed for returning a value from `#[pyfunction]` and -`#[pymethods]`. - -All types in PyO3 implement this trait, as does a `#[pyclass]` which doesn't use `extends`. - -Occasionally you may choose to implement this for custom types which are mapped to Python types -_without_ having a unique python type. - -```rust -use pyo3::prelude::*; -# #[allow(dead_code)] -struct MyPyObjectWrapper(PyObject); - -#[allow(deprecated)] -impl IntoPy for MyPyObjectWrapper { - fn into_py(self, py: Python<'_>) -> PyObject { - self.0 - } -} -``` - #### `BoundObject` for conversions that may be `Bound` or `Borrowed` `IntoPyObject::into_py_object` returns either `Bound` or `Borrowed` depending on the implementation for a concrete type. For example, the `IntoPyObject` implementation for `u32` produces a `Bound<'py, PyInt>` and the `bool` implementation produces a `Borrowed<'py, 'py, PyBool>`: @@ -672,6 +642,8 @@ where the_vec.iter() .map(|x| { Ok( + // Note: the below is equivalent to `x.into_py_any()` + // from the `IntoPyObjectExt` trait x.into_pyobject(py) .map_err(Into::into)? .into_any() @@ -693,6 +665,38 @@ let vec_of_pyobjs: Vec> = Python::with_gil(|py| { In the example above we used `BoundObject::into_any` and `BoundObject::unbind` to manipulate the python types and smart pointers into the result type we wanted to produce from the function. +### `IntoPy` + +
+ +⚠️ Warning: API update in progress 🛠️ + +PyO3 0.23 has introduced `IntoPyObject` as the new trait for to-python conversions. While `#[pymethods]` and `#[pyfunction]` contain a compatibility layer to allow `IntoPy` as a return type, all Python API have been migrated to use `IntoPyObject`. To migrate implement `IntoPyObject` for your type. +
+ + +This trait defines the to-python conversion for a Rust type. It is usually implemented as +`IntoPy`, which is the trait needed for returning a value from `#[pyfunction]` and +`#[pymethods]`. + +All types in PyO3 implement this trait, as does a `#[pyclass]` which doesn't use `extends`. + +Occasionally you may choose to implement this for custom types which are mapped to Python types +_without_ having a unique python type. + +```rust +use pyo3::prelude::*; +# #[allow(dead_code)] +struct MyPyObjectWrapper(PyObject); + +#[allow(deprecated)] +impl IntoPy for MyPyObjectWrapper { + fn into_py(self, py: Python<'_>) -> PyObject { + self.0 + } +} +``` + ### The `ToPyObject` trait
@@ -710,8 +714,12 @@ same purpose, except that it consumes `self`. [`IntoPy`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.IntoPy.html [`FromPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.FromPyObject.html [`ToPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.ToPyObject.html +[`IntoPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.IntoPyObject.html +[`IntoPyObjectExt`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.IntoPyObjectExt.html [`PyObject`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyObject.html [`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html [`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRefMut.html [`BoundObject`]: {{#PYO3_DOCS_URL}}/pyo3/instance/trait.BoundObject.html + +[`Result`]: https://doc.rust-lang.org/stable/std/result/enum.Result.html diff --git a/newsfragments/4708.added.md b/newsfragments/4708.added.md new file mode 100644 index 00000000000..c8c91d16221 --- /dev/null +++ b/newsfragments/4708.added.md @@ -0,0 +1 @@ +Add `IntoPyObjectExt` trait. diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index a83ba880271..87d02c6f878 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -1367,10 +1367,7 @@ fn impl_complex_enum_tuple_variant_getitem( .map(|i| { let field_access = format_ident!("_{}", i); quote! { #i => - #pyo3_path::IntoPyObject::into_pyobject(#variant_cls::#field_access(slf)?, py) - .map(#pyo3_path::BoundObject::into_any) - .map(#pyo3_path::BoundObject::unbind) - .map_err(::std::convert::Into::into) + #pyo3_path::IntoPyObjectExt::into_py_any(#variant_cls::#field_access(slf)?, py) } }) .collect(); @@ -1852,16 +1849,10 @@ fn pyclass_richcmp_arms( .map(|span| { quote_spanned! { span => #pyo3_path::pyclass::CompareOp::Eq => { - #pyo3_path::IntoPyObject::into_pyobject(self_val == other, py) - .map(#pyo3_path::BoundObject::into_any) - .map(#pyo3_path::BoundObject::unbind) - .map_err(::std::convert::Into::into) + #pyo3_path::IntoPyObjectExt::into_py_any(self_val == other, py) }, #pyo3_path::pyclass::CompareOp::Ne => { - #pyo3_path::IntoPyObject::into_pyobject(self_val != other, py) - .map(#pyo3_path::BoundObject::into_any) - .map(#pyo3_path::BoundObject::unbind) - .map_err(::std::convert::Into::into) + #pyo3_path::IntoPyObjectExt::into_py_any(self_val != other, py) }, } }) @@ -1876,28 +1867,16 @@ fn pyclass_richcmp_arms( .map(|ord| { quote_spanned! { ord.span() => #pyo3_path::pyclass::CompareOp::Gt => { - #pyo3_path::IntoPyObject::into_pyobject(self_val > other, py) - .map(#pyo3_path::BoundObject::into_any) - .map(#pyo3_path::BoundObject::unbind) - .map_err(::std::convert::Into::into) + #pyo3_path::IntoPyObjectExt::into_py_any(self_val > other, py) }, #pyo3_path::pyclass::CompareOp::Lt => { - #pyo3_path::IntoPyObject::into_pyobject(self_val < other, py) - .map(#pyo3_path::BoundObject::into_any) - .map(#pyo3_path::BoundObject::unbind) - .map_err(::std::convert::Into::into) + #pyo3_path::IntoPyObjectExt::into_py_any(self_val < other, py) }, #pyo3_path::pyclass::CompareOp::Le => { - #pyo3_path::IntoPyObject::into_pyobject(self_val <= other, py) - .map(#pyo3_path::BoundObject::into_any) - .map(#pyo3_path::BoundObject::unbind) - .map_err(::std::convert::Into::into) + #pyo3_path::IntoPyObjectExt::into_py_any(self_val <= other, py) }, #pyo3_path::pyclass::CompareOp::Ge => { - #pyo3_path::IntoPyObject::into_pyobject(self_val >= other, py) - .map(#pyo3_path::BoundObject::into_any) - .map(#pyo3_path::BoundObject::unbind) - .map_err(::std::convert::Into::into) + #pyo3_path::IntoPyObjectExt::into_py_any(self_val >= other, py) }, } }) diff --git a/pyo3-macros-backend/src/pyimpl.rs b/pyo3-macros-backend/src/pyimpl.rs index 614db3c5459..72f06721ec4 100644 --- a/pyo3-macros-backend/src/pyimpl.rs +++ b/pyo3-macros-backend/src/pyimpl.rs @@ -213,10 +213,7 @@ pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec, ctx: &Ctx) -> MethodAndMe let associated_method = quote! { fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> { - #pyo3_path::IntoPyObject::into_pyobject(#cls::#member, py) - .map(#pyo3_path::BoundObject::into_any) - .map(#pyo3_path::BoundObject::unbind) - .map_err(::std::convert::Into::into) + #pyo3_path::IntoPyObjectExt::into_py_any(#cls::#member, py) } }; diff --git a/src/conversion.rs b/src/conversion.rs index 5986aefd6e3..82ad4d84977 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -178,8 +178,23 @@ pub trait IntoPy: Sized { /// Defines a conversion from a Rust type to a Python object, which may fail. /// +/// This trait has `#[derive(IntoPyObject)]` to automatically implement it for simple types and +/// `#[derive(IntoPyObjectRef)]` to implement the same for references. +/// /// It functions similarly to std's [`TryInto`] trait, but requires a [GIL token](Python) /// as an argument. +/// +/// The [`into_pyobject`][IntoPyObject::into_pyobject] method is designed for maximum flexibility and efficiency; it +/// - allows for a concrete Python type to be returned (the [`Target`][IntoPyObject::Target] associated type) +/// - allows for the smart pointer containing the Python object to be either `Bound<'py, Self::Target>` or `Borrowed<'a, 'py, Self::Target>` +/// to avoid unnecessary reference counting overhead +/// - allows for a custom error type to be returned in the event of a conversion error to avoid +/// unnecessarily creating a Python exception +/// +/// # See also +/// +/// - The [`IntoPyObjectExt`] trait, which provides convenience methods for common usages of +/// `IntoPyObject` which erase type information and convert errors to `PyErr`. #[cfg_attr( diagnostic_namespace, diagnostic::on_unimplemented( @@ -227,12 +242,7 @@ pub trait IntoPyObject<'py>: Sized { I: IntoIterator + AsRef<[Self]>, I::IntoIter: ExactSizeIterator, { - let mut iter = iter.into_iter().map(|e| { - e.into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into) - }); + let mut iter = iter.into_iter().map(|e| e.into_bound_py_any(py)); let list = crate::types::list::try_new_from_iter(py, &mut iter); list.map(Bound::into_any) } @@ -250,12 +260,7 @@ pub trait IntoPyObject<'py>: Sized { I: IntoIterator + AsRef<[::BaseType]>, I::IntoIter: ExactSizeIterator, { - let mut iter = iter.into_iter().map(|e| { - e.into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into) - }); + let mut iter = iter.into_iter().map(|e| e.into_bound_py_any(py)); let list = crate::types::list::try_new_from_iter(py, &mut iter); list.map(Bound::into_any) } @@ -347,6 +352,54 @@ where } } +mod into_pyobject_ext { + pub trait Sealed {} + impl<'py, T> Sealed for T where T: super::IntoPyObject<'py> {} +} + +/// Convenience methods for common usages of [`IntoPyObject`]. Every type that implements +/// [`IntoPyObject`] also implements this trait. +/// +/// These methods: +/// - Drop type information from the output, returning a `PyAny` object. +/// - Always convert the `Error` type to `PyErr`, which may incur a performance penalty but it +/// more convenient in contexts where the `?` operator would produce a `PyErr` anyway. +pub trait IntoPyObjectExt<'py>: IntoPyObject<'py> + into_pyobject_ext::Sealed { + /// Converts `self` into an owned Python object, dropping type information. + #[inline] + fn into_bound_py_any(self, py: Python<'py>) -> PyResult> { + match self.into_pyobject(py) { + Ok(obj) => Ok(obj.into_any().into_bound()), + Err(err) => Err(err.into()), + } + } + + /// Converts `self` into an owned Python object, dropping type information and unbinding it + /// from the `'py` lifetime. + #[inline] + fn into_py_any(self, py: Python<'py>) -> PyResult> { + match self.into_pyobject(py) { + Ok(obj) => Ok(obj.into_any().unbind()), + Err(err) => Err(err.into()), + } + } + + /// Converts `self` into a Python object. + /// + /// This is equivalent to calling [`into_pyobject`][IntoPyObject::into_pyobject] followed + /// with `.map_err(Into::into)` to convert the error type to [`PyErr`]. This is helpful + /// for generic code which wants to make use of the `?` operator. + #[inline] + fn into_pyobject_or_pyerr(self, py: Python<'py>) -> PyResult { + match self.into_pyobject(py) { + Ok(obj) => Ok(obj), + Err(err) => Err(err.into()), + } + } +} + +impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} + /// Extract a type from a Python object. /// /// diff --git a/src/conversions/either.rs b/src/conversions/either.rs index 1fdcd22d7e2..a514b1fde8d 100644 --- a/src/conversions/either.rs +++ b/src/conversions/either.rs @@ -47,8 +47,8 @@ #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - conversion::IntoPyObject, exceptions::PyTypeError, types::any::PyAnyMethods, Bound, - BoundObject, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python, + exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPyObject, + IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -82,16 +82,8 @@ where fn into_pyobject(self, py: Python<'py>) -> Result { match self { - Either::Left(l) => l - .into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into), - Either::Right(r) => r - .into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into), + Either::Left(l) => l.into_bound_py_any(py), + Either::Right(r) => r.into_bound_py_any(py), } } } @@ -108,16 +100,8 @@ where fn into_pyobject(self, py: Python<'py>) -> Result { match self { - Either::Left(l) => l - .into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into), - Either::Right(r) => r - .into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into), + Either::Left(l) => l.into_bound_py_any(py), + Either::Right(r) => r.into_bound_py_any(py), } } } diff --git a/src/coroutine.rs b/src/coroutine.rs index aa4335e9d0b..671defb1770 100644 --- a/src/coroutine.rs +++ b/src/coroutine.rs @@ -15,7 +15,7 @@ use crate::{ exceptions::{PyAttributeError, PyRuntimeError, PyStopIteration}, panic::PanicException, types::{string::PyStringMethods, PyIterator, PyString}, - Bound, BoundObject, IntoPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python, + Bound, IntoPyObject, IntoPyObjectExt, Py, PyAny, PyErr, PyObject, PyResult, Python, }; pub(crate) mod cancel; @@ -60,10 +60,7 @@ impl Coroutine { let wrap = async move { let obj = future.await.map_err(Into::into)?; // SAFETY: GIL is acquired when future is polled (see `Coroutine::poll`) - obj.into_pyobject(unsafe { Python::assume_gil_acquired() }) - .map(BoundObject::into_any) - .map(BoundObject::unbind) - .map_err(Into::into) + obj.into_py_any(unsafe { Python::assume_gil_acquired() }) }; Self { name: name.map(Bound::unbind), diff --git a/src/impl_/pyclass.rs b/src/impl_/pyclass.rs index 8e7e8cf844f..7bb61442ec5 100644 --- a/src/impl_/pyclass.rs +++ b/src/impl_/pyclass.rs @@ -1,5 +1,4 @@ use crate::{ - conversion::IntoPyObject, exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError, PyValueError}, ffi, impl_::{ @@ -10,7 +9,8 @@ use crate::{ }, pycell::PyBorrowError, types::{any::PyAnyMethods, PyBool}, - Borrowed, BoundObject, Py, PyAny, PyClass, PyErr, PyRef, PyResult, PyTypeInfo, Python, + Borrowed, BoundObject, IntoPyObject, IntoPyObjectExt, Py, PyAny, PyClass, PyErr, PyRef, + PyResult, PyTypeInfo, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -1532,10 +1532,7 @@ impl ConvertField, { - obj.into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::unbind) - .map_err(Into::into) + obj.into_py_any(py) } } @@ -1545,11 +1542,7 @@ impl ConvertField, { - obj.clone() - .into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::unbind) - .map_err(Into::into) + obj.clone().into_py_any(py) } } diff --git a/src/impl_/wrap.rs b/src/impl_/wrap.rs index 9381828245a..7b214219408 100644 --- a/src/impl_/wrap.rs +++ b/src/impl_/wrap.rs @@ -2,9 +2,7 @@ use std::{convert::Infallible, marker::PhantomData, ops::Deref}; #[allow(deprecated)] use crate::IntoPy; -use crate::{ - conversion::IntoPyObject, ffi, types::PyNone, Bound, BoundObject, PyObject, PyResult, Python, -}; +use crate::{ffi, types::PyNone, Bound, IntoPyObject, IntoPyObjectExt, PyObject, PyResult, Python}; /// Used to wrap values in `Option` for default arguments. pub trait SomeWrap { @@ -97,9 +95,7 @@ impl<'py, T: IntoPyObject<'py>, E> IntoPyObjectConverter> { where T: IntoPyObject<'py>, { - obj.and_then(|obj| obj.into_pyobject(py).map_err(Into::into)) - .map(BoundObject::into_any) - .map(BoundObject::unbind) + obj.and_then(|obj| obj.into_py_any(py)) } #[inline] @@ -107,8 +103,7 @@ impl<'py, T: IntoPyObject<'py>, E> IntoPyObjectConverter> { where T: IntoPyObject<'py>, { - obj.and_then(|obj| obj.into_pyobject(py).map_err(Into::into)) - .map(BoundObject::into_bound) + obj.and_then(|obj| obj.into_bound_py_any(py)) .map(Bound::into_ptr) } } diff --git a/src/instance.rs b/src/instance.rs index 14d2d11b5d7..840416116f3 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1466,7 +1466,7 @@ impl Py { /// # Example: `intern!`ing the attribute name /// /// ``` - /// # use pyo3::{intern, pyfunction, types::PyModule, IntoPyObject, PyObject, Python, PyResult}; + /// # use pyo3::{intern, pyfunction, types::PyModule, IntoPyObjectExt, PyObject, Python, PyResult}; /// # /// #[pyfunction] /// fn set_answer(ob: PyObject, py: Python<'_>) -> PyResult<()> { @@ -1474,7 +1474,7 @@ impl Py { /// } /// # /// # Python::with_gil(|py| { - /// # let ob = PyModule::new(py, "empty").unwrap().into_pyobject(py).unwrap().into_any().unbind(); + /// # let ob = PyModule::new(py, "empty").unwrap().into_py_any(py).unwrap(); /// # set_answer(ob, py).unwrap(); /// # }); /// ``` diff --git a/src/lib.rs b/src/lib.rs index 265824adab1..46a2fd53d32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -334,7 +334,7 @@ #![doc = concat!("[Features chapter of the guide]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/features.html#features-reference \"Features Reference - PyO3 user guide\"")] //! [`Ungil`]: crate::marker::Ungil pub use crate::class::*; -pub use crate::conversion::{AsPyPointer, FromPyObject, IntoPyObject}; +pub use crate::conversion::{AsPyPointer, FromPyObject, IntoPyObject, IntoPyObjectExt}; #[allow(deprecated)] pub use crate::conversion::{IntoPy, ToPyObject}; pub use crate::err::{DowncastError, DowncastIntoError, PyErr, PyErrArguments, PyResult, ToPyErr}; diff --git a/src/types/any.rs b/src/types/any.rs index e620cf6d137..d060c187631 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -11,7 +11,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, Borrowed, BoundObject, Python}; +use crate::{err, ffi, Borrowed, BoundObject, IntoPyObjectExt, Python}; use std::cell::UnsafeCell; use std::cmp::Ordering; use std::os::raw::c_int; @@ -922,11 +922,7 @@ macro_rules! implement_binop { let py = self.py(); inner( self, - other - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } }; @@ -996,15 +992,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - attr_name - .into_pyobject(py) - .map_err(Into::into)? - .as_borrowed(), - value - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + attr_name.into_pyobject_or_pyerr(py)?.as_borrowed(), + value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -1019,13 +1008,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { } let py = self.py(); - inner( - self, - attr_name - .into_pyobject(py) - .map_err(Into::into)? - .as_borrowed(), - ) + inner(self, attr_name.into_pyobject_or_pyerr(py)?.as_borrowed()) } fn compare(&self, other: O) -> PyResult @@ -1057,11 +1040,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - other - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -1083,11 +1062,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - other - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), compare_op, ) } @@ -1198,11 +1173,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - other - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -1227,16 +1198,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - other - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), - modulus - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + other.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), + modulus.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -1264,11 +1227,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { } let py = self.py(); - inner( - self, - args.into_pyobject(py).map_err(Into::into)?.as_borrowed(), - kwargs, - ) + inner(self, args.into_pyobject_or_pyerr(py)?.as_borrowed(), kwargs) } #[inline] @@ -1304,7 +1263,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { N: IntoPyObject<'py, Target = PyString>, { let py = self.py(); - let name = name.into_pyobject(py).map_err(Into::into)?.into_bound(); + let name = name.into_pyobject_or_pyerr(py)?.into_bound(); unsafe { ffi::compat::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr()) .assume_owned_or_err(py) @@ -1354,10 +1313,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -1379,15 +1335,8 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), - value - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), + value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -1404,10 +1353,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -1574,11 +1520,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> { let py = self.py(); inner( self, - value - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } diff --git a/src/types/dict.rs b/src/types/dict.rs index 129f32dc9e1..b3c8e37962b 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -4,7 +4,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::{Borrowed, Bound}; use crate::py_result_ext::PyResultExt; use crate::types::{PyAny, PyAnyMethods, PyList, PyMapping}; -use crate::{ffi, BoundObject, IntoPyObject, Python}; +use crate::{ffi, BoundObject, IntoPyObject, IntoPyObjectExt, Python}; /// Represents a Python `dict`. /// @@ -239,7 +239,7 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { where K: IntoPyObject<'py>, { - fn inner(dict: &Bound<'_, PyDict>, key: &Bound<'_, PyAny>) -> PyResult { + fn inner(dict: &Bound<'_, PyDict>, key: Borrowed<'_, '_, PyAny>) -> PyResult { match unsafe { ffi::PyDict_Contains(dict.as_ptr(), key.as_ptr()) } { 1 => Ok(true), 0 => Ok(false), @@ -250,10 +250,7 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { let py = self.py(); inner( self, - &key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -263,7 +260,7 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { { fn inner<'py>( dict: &Bound<'py, PyDict>, - key: &Bound<'_, PyAny>, + key: Borrowed<'_, '_, PyAny>, ) -> PyResult>> { let py = dict.py(); let mut result: *mut ffi::PyObject = std::ptr::null_mut(); @@ -283,10 +280,7 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { let py = self.py(); inner( self, - &key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -297,8 +291,8 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { { fn inner( dict: &Bound<'_, PyDict>, - key: &Bound<'_, PyAny>, - value: &Bound<'_, PyAny>, + key: Borrowed<'_, '_, PyAny>, + value: Borrowed<'_, '_, PyAny>, ) -> PyResult<()> { err::error_on_minusone(dict.py(), unsafe { ffi::PyDict_SetItem(dict.as_ptr(), key.as_ptr(), value.as_ptr()) @@ -308,15 +302,8 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { let py = self.py(); inner( self, - &key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), - &value - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), + value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -324,7 +311,7 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { where K: IntoPyObject<'py>, { - fn inner(dict: &Bound<'_, PyDict>, key: &Bound<'_, PyAny>) -> PyResult<()> { + fn inner(dict: &Bound<'_, PyDict>, key: Borrowed<'_, '_, PyAny>) -> PyResult<()> { err::error_on_minusone(dict.py(), unsafe { ffi::PyDict_DelItem(dict.as_ptr(), key.as_ptr()) }) @@ -333,10 +320,7 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { let py = self.py(); inner( self, - &key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } diff --git a/src/types/frozenset.rs b/src/types/frozenset.rs index 3c5a62a01d8..954c49b5902 100644 --- a/src/types/frozenset.rs +++ b/src/types/frozenset.rs @@ -1,4 +1,3 @@ -use crate::conversion::IntoPyObject; use crate::types::PyIterator; use crate::{ err::{self, PyErr, PyResult}, @@ -8,7 +7,7 @@ use crate::{ types::any::PyAnyMethods, Bound, PyAny, Python, }; -use crate::{Borrowed, BoundObject}; +use crate::{Borrowed, BoundObject, IntoPyObject, IntoPyObjectExt}; use std::ptr; /// Allows building a Python `frozenset` one item at a time @@ -181,10 +180,7 @@ impl<'py> PyFrozenSetMethods<'py> for Bound<'py, PyFrozenSet> { let py = self.py(); inner( self, - key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -266,7 +262,7 @@ where let ptr = set.as_ptr(); for e in elements { - let obj = e.into_pyobject(py).map_err(Into::into)?; + let obj = e.into_pyobject_or_pyerr(py)?; err::error_on_minusone(py, unsafe { ffi::PySet_Add(ptr, obj.as_ptr()) })?; } diff --git a/src/types/list.rs b/src/types/list.rs index f00c194739f..af2b557cba9 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -5,7 +5,9 @@ use crate::ffi::{self, Py_ssize_t}; use crate::ffi_ptr_ext::FfiPtrExt; use crate::internal_tricks::get_ssize_index; use crate::types::{PySequence, PyTuple}; -use crate::{Borrowed, Bound, BoundObject, IntoPyObject, PyAny, PyErr, PyObject, Python}; +use crate::{ + Borrowed, Bound, BoundObject, IntoPyObject, IntoPyObjectExt, PyAny, PyErr, PyObject, Python, +}; use crate::types::any::PyAnyMethods; use crate::types::sequence::PySequenceMethods; @@ -104,12 +106,7 @@ impl PyList { T: IntoPyObject<'py>, U: ExactSizeIterator, { - let iter = elements.into_iter().map(|e| { - e.into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into) - }); + let iter = elements.into_iter().map(|e| e.into_bound_py_any(py)); try_new_from_iter(py, iter) } @@ -339,14 +336,7 @@ impl<'py> PyListMethods<'py> for Bound<'py, PyList> { } let py = self.py(); - inner( - self, - index, - item.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .into_bound(), - ) + inner(self, index, item.into_bound_py_any(py)?) } /// Deletes the `index`th element of self. @@ -394,10 +384,7 @@ impl<'py> PyListMethods<'py> for Bound<'py, PyList> { let py = self.py(); inner( self, - item.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + item.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -422,10 +409,7 @@ impl<'py> PyListMethods<'py> for Bound<'py, PyList> { inner( self, index, - item.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + item.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } diff --git a/src/types/module.rs b/src/types/module.rs index d3e59c85198..fd7299cb084 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -1,4 +1,3 @@ -use crate::conversion::IntoPyObject; use crate::err::{PyErr, PyResult}; use crate::ffi_ptr_ext::FfiPtrExt; use crate::impl_::callback::IntoPyCallbackOutput; @@ -7,7 +6,10 @@ use crate::pyclass::PyClass; use crate::types::{ any::PyAnyMethods, list::PyListMethods, PyAny, PyCFunction, PyDict, PyList, PyString, }; -use crate::{exceptions, ffi, Borrowed, Bound, BoundObject, Py, PyObject, Python}; +use crate::{ + exceptions, ffi, Borrowed, Bound, BoundObject, IntoPyObject, IntoPyObjectExt, Py, PyObject, + Python, +}; use std::ffi::{CStr, CString}; #[cfg(all(not(Py_LIMITED_API), Py_GIL_DISABLED))] use std::os::raw::c_int; @@ -89,7 +91,7 @@ impl PyModule { where N: IntoPyObject<'py, Target = PyString>, { - let name = name.into_pyobject(py).map_err(Into::into)?; + let name = name.into_pyobject_or_pyerr(py)?; unsafe { ffi::PyImport_Import(name.as_ptr()) .assume_owned_or_err(py) @@ -508,12 +510,8 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> { let py = self.py(); inner( self, - name.into_pyobject(py).map_err(Into::into)?.as_borrowed(), - value - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + name.into_pyobject_or_pyerr(py)?.as_borrowed(), + value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } diff --git a/src/types/sequence.rs b/src/types/sequence.rs index 0801704f700..bc2643dcf8e 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -9,7 +9,10 @@ use crate::py_result_ext::PyResultExt; use crate::sync::GILOnceCell; use crate::type_object::PyTypeInfo; use crate::types::{any::PyAnyMethods, PyAny, PyList, PyString, PyTuple, PyType}; -use crate::{ffi, Borrowed, BoundObject, FromPyObject, IntoPyObject, Py, PyTypeCheck, Python}; +use crate::{ + ffi, Borrowed, BoundObject, FromPyObject, IntoPyObject, IntoPyObjectExt, Py, PyTypeCheck, + Python, +}; /// Represents a reference to a Python object supporting the sequence protocol. /// @@ -221,10 +224,7 @@ impl<'py> PySequenceMethods<'py> for Bound<'py, PySequence> { inner( self, i, - item.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + item.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -269,11 +269,7 @@ impl<'py> PySequenceMethods<'py> for Bound<'py, PySequence> { let py = self.py(); inner( self, - value - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -294,11 +290,7 @@ impl<'py> PySequenceMethods<'py> for Bound<'py, PySequence> { let py = self.py(); inner( self, - value - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -316,11 +308,7 @@ impl<'py> PySequenceMethods<'py> for Bound<'py, PySequence> { let py = self.py(); inner( self, - value - .into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } diff --git a/src/types/set.rs b/src/types/set.rs index e7c24f5b1ea..d5e39ebc83d 100644 --- a/src/types/set.rs +++ b/src/types/set.rs @@ -1,4 +1,3 @@ -use crate::conversion::IntoPyObject; use crate::types::PyIterator; #[allow(deprecated)] use crate::ToPyObject; @@ -9,7 +8,7 @@ use crate::{ py_result_ext::PyResultExt, types::any::PyAnyMethods, }; -use crate::{ffi, Borrowed, BoundObject, PyAny, Python}; +use crate::{ffi, Borrowed, BoundObject, IntoPyObject, IntoPyObjectExt, PyAny, Python}; use std::ptr; /// Represents a Python `set`. @@ -161,10 +160,7 @@ impl<'py> PySetMethods<'py> for Bound<'py, PySet> { let py = self.py(); inner( self, - key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -183,10 +179,7 @@ impl<'py> PySetMethods<'py> for Bound<'py, PySet> { let py = self.py(); inner( self, - key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -203,10 +196,7 @@ impl<'py> PySetMethods<'py> for Bound<'py, PySet> { let py = self.py(); inner( self, - key.into_pyobject(py) - .map_err(Into::into)? - .into_any() - .as_borrowed(), + key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(), ) } @@ -316,7 +306,7 @@ where let ptr = set.as_ptr(); elements.into_iter().try_for_each(|element| { - let obj = element.into_pyobject(py).map_err(Into::into)?; + let obj = element.into_pyobject_or_pyerr(py)?; err::error_on_minusone(py, unsafe { ffi::PySet_Add(ptr, obj.as_ptr()) }) })?; diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 3a1f92815c2..216a376d833 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -1,6 +1,5 @@ use std::iter::FusedIterator; -use crate::conversion::IntoPyObject; use crate::ffi::{self, Py_ssize_t}; use crate::ffi_ptr_ext::FfiPtrExt; #[cfg(feature = "experimental-inspect")] @@ -9,7 +8,8 @@ use crate::instance::Borrowed; use crate::internal_tricks::get_ssize_index; use crate::types::{any::PyAnyMethods, sequence::PySequenceMethods, PyList, PySequence}; use crate::{ - exceptions, Bound, BoundObject, FromPyObject, Py, PyAny, PyErr, PyObject, PyResult, Python, + exceptions, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, Py, PyAny, PyErr, PyObject, + PyResult, Python, }; #[allow(deprecated)] use crate::{IntoPy, ToPyObject}; @@ -99,12 +99,7 @@ impl PyTuple { T: IntoPyObject<'py>, U: ExactSizeIterator, { - let elements = elements.into_iter().map(|e| { - e.into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into) - }); + let elements = elements.into_iter().map(|e| e.into_bound_py_any(py)); try_new_from_iter(py, elements) } @@ -523,14 +518,14 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+ #[allow(deprecated)] impl <$($T: ToPyObject),+> ToPyObject for ($($T,)+) { fn to_object(&self, py: Python<'_>) -> PyObject { - array_into_tuple(py, [$(self.$n.to_object(py)),+]).into() + array_into_tuple(py, [$(self.$n.to_object(py).into_bound(py)),+]).into() } } #[allow(deprecated)] impl <$($T: IntoPy),+> IntoPy for ($($T,)+) { fn into_py(self, py: Python<'_>) -> PyObject { - array_into_tuple(py, [$(self.$n.into_py(py)),+]).into() + array_into_tuple(py, [$(self.$n.into_py(py).into_bound(py)),+]).into() } } @@ -543,7 +538,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+ type Error = PyErr; fn into_pyobject(self, py: Python<'py>) -> Result { - Ok(array_into_tuple(py, [$(self.$n.into_pyobject(py).map_err(Into::into)?.into_any().unbind()),+]).into_bound(py)) + Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+])) } #[cfg(feature = "experimental-inspect")] @@ -562,7 +557,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+ type Error = PyErr; fn into_pyobject(self, py: Python<'py>) -> Result { - Ok(array_into_tuple(py, [$(self.$n.into_pyobject(py).map_err(Into::into)?.into_any().unbind()),+]).into_bound(py)) + Ok(array_into_tuple(py, [$(self.$n.into_bound_py_any(py)?),+])) } #[cfg(feature = "experimental-inspect")] @@ -574,7 +569,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+ #[allow(deprecated)] impl <$($T: IntoPy),+> IntoPy> for ($($T,)+) { fn into_py(self, py: Python<'_>) -> Py { - array_into_tuple(py, [$(self.$n.into_py(py)),+]) + array_into_tuple(py, [$(self.$n.into_py(py).into_bound(py)),+]).unbind() } } @@ -600,10 +595,13 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+ } }); -fn array_into_tuple(py: Python<'_>, array: [PyObject; N]) -> Py { +fn array_into_tuple<'py, const N: usize>( + py: Python<'py>, + array: [Bound<'py, PyAny>; N], +) -> Bound<'py, PyTuple> { unsafe { let ptr = ffi::PyTuple_New(N.try_into().expect("0 < N <= 12")); - let tup = Py::from_owned_ptr(py, ptr); + let tup = ptr.assume_owned(py).downcast_into_unchecked(); for (index, obj) in array.into_iter().enumerate() { #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] ffi::PyTuple_SET_ITEM(ptr, index as ffi::Py_ssize_t, obj.into_ptr()); diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 6b20a29b8c2..5334f0341f1 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -3,7 +3,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::PyTypeCheck; use crate::types::any::PyAny; -use crate::{ffi, Bound, BoundObject, IntoPyObject}; +use crate::{ffi, Borrowed, Bound, BoundObject, IntoPyObject, IntoPyObjectExt}; use super::PyWeakrefMethods; @@ -152,7 +152,7 @@ impl PyWeakrefProxy { { fn inner<'py>( object: &Bound<'py, PyAny>, - callback: Bound<'py, PyAny>, + callback: Borrowed<'_, 'py, PyAny>, ) -> PyResult> { unsafe { Bound::from_owned_ptr_or_err( @@ -167,10 +167,9 @@ impl PyWeakrefProxy { inner( object, callback - .into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into)?, + .into_pyobject_or_pyerr(py)? + .into_any() + .as_borrowed(), ) } diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index dc7ea4a272a..edabb6da935 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -2,7 +2,7 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::types::any::PyAny; -use crate::{ffi, Bound, BoundObject, IntoPyObject}; +use crate::{ffi, Borrowed, Bound, BoundObject, IntoPyObject, IntoPyObjectExt}; #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] use crate::type_object::PyTypeCheck; @@ -161,7 +161,7 @@ impl PyWeakrefReference { { fn inner<'py>( object: &Bound<'py, PyAny>, - callback: Bound<'py, PyAny>, + callback: Borrowed<'_, 'py, PyAny>, ) -> PyResult> { unsafe { Bound::from_owned_ptr_or_err( @@ -176,10 +176,9 @@ impl PyWeakrefReference { inner( object, callback - .into_pyobject(py) - .map(BoundObject::into_any) - .map(BoundObject::into_bound) - .map_err(Into::into)?, + .into_pyobject_or_pyerr(py)? + .into_any() + .as_borrowed(), ) }