From 1376b8a1480ab1008315d019f5362fb367109451 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Mon, 29 Jul 2024 19:27:00 +0200 Subject: [PATCH 01/11] replace `FromPyObject` with `FromPyObjectBound` --- pyo3-benches/benches/bench_dict.rs | 15 +-- pyo3-macros-backend/src/frompyobject.rs | 9 +- src/buffer.rs | 8 +- src/conversion.rs | 130 +++--------------------- src/conversions/bigdecimal.rs | 6 +- src/conversions/bytes.rs | 11 +- src/conversions/chrono.rs | 51 ++++++---- src/conversions/chrono_tz.rs | 7 +- src/conversions/either.rs | 12 +-- src/conversions/hashbrown.rs | 18 ++-- src/conversions/indexmap.rs | 12 +-- src/conversions/jiff.rs | 48 ++++----- src/conversions/num_bigint.rs | 28 ++--- src/conversions/num_complex.rs | 10 +- src/conversions/num_rational.rs | 6 +- src/conversions/ordered_float.rs | 9 +- src/conversions/rust_decimal.rs | 6 +- src/conversions/smallvec.rs | 17 ++-- src/conversions/std/array.rs | 15 ++- src/conversions/std/cell.rs | 9 +- src/conversions/std/ipaddr.rs | 7 +- src/conversions/std/map.rs | 22 ++-- src/conversions/std/num.rs | 38 ++++--- src/conversions/std/option.rs | 11 +- src/conversions/std/osstr.rs | 10 +- src/conversions/std/path.rs | 7 +- src/conversions/std/set.rs | 15 ++- src/conversions/std/slice.rs | 8 +- src/conversions/std/string.rs | 22 ++-- src/conversions/std/time.rs | 8 +- src/conversions/time.rs | 36 +++---- src/conversions/uuid.rs | 6 +- src/impl_/extract_argument.rs | 7 +- src/impl_/frompyobject.rs | 12 +-- src/inspect/types.rs | 2 +- src/instance.rs | 22 ++-- src/pybacked.rs | 12 +-- src/pyclass/guard.rs | 11 +- src/types/any.rs | 11 +- src/types/boolobject.rs | 10 +- src/types/float.rs | 11 +- src/types/sequence.rs | 12 +-- src/types/tuple.rs | 8 +- tests/ui/invalid_cancel_handle.stderr | 12 +-- 44 files changed, 325 insertions(+), 422 deletions(-) diff --git a/pyo3-benches/benches/bench_dict.rs b/pyo3-benches/benches/bench_dict.rs index 7959a60d7ca..8ea38b7f67e 100644 --- a/pyo3-benches/benches/bench_dict.rs +++ b/pyo3-benches/benches/bench_dict.rs @@ -62,8 +62,9 @@ fn extract_hashmap(b: &mut Bencher<'_>) { let dict = (0..LEN as u64) .map(|i| (i, i * 2)) .into_py_dict(py) - .unwrap(); - b.iter(|| HashMap::::extract_bound(&dict)); + .unwrap() + .into_any(); + b.iter(|| HashMap::::extract(dict.as_borrowed())); }); } @@ -73,8 +74,9 @@ fn extract_btreemap(b: &mut Bencher<'_>) { let dict = (0..LEN as u64) .map(|i| (i, i * 2)) .into_py_dict(py) - .unwrap(); - b.iter(|| BTreeMap::::extract_bound(&dict)); + .unwrap() + .into_any(); + b.iter(|| BTreeMap::::extract(dict.as_borrowed())); }); } @@ -84,8 +86,9 @@ fn extract_hashbrown_map(b: &mut Bencher<'_>) { let dict = (0..LEN as u64) .map(|i| (i, i * 2)) .into_py_dict(py) - .unwrap(); - b.iter(|| hashbrown::HashMap::::extract_bound(&dict)); + .unwrap() + .into_any(); + b.iter(|| hashbrown::HashMap::::extract(dict.as_borrowed())); }); } diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index b674f4e530e..a7733ce58e1 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -499,7 +499,7 @@ impl<'a> Container<'a> { let ty = ty.clone().elide_lifetimes(); let pyo3_crate_path = &ctx.pyo3_path; builder.push_tokens( - quote! { <#ty as #pyo3_crate_path::FromPyObject<'_>>::INPUT_TYPE.as_bytes() }, + quote! { <#ty as #pyo3_crate_path::FromPyObject<'_, '_>>::INPUT_TYPE.as_bytes() }, ) } } @@ -543,7 +543,7 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result { let gen_ident = ¶m.ident; where_clause .predicates - .push(parse_quote!(#gen_ident: #pyo3_path::FromPyObject<'py>)) + .push(parse_quote!(#gen_ident: for<'_a> #pyo3_path::FromPyObject<'_a, 'py>)) } let derives = match &tokens.data { @@ -605,8 +605,9 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result { let ident = &tokens.ident; Ok(quote!( #[automatically_derived] - impl #impl_generics #pyo3_path::FromPyObject<#lt_param> for #ident #ty_generics #where_clause { - fn extract_bound(obj: &#pyo3_path::Bound<#lt_param, #pyo3_path::PyAny>) -> #pyo3_path::PyResult { + impl #impl_generics #pyo3_path::FromPyObject<'_, #lt_param> for #ident #ty_generics #where_clause { + fn extract(obj: #pyo3_path::Borrowed<'_, #lt_param, #pyo3_path::PyAny>) -> #pyo3_path::PyResult { + let obj: &#pyo3_path::Bound<'_, _> = &*obj; #derives } #input_type diff --git a/src/buffer.rs b/src/buffer.rs index c3aad73e04d..6d1d8683fcb 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -18,8 +18,8 @@ // DEALINGS IN THE SOFTWARE. //! `PyBuffer` implementation -use crate::Bound; use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python}; +use crate::{Borrowed, Bound}; use std::ffi::{ c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort, c_void, @@ -185,9 +185,9 @@ pub unsafe trait Element: Copy { fn is_compatible_format(format: &CStr) -> bool; } -impl FromPyObject<'_> for PyBuffer { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult> { - Self::get(obj) +impl FromPyObject<'_, '_> for PyBuffer { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult> { + Self::get(&obj) } } diff --git a/src/conversion.rs b/src/conversion.rs index 9d54124e29a..bf3fa992fe3 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -271,97 +271,16 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// # } /// ``` /// -// /// FIXME: until `FromPyObject` can pick up a second lifetime, the below commentary is no longer -// /// true. Update and restore this documentation at that time. -// /// -// /// Note: depending on the implementation, the lifetime of the extracted result may -// /// depend on the lifetime of the `obj` or the `prepared` variable. -// /// -// /// For example, when extracting `&str` from a Python byte string, the resulting string slice will -// /// point to the existing string data (lifetime: `'py`). -// /// On the other hand, when extracting `&str` from a Python Unicode string, the preparation step -// /// will convert the string to UTF-8, and the resulting string slice will have lifetime `'prepared`. -// /// Since which case applies depends on the runtime type of the Python object, -// /// both the `obj` and `prepared` variables must outlive the resulting string slice. +/// Note: depending on the implementation, the lifetime of the extracted result may +/// depend on the lifetime of the `obj` or the `prepared` variable. /// -/// During the migration of PyO3 from the "GIL Refs" API to the `Bound` smart pointer, this trait -/// has two methods `extract` and `extract_bound` which are defaulted to call each other. To avoid -/// infinite recursion, implementors must implement at least one of these methods. The recommendation -/// is to implement `extract_bound` and leave `extract` as the default implementation. -pub trait FromPyObject<'py>: Sized { - /// Provides the type hint information for this type when it appears as an argument. - /// - /// For example, `Vec` would be `collections.abc.Sequence[int]`. - /// The default value is `typing.Any`, which is correct for any type. - #[cfg(feature = "experimental-inspect")] - const INPUT_TYPE: &'static str = "typing.Any"; - - /// Extracts `Self` from the bound smart pointer `obj`. - /// - /// Implementors are encouraged to implement this method and leave `extract` defaulted, as - /// this will be most compatible with PyO3's future API. - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult; - - /// Extracts the type hint information for this type when it appears as an argument. - /// - /// For example, `Vec` would return `Sequence[int]`. - /// The default implementation returns `Any`, which is correct for any type. - /// - /// For most types, the return value for this method will be identical to that of - /// [`IntoPyObject::type_output`]. It may be different for some types, such as `Dict`, - /// to allow duck-typing: functions return `Dict` but take `Mapping` as argument. - #[cfg(feature = "experimental-inspect")] - fn type_input() -> TypeInfo { - TypeInfo::Any - } -} - -mod from_py_object_bound_sealed { - use crate::{pyclass::boolean_struct::False, PyClass, PyClassGuard, PyClassGuardMut}; - - /// Private seal for the `FromPyObjectBound` trait. - /// - /// This prevents downstream types from implementing the trait before - /// PyO3 is ready to declare the trait as public API. - pub trait Sealed {} - - // This generic implementation is why the seal is separate from - // `crate::sealed::Sealed`. - impl<'py, T> Sealed for T where T: super::FromPyObject<'py> {} - impl Sealed for PyClassGuard<'_, T> where T: PyClass {} - impl Sealed for PyClassGuardMut<'_, T> where T: PyClass {} - impl Sealed for &'_ str {} - impl Sealed for std::borrow::Cow<'_, str> {} - impl Sealed for &'_ [u8] {} - impl Sealed for std::borrow::Cow<'_, [u8]> {} -} - -/// Expected form of [`FromPyObject`] to be used in a future PyO3 release. -/// -/// The difference between this and `FromPyObject` is that this trait takes an -/// additional lifetime `'a`, which is the lifetime of the input `Bound`. -/// -/// This allows implementations for `&'a str` and `&'a [u8]`, which could not -/// be expressed by the existing `FromPyObject` trait once the GIL Refs API was -/// removed. -/// -/// # Usage -/// -/// Users are prevented from implementing this trait, instead they should implement -/// the normal `FromPyObject` trait. This trait has a blanket implementation -/// for `T: FromPyObject`. -/// -/// The only case where this trait may have a use case to be implemented is when the -/// lifetime of the extracted value is tied to the lifetime `'a` of the input `Bound` -/// instead of the GIL lifetime `py`, as is the case for the `&'a str` implementation. -/// -/// Please contact the PyO3 maintainers if you believe you have a use case for implementing -/// this trait before PyO3 is ready to change the main `FromPyObject` trait to take an -/// additional lifetime. -/// -/// Similarly, users should typically not call these trait methods and should instead -/// use this via the `extract` method on `Bound` and `Py`. -pub trait FromPyObjectBound<'a, 'py>: Sized + from_py_object_bound_sealed::Sealed { +/// For example, when extracting `&str` from a Python byte string, the resulting string slice will +/// point to the existing string data (lifetime: `'py`). +/// On the other hand, when extracting `&str` from a Python Unicode string, the preparation step +/// will convert the string to UTF-8, and the resulting string slice will have lifetime `'prepared`. +/// Since which case applies depends on the runtime type of the Python object, +/// both the `obj` and `prepared` variables must outlive the resulting string slice. +pub trait FromPyObject<'a, 'py>: Sized { /// Provides the type hint information for this type when it appears as an argument. /// /// For example, `Vec` would be `collections.abc.Sequence[int]`. @@ -373,7 +292,7 @@ pub trait FromPyObjectBound<'a, 'py>: Sized + from_py_object_bound_sealed::Seale /// /// Users are advised against calling this method directly: instead, use this via /// [`Bound<'_, PyAny>::extract`](crate::types::any::PyAnyMethods::extract) or [`Py::extract`]. - fn from_py_object_bound(ob: Borrowed<'a, 'py, PyAny>) -> PyResult; + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult; /// Extracts the type hint information for this type when it appears as an argument. /// @@ -389,56 +308,39 @@ pub trait FromPyObjectBound<'a, 'py>: Sized + from_py_object_bound_sealed::Seale } } -impl<'py, T> FromPyObjectBound<'_, 'py> for T -where - T: FromPyObject<'py>, -{ - #[cfg(feature = "experimental-inspect")] - const INPUT_TYPE: &'static str = T::INPUT_TYPE; - - fn from_py_object_bound(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { - Self::extract_bound(&ob) - } - - #[cfg(feature = "experimental-inspect")] - fn type_input() -> TypeInfo { - ::type_input() - } -} - -impl FromPyObject<'_> for T +impl FromPyObject<'_, '_> for T where T: PyClass + Clone, { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = ::TYPE_NAME; - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let bound = obj.cast::()?; Ok(bound.try_borrow()?.clone()) } } -impl<'py, T> FromPyObject<'py> for PyRef<'py, T> +impl<'py, T> FromPyObject<'_, 'py> for PyRef<'py, T> where T: PyClass, { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = ::TYPE_NAME; - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { obj.cast::()?.try_borrow().map_err(Into::into) } } -impl<'py, T> FromPyObject<'py> for PyRefMut<'py, T> +impl<'py, T> FromPyObject<'_, 'py> for PyRefMut<'py, T> where T: PyClass, { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = ::TYPE_NAME; - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { obj.cast::()?.try_borrow_mut().map_err(Into::into) } } diff --git a/src/conversions/bigdecimal.rs b/src/conversions/bigdecimal.rs index af976431894..ffd77ff5621 100644 --- a/src/conversions/bigdecimal.rs +++ b/src/conversions/bigdecimal.rs @@ -56,7 +56,7 @@ use crate::{ exceptions::PyValueError, sync::PyOnceLock, types::{PyAnyMethods, PyStringMethods, PyType}, - Bound, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, + Borrowed, Bound, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, }; use bigdecimal::BigDecimal; use num_bigint::Sign; @@ -71,8 +71,8 @@ fn get_invalid_operation_error_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType INVALID_OPERATION_CLS.import(py, "decimal", "InvalidOperation") } -impl FromPyObject<'_> for BigDecimal { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for BigDecimal { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let py_str = &obj.str()?; let rs_str = &py_str.to_cow()?; BigDecimal::from_str(rs_str).map_err(|e| PyValueError::new_err(e.to_string())) diff --git a/src/conversions/bytes.rs b/src/conversions/bytes.rs index 9c615e84a5e..934a3bc1444 100644 --- a/src/conversions/bytes.rs +++ b/src/conversions/bytes.rs @@ -67,13 +67,12 @@ use bytes::Bytes; use crate::conversion::IntoPyObject; use crate::instance::Bound; use crate::pybacked::PyBackedBytes; -use crate::types::any::PyAnyMethods; use crate::types::PyBytes; -use crate::{FromPyObject, PyAny, PyErr, PyResult, Python}; +use crate::{Borrowed, FromPyObject, PyAny, PyErr, PyResult, Python}; -impl FromPyObject<'_> for Bytes { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { - Ok(Bytes::from_owner(ob.extract::()?)) +impl FromPyObject<'_, '_> for Bytes { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + Ok(Bytes::from_owner(obj.extract::()?)) } } @@ -100,7 +99,7 @@ impl<'py> IntoPyObject<'py> for &Bytes { #[cfg(test)] mod tests { use super::*; - use crate::types::{PyByteArray, PyByteArrayMethods, PyBytes}; + use crate::types::{PyAnyMethods, PyByteArray, PyByteArrayMethods, PyBytes}; use crate::Python; #[test] diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index bd97d3409fc..cb85fca14f7 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -108,8 +108,8 @@ impl<'py> IntoPyObject<'py> for &Duration { } } -impl FromPyObject<'_> for Duration { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Duration { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let delta = ob.cast::()?; // Python size are much lower than rust size so we do not need bound checks. // 0 <= microseconds < 1000000 @@ -162,9 +162,9 @@ impl<'py> IntoPyObject<'py> for &NaiveDate { } } -impl FromPyObject<'_> for NaiveDate { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { - let date = ob.cast::()?; +impl FromPyObject<'_, '_> for NaiveDate { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + let date = &*ob.cast::()?; py_date_to_naive_date(date) } } @@ -204,9 +204,9 @@ impl<'py> IntoPyObject<'py> for &NaiveTime { } } -impl FromPyObject<'_> for NaiveTime { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { - let time = ob.cast::()?; +impl FromPyObject<'_, '_> for NaiveTime { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + let time = &*ob.cast::()?; py_time_to_naive_time(time) } } @@ -247,9 +247,9 @@ impl<'py> IntoPyObject<'py> for &NaiveDateTime { } } -impl FromPyObject<'_> for NaiveDateTime { - fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult { - let dt = dt.cast::()?; +impl FromPyObject<'_, '_> for NaiveDateTime { + fn extract(dt: Borrowed<'_, '_, PyAny>) -> PyResult { + let dt = &*dt.cast::()?; // If the user tries to convert a timezone aware datetime into a naive one, // we return a hard error. We could silently remove tzinfo, or assume local timezone @@ -324,9 +324,12 @@ where } } -impl FromPyObject<'py>> FromPyObject<'_> for DateTime { - fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult> { - let dt = dt.cast::()?; +impl<'py, Tz> FromPyObject<'_, 'py> for DateTime +where + Tz: TimeZone + for<'a> FromPyObject<'a, 'py>, +{ + fn extract(dt: Borrowed<'_, 'py, PyAny>) -> PyResult> { + let dt = &*dt.cast::()?; let tzinfo = dt.get_tzinfo(); let tz = if let Some(tzinfo) = tzinfo { @@ -382,12 +385,12 @@ impl<'py> IntoPyObject<'py> for &FixedOffset { } } -impl FromPyObject<'_> for FixedOffset { +impl FromPyObject<'_, '_> for FixedOffset { /// Convert python tzinfo to rust [`FixedOffset`]. /// /// Note that the conversion will result in precision lost in microseconds as chrono offset /// does not supports microseconds. - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let ob = ob.cast::()?; // Passing Python's None to the `utcoffset` function will only @@ -431,8 +434,8 @@ impl<'py> IntoPyObject<'py> for &Utc { } } -impl FromPyObject<'_> for Utc { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Utc { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let py_utc = PyTzInfo::utc(ob.py())?; if ob.eq(py_utc)? { Ok(Utc) @@ -475,8 +478,8 @@ impl<'py> IntoPyObject<'py> for &Local { } #[cfg(feature = "chrono-local")] -impl FromPyObject<'_> for Local { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Local { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let local_tz = Local.into_pyobject(ob.py())?; if ob.eq(local_tz)? { Ok(Local) @@ -543,7 +546,9 @@ fn warn_truncated_leap_second(obj: &Bound<'_, PyAny>) { } #[cfg(not(Py_LIMITED_API))] -fn py_date_to_naive_date(py_date: &impl PyDateAccess) -> PyResult { +fn py_date_to_naive_date( + py_date: impl std::ops::Deref, +) -> PyResult { NaiveDate::from_ymd_opt( py_date.get_year(), py_date.get_month().into(), @@ -563,7 +568,9 @@ fn py_date_to_naive_date(py_date: &Bound<'_, PyAny>) -> PyResult { } #[cfg(not(Py_LIMITED_API))] -fn py_time_to_naive_time(py_time: &impl PyTimeAccess) -> PyResult { +fn py_time_to_naive_time( + py_time: impl std::ops::Deref, +) -> PyResult { NaiveTime::from_hms_micro_opt( py_time.get_hour().into(), py_time.get_minute().into(), diff --git a/src/conversions/chrono_tz.rs b/src/conversions/chrono_tz.rs index ff2b6b74d54..bd652e3a25b 100644 --- a/src/conversions/chrono_tz.rs +++ b/src/conversions/chrono_tz.rs @@ -38,7 +38,7 @@ use crate::conversion::IntoPyObject; use crate::exceptions::PyValueError; use crate::pybacked::PyBackedStr; use crate::types::{any::PyAnyMethods, PyTzInfo}; -use crate::{intern, Bound, FromPyObject, PyAny, PyErr, PyResult, Python}; +use crate::{intern, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python}; use chrono_tz::Tz; use std::str::FromStr; @@ -63,8 +63,8 @@ impl<'py> IntoPyObject<'py> for &Tz { } } -impl FromPyObject<'_> for Tz { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Tz { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { Tz::from_str( &ob.getattr(intern!(ob.py(), "key"))? .extract::()?, @@ -78,6 +78,7 @@ mod tests { use super::*; use crate::prelude::PyAnyMethods; use crate::types::PyTzInfo; + use crate::Bound; use crate::Python; use chrono::{DateTime, Utc}; use chrono_tz::Tz; diff --git a/src/conversions/either.rs b/src/conversions/either.rs index d073f3e2706..9b76ed0f9be 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::{ - exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPyObject, - IntoPyObjectExt, PyAny, PyErr, PyResult, Python, + exceptions::PyTypeError, Borrowed, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, PyAny, + PyErr, PyResult, Python, }; use either::Either; @@ -89,13 +89,13 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "either")))] -impl<'py, L, R> FromPyObject<'py> for Either +impl<'a, 'py, L, R> FromPyObject<'a, 'py> for Either where - L: FromPyObject<'py>, - R: FromPyObject<'py>, + L: FromPyObject<'a, 'py>, + R: FromPyObject<'a, 'py>, { #[inline] - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { if let Ok(l) = obj.extract::() { Ok(Either::Left(l)) } else if let Ok(r) = obj.extract::() { diff --git a/src/conversions/hashbrown.rs b/src/conversions/hashbrown.rs index 26fcc5c627e..75aa13a4e8c 100644 --- a/src/conversions/hashbrown.rs +++ b/src/conversions/hashbrown.rs @@ -25,7 +25,7 @@ use crate::{ set::{try_new_from_iter, PySetMethods}, PyDict, PyFrozenSet, PySet, }, - Bound, FromPyObject, PyAny, PyErr, PyResult, Python, + Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python, }; use std::{cmp, hash}; @@ -67,16 +67,16 @@ where } } -impl<'py, K, V, S> FromPyObject<'py> for hashbrown::HashMap +impl<'py, K, V, S> FromPyObject<'_, 'py> for hashbrown::HashMap where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, - V: FromPyObject<'py>, + K: for<'a> FromPyObject<'a, 'py> + cmp::Eq + hash::Hash, + V: for<'a> FromPyObject<'a, 'py>, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> Result { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default()); - for (k, v) in dict { + for (k, v) in dict.iter() { ret.insert(k.extract()?, v.extract()?); } Ok(ret) @@ -111,12 +111,12 @@ where } } -impl<'py, K, S> FromPyObject<'py> for hashbrown::HashSet +impl<'py, K, S> FromPyObject<'_, 'py> for hashbrown::HashSet where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, + K: for<'a> FromPyObject<'a, 'py> + cmp::Eq + hash::Hash, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { match ob.cast::() { Ok(set) => set.iter().map(|any| any.extract()).collect(), Err(err) => { diff --git a/src/conversions/indexmap.rs b/src/conversions/indexmap.rs index b3e8190d471..6abf47abe09 100644 --- a/src/conversions/indexmap.rs +++ b/src/conversions/indexmap.rs @@ -89,7 +89,7 @@ use crate::conversion::IntoPyObject; use crate::types::*; -use crate::{Bound, FromPyObject, PyErr, Python}; +use crate::{Borrowed, Bound, FromPyObject, PyErr, Python}; use std::{cmp, hash}; impl<'py, K, V, H> IntoPyObject<'py> for indexmap::IndexMap @@ -130,16 +130,16 @@ where } } -impl<'py, K, V, S> FromPyObject<'py> for indexmap::IndexMap +impl<'py, K, V, S> FromPyObject<'_, 'py> for indexmap::IndexMap where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, - V: FromPyObject<'py>, + K: for<'a> FromPyObject<'a, 'py> + cmp::Eq + hash::Hash, + V: for<'a> FromPyObject<'a, 'py>, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> Result { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; let mut ret = indexmap::IndexMap::with_capacity_and_hasher(dict.len(), S::default()); - for (k, v) in dict { + for (k, v) in dict.iter() { ret.insert(k.extract()?, v.extract()?); } Ok(ret) diff --git a/src/conversions/jiff.rs b/src/conversions/jiff.rs index fd2503646c0..72ca06cd2fa 100644 --- a/src/conversions/jiff.rs +++ b/src/conversions/jiff.rs @@ -51,7 +51,7 @@ use crate::types::{PyAnyMethods, PyNone}; use crate::types::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo, PyTzInfoAccess}; #[cfg(not(Py_LIMITED_API))] use crate::types::{PyDateAccess, PyDeltaAccess, PyTimeAccess}; -use crate::{intern, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, PyResult, Python}; +use crate::{intern, Borrowed, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, PyResult, Python}; use jiff::civil::{Date, DateTime, Time}; use jiff::tz::{Offset, TimeZone}; use jiff::{SignedDuration, Span, Timestamp, Zoned}; @@ -122,8 +122,8 @@ impl<'py> IntoPyObject<'py> for &Timestamp { } } -impl<'py> FromPyObject<'py> for Timestamp { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for Timestamp { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let zoned = ob.extract::()?; Ok(zoned.timestamp()) } @@ -154,8 +154,8 @@ impl<'py> IntoPyObject<'py> for &Date { } } -impl<'py> FromPyObject<'py> for Date { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for Date { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let date = ob.cast::()?; #[cfg(not(Py_LIMITED_API))] @@ -206,11 +206,11 @@ impl<'py> IntoPyObject<'py> for &Time { } } -impl<'py> FromPyObject<'py> for Time { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for Time { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let ob = ob.cast::()?; - - pytime_to_time(ob) + #[allow(clippy::explicit_auto_deref)] + pytime_to_time(&*ob) } } @@ -234,8 +234,8 @@ impl<'py> IntoPyObject<'py> for &DateTime { } } -impl<'py> FromPyObject<'py> for DateTime { - fn extract_bound(dt: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for DateTime { + fn extract(dt: Borrowed<'_, 'py, PyAny>) -> PyResult { let dt = dt.cast::()?; let has_tzinfo = dt.get_tzinfo().is_some(); @@ -243,7 +243,8 @@ impl<'py> FromPyObject<'py> for DateTime { return Err(PyTypeError::new_err("expected a datetime without tzinfo")); } - Ok(DateTime::from_parts(dt.extract()?, pytime_to_time(dt)?)) + #[allow(clippy::explicit_auto_deref)] + Ok(DateTime::from_parts(dt.extract()?, pytime_to_time(&*dt)?)) } } @@ -283,8 +284,8 @@ impl<'py> IntoPyObject<'py> for &Zoned { } } -impl<'py> FromPyObject<'py> for Zoned { - fn extract_bound(dt: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for Zoned { + fn extract(dt: Borrowed<'_, 'py, PyAny>) -> PyResult { let dt = dt.cast::()?; let tz = dt @@ -295,7 +296,8 @@ impl<'py> FromPyObject<'py> for Zoned { "expected a datetime with non-None tzinfo", )) })?; - let datetime = DateTime::from_parts(dt.extract()?, pytime_to_time(dt)?); + #[allow(clippy::explicit_auto_deref)] + let datetime = DateTime::from_parts(dt.extract()?, pytime_to_time(&*dt)?); let zoned = tz.into_ambiguous_zoned(datetime); #[cfg(not(Py_LIMITED_API))] @@ -340,8 +342,8 @@ impl<'py> IntoPyObject<'py> for &TimeZone { } } -impl<'py> FromPyObject<'py> for TimeZone { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for TimeZone { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let ob = ob.cast::()?; let attr = intern!(ob.py(), "key"); @@ -377,8 +379,8 @@ impl<'py> IntoPyObject<'py> for Offset { } } -impl<'py> FromPyObject<'py> for Offset { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for Offset { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let py = ob.py(); let ob = ob.cast::()?; @@ -425,8 +427,8 @@ impl<'py> IntoPyObject<'py> for SignedDuration { } } -impl<'py> FromPyObject<'py> for SignedDuration { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for SignedDuration { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let delta = ob.cast::()?; #[cfg(not(Py_LIMITED_API))] @@ -450,8 +452,8 @@ impl<'py> FromPyObject<'py> for SignedDuration { } } -impl<'py> FromPyObject<'py> for Span { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for Span { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let duration = ob.extract::()?; Ok(duration.try_into()?) } diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index 0f8924bbf12..ef63c35f24b 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -50,7 +50,7 @@ #[cfg(Py_LIMITED_API)] use crate::types::{bytes::PyBytesMethods, PyBytes}; use crate::{ - conversion::IntoPyObject, ffi, instance::Bound, types::PyInt, FromPyObject, Py, PyAny, PyErr, + conversion::IntoPyObject, ffi, types::PyInt, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python, }; @@ -123,8 +123,8 @@ bigint_conversion!(BigUint, false, BigUint::to_bytes_le); bigint_conversion!(BigInt, true, BigInt::to_signed_bytes_le); #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] -impl<'py> FromPyObject<'py> for BigInt { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for BigInt { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let py = ob.py(); // fast path - checking for subclass of `int` just checks a bit in the type object let num_owned: Py; @@ -132,11 +132,11 @@ impl<'py> FromPyObject<'py> for BigInt { long } else { num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? }; - num_owned.bind(py) + num_owned.bind_borrowed(py) }; #[cfg(not(Py_LIMITED_API))] { - let mut buffer = int_to_u32_vec::(num)?; + let mut buffer = int_to_u32_vec::(&num)?; let sign = if buffer.last().copied().is_some_and(|last| last >> 31 != 0) { // BigInt::new takes an unsigned array, so need to convert from two's complement // flip all bits, 'subtract' 1 (by adding one to the unsigned array) @@ -160,19 +160,19 @@ impl<'py> FromPyObject<'py> for BigInt { } #[cfg(Py_LIMITED_API)] { - let n_bits = int_n_bits(num)?; + let n_bits = int_n_bits(&num)?; if n_bits == 0 { return Ok(BigInt::from(0isize)); } - let bytes = int_to_py_bytes(num, (n_bits + 8) / 8, true)?; + let bytes = int_to_py_bytes(&num, (n_bits + 8) / 8, true)?; Ok(BigInt::from_signed_bytes_le(bytes.as_bytes())) } } } #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] -impl<'py> FromPyObject<'py> for BigUint { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for BigUint { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let py = ob.py(); // fast path - checking for subclass of `int` just checks a bit in the type object let num_owned: Py; @@ -180,20 +180,20 @@ impl<'py> FromPyObject<'py> for BigUint { long } else { num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? }; - num_owned.bind(py) + num_owned.bind_borrowed(py) }; #[cfg(not(Py_LIMITED_API))] { - let buffer = int_to_u32_vec::(num)?; + let buffer = int_to_u32_vec::(&num)?; Ok(BigUint::new(buffer)) } #[cfg(Py_LIMITED_API)] { - let n_bits = int_n_bits(num)?; + let n_bits = int_n_bits(&num)?; if n_bits == 0 { return Ok(BigUint::from(0usize)); } - let bytes = int_to_py_bytes(num, n_bits.div_ceil(8), false)?; + let bytes = int_to_py_bytes(&num, n_bits.div_ceil(8), false)?; Ok(BigUint::from_bytes_le(bytes.as_bytes())) } } @@ -307,8 +307,8 @@ fn int_n_bits(long: &Bound<'_, PyInt>) -> PyResult { #[cfg(Py_LIMITED_API)] { - use crate::types::any::PyAnyMethods; // slow path + use crate::types::PyAnyMethods; long.call_method0(crate::intern!(py, "bit_length")) .and_then(|any| any.extract()) } diff --git a/src/conversions/num_complex.rs b/src/conversions/num_complex.rs index cdb7ad3fc42..df416f7a0fb 100644 --- a/src/conversions/num_complex.rs +++ b/src/conversions/num_complex.rs @@ -94,8 +94,8 @@ //! assert result == [complex(1,-1), complex(-2,0)] //! ``` use crate::{ - ffi, ffi_ptr_ext::FfiPtrExt, types::PyComplex, Bound, FromPyObject, PyAny, PyErr, PyResult, - Python, + ffi, ffi_ptr_ext::FfiPtrExt, types::PyComplex, Borrowed, Bound, FromPyObject, PyAny, PyErr, + PyResult, Python, }; use num_complex::Complex; use std::ffi::c_double; @@ -146,8 +146,8 @@ macro_rules! complex_conversion { } #[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))] - impl FromPyObject<'_> for Complex<$float> { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult> { + impl FromPyObject<'_, '_> for Complex<$float> { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult> { #[cfg(not(any(Py_LIMITED_API, PyPy)))] unsafe { let val = ffi::PyComplex_AsCComplex(obj.as_ptr()); @@ -169,7 +169,7 @@ macro_rules! complex_conversion { obj.lookup_special(crate::intern!(obj.py(), "__complex__"))? { complex = method.call0()?; - &complex + complex.as_borrowed() } else { // `obj` might still implement `__float__` or `__index__`, which will be // handled by `PyComplex_{Real,Imag}AsDouble`, including propagating any diff --git a/src/conversions/num_rational.rs b/src/conversions/num_rational.rs index 25aac24aa7a..9a648de9ebe 100644 --- a/src/conversions/num_rational.rs +++ b/src/conversions/num_rational.rs @@ -48,7 +48,7 @@ use crate::ffi; use crate::sync::PyOnceLock; use crate::types::any::PyAnyMethods; use crate::types::PyType; -use crate::{Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; +use crate::{Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; #[cfg(feature = "num-bigint")] use num_bigint::BigInt; @@ -62,8 +62,8 @@ fn get_fraction_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { macro_rules! rational_conversion { ($int: ty) => { - impl<'py> FromPyObject<'py> for Ratio<$int> { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + impl<'py> FromPyObject<'_, 'py> for Ratio<$int> { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { let py = obj.py(); let py_numerator_obj = obj.getattr(crate::intern!(py, "numerator"))?; let py_denominator_obj = obj.getattr(crate::intern!(py, "denominator"))?; diff --git a/src/conversions/ordered_float.rs b/src/conversions/ordered_float.rs index a4da92b5b14..fd9c81ba7d7 100644 --- a/src/conversions/ordered_float.rs +++ b/src/conversions/ordered_float.rs @@ -53,15 +53,15 @@ use crate::conversion::IntoPyObject; use crate::exceptions::PyValueError; -use crate::types::{any::PyAnyMethods, PyFloat}; -use crate::{Bound, FromPyObject, PyAny, PyResult, Python}; +use crate::types::PyFloat; +use crate::{Borrowed, Bound, FromPyObject, PyAny, PyResult, Python}; use ordered_float::{NotNan, OrderedFloat}; use std::convert::Infallible; macro_rules! float_conversions { ($wrapper:ident, $float_type:ty, $constructor:expr) => { - impl FromPyObject<'_> for $wrapper<$float_type> { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + impl FromPyObject<'_, '_> for $wrapper<$float_type> { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let val: $float_type = obj.extract()?; $constructor(val) } @@ -101,6 +101,7 @@ mod test_ordered_float { use super::*; use crate::ffi::c_str; use crate::py_run; + use crate::types::PyAnyMethods; #[cfg(not(target_arch = "wasm32"))] use proptest::prelude::*; diff --git a/src/conversions/rust_decimal.rs b/src/conversions/rust_decimal.rs index b533f97c5a5..60495561e1e 100644 --- a/src/conversions/rust_decimal.rs +++ b/src/conversions/rust_decimal.rs @@ -55,12 +55,12 @@ use crate::sync::PyOnceLock; use crate::types::any::PyAnyMethods; use crate::types::string::PyStringMethods; use crate::types::PyType; -use crate::{Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; +use crate::{Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; use rust_decimal::Decimal; use std::str::FromStr; -impl FromPyObject<'_> for Decimal { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Decimal { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { // use the string representation to not be lossy if let Ok(val) = obj.extract() { Ok(Decimal::new(val, 0)) diff --git a/src/conversions/smallvec.rs b/src/conversions/smallvec.rs index e6e7262590f..625228e3f1b 100644 --- a/src/conversions/smallvec.rs +++ b/src/conversions/smallvec.rs @@ -21,8 +21,9 @@ use crate::exceptions::PyTypeError; use crate::inspect::types::TypeInfo; use crate::types::any::PyAnyMethods; use crate::types::{PySequence, PyString}; -use crate::PyErr; -use crate::{err::DowncastError, ffi, Bound, FromPyObject, PyAny, PyResult, Python}; +use crate::{ + err::DowncastError, ffi, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python, +}; use smallvec::{Array, SmallVec}; impl<'py, A> IntoPyObject<'py> for SmallVec @@ -70,12 +71,12 @@ where } } -impl<'py, A> FromPyObject<'py> for SmallVec +impl<'py, A> FromPyObject<'_, 'py> for SmallVec where A: Array, - A::Item: FromPyObject<'py>, + A::Item: for<'a> FromPyObject<'a, 'py>, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { if obj.is_instance_of::() { return Err(PyTypeError::new_err("Can't extract `str` to `SmallVec`")); } @@ -88,10 +89,10 @@ where } } -fn extract_sequence<'py, A>(obj: &Bound<'py, PyAny>) -> PyResult> +fn extract_sequence<'py, A>(obj: Borrowed<'_, 'py, PyAny>) -> PyResult> where A: Array, - A::Item: FromPyObject<'py>, + A::Item: for<'a> FromPyObject<'a, 'py>, { // Types that pass `PySequence_Check` usually implement enough of the sequence protocol // to support this function and if not, we will only fail extraction safely. @@ -99,7 +100,7 @@ where if ffi::PySequence_Check(obj.as_ptr()) != 0 { obj.cast_unchecked::() } else { - return Err(DowncastError::new(obj, "Sequence").into()); + return Err(DowncastError::new_from_borrowed(obj, "Sequence").into()); } }; diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index a7f2d953284..c716f69b103 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -1,9 +1,8 @@ use crate::conversion::IntoPyObject; -use crate::instance::Bound; use crate::types::any::PyAnyMethods; use crate::types::PySequence; use crate::{err::DowncastError, ffi, FromPyObject, PyAny, PyResult, Python}; -use crate::{exceptions, PyErr}; +use crate::{exceptions, Borrowed, Bound, PyErr}; impl<'py, T, const N: usize> IntoPyObject<'py> for [T; N] where @@ -37,18 +36,18 @@ where } } -impl<'py, T, const N: usize> FromPyObject<'py> for [T; N] +impl<'py, T, const N: usize> FromPyObject<'_, 'py> for [T; N] where - T: FromPyObject<'py>, + T: for<'a> FromPyObject<'a, 'py>, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { create_array_from_obj(obj) } } -fn create_array_from_obj<'py, T, const N: usize>(obj: &Bound<'py, PyAny>) -> PyResult<[T; N]> +fn create_array_from_obj<'py, T, const N: usize>(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<[T; N]> where - T: FromPyObject<'py>, + T: for<'a> FromPyObject<'a, 'py>, { // Types that pass `PySequence_Check` usually implement enough of the sequence protocol // to support this function and if not, we will only fail extraction safely. @@ -56,7 +55,7 @@ where if ffi::PySequence_Check(obj.as_ptr()) != 0 { obj.cast_unchecked::() } else { - return Err(DowncastError::new(obj, "Sequence").into()); + return Err(DowncastError::new_from_borrowed(obj, "Sequence").into()); } }; let seq_len = seq.len()?; diff --git a/src/conversions/std/cell.rs b/src/conversions/std/cell.rs index 17f29d37141..f123caf4a6d 100644 --- a/src/conversions/std/cell.rs +++ b/src/conversions/std/cell.rs @@ -1,9 +1,6 @@ use std::cell::Cell; -use crate::{ - conversion::IntoPyObject, types::any::PyAnyMethods, Bound, FromPyObject, PyAny, PyResult, - Python, -}; +use crate::{conversion::IntoPyObject, Borrowed, FromPyObject, PyAny, PyResult, Python}; impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for Cell { type Target = T::Target; @@ -33,11 +30,11 @@ impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for &Cell { } } -impl<'py, T: FromPyObject<'py>> FromPyObject<'py> for Cell { +impl<'a, 'py, T: FromPyObject<'a, 'py>> FromPyObject<'a, 'py> for Cell { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = T::INPUT_TYPE; - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { ob.extract().map(Cell::new) } } diff --git a/src/conversions/std/ipaddr.rs b/src/conversions/std/ipaddr.rs index 7e6453ed654..4bd563fe3a9 100644 --- a/src/conversions/std/ipaddr.rs +++ b/src/conversions/std/ipaddr.rs @@ -2,15 +2,14 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use crate::conversion::IntoPyObject; use crate::exceptions::PyValueError; -use crate::instance::Bound; use crate::sync::PyOnceLock; use crate::types::any::PyAnyMethods; use crate::types::string::PyStringMethods; use crate::types::PyType; -use crate::{intern, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; +use crate::{intern, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; -impl FromPyObject<'_> for IpAddr { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for IpAddr { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { match obj.getattr(intern!(obj.py(), "packed")) { Ok(packed) => { if let Ok(packed) = packed.extract::<[u8; 4]>() { diff --git a/src/conversions/std/map.rs b/src/conversions/std/map.rs index fc96d211dc3..8646f81b8bc 100644 --- a/src/conversions/std/map.rs +++ b/src/conversions/std/map.rs @@ -6,7 +6,7 @@ use crate::{ conversion::IntoPyObject, instance::Bound, types::{any::PyAnyMethods, dict::PyDictMethods, PyDict}, - FromPyObject, PyAny, PyErr, Python, + Borrowed, FromPyObject, PyAny, PyErr, Python, }; impl<'py, K, V, H> IntoPyObject<'py> for collections::HashMap @@ -107,16 +107,16 @@ where } } -impl<'py, K, V, S> FromPyObject<'py> for collections::HashMap +impl<'py, K, V, S> FromPyObject<'_, 'py> for collections::HashMap where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, - V: FromPyObject<'py>, + K: for<'a> FromPyObject<'a, 'py> + cmp::Eq + hash::Hash, + V: for<'a> FromPyObject<'a, 'py>, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> Result { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; let mut ret = collections::HashMap::with_capacity_and_hasher(dict.len(), S::default()); - for (k, v) in dict { + for (k, v) in dict.iter() { ret.insert(k.extract()?, v.extract()?); } Ok(ret) @@ -128,15 +128,15 @@ where } } -impl<'py, K, V> FromPyObject<'py> for collections::BTreeMap +impl<'py, K, V> FromPyObject<'_, 'py> for collections::BTreeMap where - K: FromPyObject<'py> + cmp::Ord, - V: FromPyObject<'py>, + K: for<'a> FromPyObject<'a, 'py> + cmp::Ord, + V: for<'a> FromPyObject<'a, 'py>, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> Result { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; let mut ret = collections::BTreeMap::new(); - for (k, v) in dict { + for (k, v) in dict.iter() { ret.insert(k.extract()?, v.extract()?); } Ok(ret) diff --git a/src/conversions/std/num.rs b/src/conversions/std/num.rs index 42a65a27929..9cf327f220a 100644 --- a/src/conversions/std/num.rs +++ b/src/conversions/std/num.rs @@ -3,9 +3,8 @@ use crate::conversion::IntoPyObject; use crate::ffi_ptr_ext::FfiPtrExt; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; -use crate::types::any::PyAnyMethods; use crate::types::{PyBytes, PyInt}; -use crate::{exceptions, ffi, Bound, FromPyObject, PyAny, PyErr, PyResult, Python}; +use crate::{exceptions, ffi, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python}; use std::convert::Infallible; use std::ffi::c_long; use std::num::{ @@ -51,11 +50,11 @@ macro_rules! int_fits_larger_int { } } - impl FromPyObject<'_> for $rust_type { + impl FromPyObject<'_, '_> for $rust_type { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = <$larger_type>::INPUT_TYPE; - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let val: $larger_type = obj.extract()?; <$rust_type>::try_from(val) .map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) @@ -129,17 +128,12 @@ macro_rules! int_convert_u64_or_i64 { fn into_pyobject(self, py: Python<'py>) -> Result { (*self).into_pyobject(py) } - - #[cfg(feature = "experimental-inspect")] - fn type_output() -> TypeInfo { - TypeInfo::builtin("int") - } } - impl FromPyObject<'_> for $rust_type { + impl FromPyObject<'_, '_> for $rust_type { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { extract_int!(obj, !0, $pylong_as_ll_or_ull, $force_index_call) } @@ -194,11 +188,11 @@ macro_rules! int_fits_c_long { } } - impl<'py> FromPyObject<'py> for $rust_type { + impl<'py> FromPyObject<'_, 'py> for $rust_type { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { let val: c_long = extract_int!(obj, -1, ffi::PyLong_AsLong)?; <$rust_type>::try_from(val) .map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) @@ -277,11 +271,11 @@ impl<'py> IntoPyObject<'py> for &'_ u8 { } } -impl FromPyObject<'_> for u8 { +impl<'py> FromPyObject<'_, 'py> for u8 { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { let val: c_long = extract_int!(obj, -1, ffi::PyLong_AsLong)?; u8::try_from(val).map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) } @@ -408,11 +402,11 @@ mod fast_128bit_int_conversion { } } - impl FromPyObject<'_> for $rust_type { + impl FromPyObject<'_, '_> for $rust_type { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { let num = unsafe { ffi::PyNumber_Index(ob.as_ptr()).assume_owned_or_err(ob.py())? }; let mut buffer = [0u8; std::mem::size_of::<$rust_type>()]; @@ -474,6 +468,7 @@ mod fast_128bit_int_conversion { #[cfg(any(Py_LIMITED_API, GraalPy))] mod slow_128bit_int_conversion { use super::*; + use crate::types::any::PyAnyMethods as _; const SHIFT: usize = 64; // for 128bit Integers @@ -526,11 +521,11 @@ mod slow_128bit_int_conversion { } } - impl FromPyObject<'_> for $rust_type { + impl FromPyObject<'_, '_> for $rust_type { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { let py = ob.py(); unsafe { let lower = err_if_invalid_value( @@ -614,11 +609,11 @@ macro_rules! nonzero_int_impl { } } - impl FromPyObject<'_> for $nonzero_type { + impl FromPyObject<'_, '_> for $nonzero_type { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = <$primitive_type>::INPUT_TYPE; - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let val: $primitive_type = obj.extract()?; <$nonzero_type>::try_from(val) .map_err(|_| exceptions::PyValueError::new_err("invalid zero value")) @@ -648,6 +643,7 @@ nonzero_int_impl!(NonZeroUsize, usize); #[cfg(test)] mod test_128bit_integers { use super::*; + use crate::types::PyAnyMethods; #[cfg(not(target_arch = "wasm32"))] use crate::types::PyDict; diff --git a/src/conversions/std/option.rs b/src/conversions/std/option.rs index ae0ec441c61..04369bf79f9 100644 --- a/src/conversions/std/option.rs +++ b/src/conversions/std/option.rs @@ -1,7 +1,8 @@ use crate::{ - conversion::IntoPyObject, types::any::PyAnyMethods, Bound, BoundObject, FromPyObject, PyAny, - PyResult, Python, + conversion::IntoPyObject, types::any::PyAnyMethods, BoundObject, FromPyObject, PyAny, PyResult, + Python, }; +use crate::{Borrowed, Bound}; impl<'py, T> IntoPyObject<'py> for Option where @@ -37,11 +38,11 @@ where } } -impl<'py, T> FromPyObject<'py> for Option +impl<'a, 'py, T> FromPyObject<'a, 'py> for Option where - T: FromPyObject<'py>, + T: FromPyObject<'a, 'py>, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { if obj.is_none() { Ok(None) } else { diff --git a/src/conversions/std/osstr.rs b/src/conversions/std/osstr.rs index acc6f2bab7f..df38f9f9b48 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -2,7 +2,7 @@ use crate::conversion::IntoPyObject; use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::Bound; use crate::types::PyString; -use crate::{ffi, FromPyObject, PyAny, PyResult, Python}; +use crate::{ffi, Borrowed, FromPyObject, PyAny, PyResult, Python}; use std::borrow::Cow; use std::convert::Infallible; use std::ffi::{OsStr, OsString}; @@ -70,8 +70,8 @@ impl<'py> IntoPyObject<'py> for &&OsStr { // There's no FromPyObject implementation for &OsStr because albeit possible on Unix, this would // be impossible to implement on Windows. Hence it's omitted entirely -impl FromPyObject<'_> for OsString { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for OsString { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let pystring = ob.cast::()?; #[cfg(not(windows))] @@ -97,8 +97,6 @@ impl FromPyObject<'_> for OsString { #[cfg(windows)] { - use crate::types::string::PyStringMethods; - // Take the quick and easy shortcut if UTF-8 if let Ok(utf8_string) = pystring.to_cow() { return Ok(utf8_string.into_owned().into()); @@ -169,7 +167,7 @@ impl<'py> IntoPyObject<'py> for &OsString { #[cfg(test)] mod tests { - use crate::types::{PyAnyMethods, PyString, PyStringMethods}; + use crate::types::{PyString, PyStringMethods}; use crate::{BoundObject, IntoPyObject, Python}; use std::fmt::Debug; use std::{ diff --git a/src/conversions/std/path.rs b/src/conversions/std/path.rs index 17cad8f2694..4ba21fe098b 100644 --- a/src/conversions/std/path.rs +++ b/src/conversions/std/path.rs @@ -1,17 +1,16 @@ use crate::conversion::IntoPyObject; use crate::ffi_ptr_ext::FfiPtrExt; -use crate::instance::Bound; use crate::sync::PyOnceLock; use crate::types::any::PyAnyMethods; -use crate::{ffi, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; +use crate::{ffi, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; use std::borrow::Cow; use std::ffi::OsString; use std::path::{Path, PathBuf}; // See osstr.rs for why there's no FromPyObject impl for &Path -impl FromPyObject<'_> for PathBuf { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for PathBuf { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { // We use os.fspath to get the underlying path as bytes or str let path = unsafe { ffi::PyOS_FSPath(ob.as_ptr()).assume_owned_or_err(ob.py())? }; Ok(path.extract::()?.into()) diff --git a/src/conversions/std/set.rs b/src/conversions/std/set.rs index 4d4073101a0..5ec48a08e20 100644 --- a/src/conversions/std/set.rs +++ b/src/conversions/std/set.rs @@ -4,14 +4,13 @@ use std::{cmp, collections, hash}; use crate::inspect::types::TypeInfo; use crate::{ conversion::IntoPyObject, - instance::Bound, types::{ any::PyAnyMethods, frozenset::PyFrozenSetMethods, set::{try_new_from_iter, PySetMethods}, PyFrozenSet, PySet, }, - FromPyObject, PyAny, PyErr, PyResult, Python, + Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python, }; impl<'py, K, S> IntoPyObject<'py> for collections::HashSet @@ -53,12 +52,12 @@ where } } -impl<'py, K, S> FromPyObject<'py> for collections::HashSet +impl<'py, K, S> FromPyObject<'_, 'py> for collections::HashSet where - K: FromPyObject<'py> + cmp::Eq + hash::Hash, + K: for<'a> FromPyObject<'a, 'py> + cmp::Eq + hash::Hash, S: hash::BuildHasher + Default, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { match ob.cast::() { Ok(set) => set.iter().map(|any| any.extract()).collect(), Err(err) => { @@ -114,11 +113,11 @@ where } } -impl<'py, K> FromPyObject<'py> for collections::BTreeSet +impl<'py, K> FromPyObject<'_, 'py> for collections::BTreeSet where - K: FromPyObject<'py> + cmp::Ord, + K: for<'a> FromPyObject<'a, 'py> + cmp::Ord, { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { match ob.cast::() { Ok(set) => set.iter().map(|any| any.extract()).collect(), Err(err) => { diff --git a/src/conversions/std/slice.rs b/src/conversions/std/slice.rs index dca72b8fdf7..796eb65e4f1 100644 --- a/src/conversions/std/slice.rs +++ b/src/conversions/std/slice.rs @@ -35,8 +35,8 @@ where } } -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a [u8] { - fn from_py_object_bound(obj: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a [u8] { + fn extract(obj: crate::Borrowed<'a, '_, PyAny>) -> PyResult { Ok(obj.cast::()?.as_bytes()) } @@ -51,8 +51,8 @@ impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a [u8] { /// If the source object is a `bytes` object, the `Cow` will be borrowed and /// pointing into the source object, and no copying or heap allocations will happen. /// If it is a `bytearray`, its contents will be copied to an owned `Cow`. -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, [u8]> { - fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a> crate::conversion::FromPyObject<'a, '_> for Cow<'a, [u8]> { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { if let Ok(bytes) = ob.cast::() { return Ok(Cow::Borrowed(bytes.as_bytes())); } diff --git a/src/conversions/std/string.rs b/src/conversions/std/string.rs index e4c804facd2..4ee826d8f7d 100644 --- a/src/conversions/std/string.rs +++ b/src/conversions/std/string.rs @@ -3,10 +3,8 @@ use std::{borrow::Cow, convert::Infallible}; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - conversion::IntoPyObject, - instance::Bound, - types::{string::PyStringMethods, PyString}, - FromPyObject, PyAny, PyResult, Python, + conversion::IntoPyObject, instance::Bound, types::PyString, Borrowed, FromPyObject, PyAny, + PyResult, Python, }; impl<'py> IntoPyObject<'py> for &str { @@ -161,11 +159,11 @@ impl<'py> IntoPyObject<'py> for &String { } #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str { +impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a str { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "str"; - fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { ob.cast::()?.to_str() } @@ -175,11 +173,11 @@ impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str { } } -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> { +impl<'a> crate::conversion::FromPyObject<'a, '_> for Cow<'a, str> { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "str"; - fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { ob.cast::()?.to_cow() } @@ -191,11 +189,11 @@ impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> { /// Allows extracting strings from Python objects. /// Accepts Python `str` and `unicode` objects. -impl FromPyObject<'_> for String { +impl FromPyObject<'_, '_> for String { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "str"; - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { obj.cast::()?.to_cow().map(Cow::into_owned) } @@ -205,11 +203,11 @@ impl FromPyObject<'_> for String { } } -impl FromPyObject<'_> for char { +impl FromPyObject<'_, '_> for char { #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "str"; - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let s = obj.cast::()?.to_cow()?; let mut iter = s.chars(); if let (Some(ch), None) = (iter.next(), iter.next()) { diff --git a/src/conversions/std/time.rs b/src/conversions/std/time.rs index d2e2ed7c237..4a750d2332c 100644 --- a/src/conversions/std/time.rs +++ b/src/conversions/std/time.rs @@ -12,8 +12,8 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; const SECONDS_PER_DAY: u64 = 24 * 60 * 60; -impl FromPyObject<'_> for Duration { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Duration { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let delta = obj.cast::()?; #[cfg(not(Py_LIMITED_API))] let (days, seconds, microseconds) = { @@ -87,8 +87,8 @@ impl<'py> IntoPyObject<'py> for &Duration { // // TODO: it might be nice to investigate using timestamps anyway, at least when the datetime is a safe range. -impl FromPyObject<'_> for SystemTime { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for SystemTime { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let duration_since_unix_epoch: Duration = obj.sub(unix_epoch_py(obj.py())?)?.extract()?; UNIX_EPOCH .checked_add(duration_since_unix_epoch) diff --git a/src/conversions/time.rs b/src/conversions/time.rs index d9161f9553a..a20346a2d9d 100644 --- a/src/conversions/time.rs +++ b/src/conversions/time.rs @@ -58,7 +58,7 @@ use crate::types::datetime::{PyDateAccess, PyDeltaAccess}; use crate::types::{PyAnyMethods, PyDate, PyDateTime, PyDelta, PyNone, PyTime, PyTzInfo}; #[cfg(not(Py_LIMITED_API))] use crate::types::{PyTimeAccess, PyTzInfoAccess}; -use crate::{Bound, FromPyObject, IntoPyObject, PyAny, PyErr, PyResult, Python}; +use crate::{Borrowed, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, PyResult, Python}; use time::{ Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, }; @@ -180,8 +180,8 @@ impl<'py> IntoPyObject<'py> for Duration { } } -impl FromPyObject<'_> for Duration { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Duration { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { #[cfg(not(Py_LIMITED_API))] let (days, seconds, microseconds) = { let delta = ob.cast::()?; @@ -223,8 +223,8 @@ impl<'py> IntoPyObject<'py> for Date { } } -impl FromPyObject<'_> for Date { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for Date { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let (year, month, day) = { #[cfg(not(Py_LIMITED_API))] { @@ -264,8 +264,8 @@ impl<'py> IntoPyObject<'py> for Time { } } -impl FromPyObject<'_> for Time { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult where A: Array, - A::Item: for<'a> FromPyObject<'a, 'py>, + A::Item: FromPyObjectOwned<'py>, { fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { if obj.is_instance_of::() { @@ -92,7 +92,7 @@ where fn extract_sequence<'py, A>(obj: Borrowed<'_, 'py, PyAny>) -> PyResult> where A: Array, - A::Item: for<'a> FromPyObject<'a, 'py>, + A::Item: FromPyObjectOwned<'py>, { // Types that pass `PySequence_Check` usually implement enough of the sequence protocol // to support this function and if not, we will only fail extraction safely. diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index c716f69b103..2c806fb5ee7 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -1,4 +1,4 @@ -use crate::conversion::IntoPyObject; +use crate::conversion::{FromPyObjectOwned, IntoPyObject}; use crate::types::any::PyAnyMethods; use crate::types::PySequence; use crate::{err::DowncastError, ffi, FromPyObject, PyAny, PyResult, Python}; @@ -38,7 +38,7 @@ where impl<'py, T, const N: usize> FromPyObject<'_, 'py> for [T; N] where - T: for<'a> FromPyObject<'a, 'py>, + T: FromPyObjectOwned<'py>, { fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { create_array_from_obj(obj) @@ -47,7 +47,7 @@ where fn create_array_from_obj<'py, T, const N: usize>(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<[T; N]> where - T: for<'a> FromPyObject<'a, 'py>, + T: FromPyObjectOwned<'py>, { // Types that pass `PySequence_Check` usually implement enough of the sequence protocol // to support this function and if not, we will only fail extraction safely. diff --git a/src/conversions/std/map.rs b/src/conversions/std/map.rs index 8646f81b8bc..a3b760361e9 100644 --- a/src/conversions/std/map.rs +++ b/src/conversions/std/map.rs @@ -3,7 +3,7 @@ use std::{cmp, collections, hash}; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - conversion::IntoPyObject, + conversion::{FromPyObjectOwned, IntoPyObject}, instance::Bound, types::{any::PyAnyMethods, dict::PyDictMethods, PyDict}, Borrowed, FromPyObject, PyAny, PyErr, Python, @@ -109,8 +109,8 @@ where impl<'py, K, V, S> FromPyObject<'_, 'py> for collections::HashMap where - K: for<'a> FromPyObject<'a, 'py> + cmp::Eq + hash::Hash, - V: for<'a> FromPyObject<'a, 'py>, + K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, + V: FromPyObjectOwned<'py>, S: hash::BuildHasher + Default, { fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { @@ -130,8 +130,8 @@ where impl<'py, K, V> FromPyObject<'_, 'py> for collections::BTreeMap where - K: for<'a> FromPyObject<'a, 'py> + cmp::Ord, - V: for<'a> FromPyObject<'a, 'py>, + K: FromPyObjectOwned<'py> + cmp::Ord, + V: FromPyObjectOwned<'py>, { fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; diff --git a/src/conversions/std/set.rs b/src/conversions/std/set.rs index 5ec48a08e20..0c1a90ec948 100644 --- a/src/conversions/std/set.rs +++ b/src/conversions/std/set.rs @@ -3,7 +3,7 @@ use std::{cmp, collections, hash}; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - conversion::IntoPyObject, + conversion::{FromPyObjectOwned, IntoPyObject}, types::{ any::PyAnyMethods, frozenset::PyFrozenSetMethods, @@ -54,7 +54,7 @@ where impl<'py, K, S> FromPyObject<'_, 'py> for collections::HashSet where - K: for<'a> FromPyObject<'a, 'py> + cmp::Eq + hash::Hash, + K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, S: hash::BuildHasher + Default, { fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { @@ -115,7 +115,7 @@ where impl<'py, K> FromPyObject<'_, 'py> for collections::BTreeSet where - K: for<'a> FromPyObject<'a, 'py> + cmp::Ord, + K: FromPyObjectOwned<'py> + cmp::Ord, { fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { match ob.cast::() { diff --git a/src/prelude.rs b/src/prelude.rs index 31cf4b11b71..ccde0c902c3 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -8,7 +8,7 @@ //! use pyo3::prelude::*; //! ``` -pub use crate::conversion::{FromPyObject, IntoPyObject}; +pub use crate::conversion::{FromPyObject, FromPyObjectOwned, IntoPyObject}; pub use crate::err::{PyErr, PyResult}; #[allow(deprecated)] pub use crate::instance::PyObject; diff --git a/src/types/sequence.rs b/src/types/sequence.rs index 6d9fd8d26e4..e4259ac4305 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -1,3 +1,4 @@ +use crate::conversion::FromPyObjectOwned; use crate::err::{self, DowncastError, PyErr, PyResult}; use crate::exceptions::PyTypeError; use crate::ffi_ptr_ext::FfiPtrExt; @@ -333,7 +334,7 @@ impl<'py> PySequenceMethods<'py> for Bound<'py, PySequence> { impl<'py, T> FromPyObject<'_, 'py> for Vec where - T: for<'a> FromPyObject<'a, 'py>, + T: FromPyObjectOwned<'py>, { fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { if obj.is_instance_of::() { @@ -350,7 +351,7 @@ where fn extract_sequence<'py, T>(obj: Borrowed<'_, 'py, PyAny>) -> PyResult> where - T: for<'a> FromPyObject<'a, 'py>, + T: FromPyObjectOwned<'py>, { // Types that pass `PySequence_Check` usually implement enough of the sequence protocol // to support this function and if not, we will only fail extraction safely. From a385204b0f2e3e4f87deb15a90aaba52cd617738 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Mon, 29 Jul 2024 20:13:38 +0200 Subject: [PATCH 04/11] add newsfragments --- newsfragments/4390.added.md | 2 ++ newsfragments/4390.changed.md | 3 +++ newsfragments/4390.removed.md | 1 + 3 files changed, 6 insertions(+) create mode 100644 newsfragments/4390.added.md create mode 100644 newsfragments/4390.changed.md create mode 100644 newsfragments/4390.removed.md diff --git a/newsfragments/4390.added.md b/newsfragments/4390.added.md new file mode 100644 index 00000000000..c234b0b6547 --- /dev/null +++ b/newsfragments/4390.added.md @@ -0,0 +1,2 @@ +added `FromPyObjectOwned` as more convenient trait bound +added `Borrowed::extract`, same as `PyAnyMethods::extract`, but does not restrict the lifetime by deref \ No newline at end of file diff --git a/newsfragments/4390.changed.md b/newsfragments/4390.changed.md new file mode 100644 index 00000000000..50c9ffb7fec --- /dev/null +++ b/newsfragments/4390.changed.md @@ -0,0 +1,3 @@ +added second lifetime to `FromPyObject` +reintroduced `extract` method +deprecated `extract_bound` method \ No newline at end of file diff --git a/newsfragments/4390.removed.md b/newsfragments/4390.removed.md new file mode 100644 index 00000000000..a40ee2db268 --- /dev/null +++ b/newsfragments/4390.removed.md @@ -0,0 +1 @@ +removed `FromPyObjectBound` \ No newline at end of file From 09c53694b0b281060c524339f380dd42a99da13a Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sat, 3 Aug 2024 19:30:59 +0200 Subject: [PATCH 05/11] use `FromPyObjectOwned` in derive macro --- pyo3-macros-backend/src/frompyobject.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index a7733ce58e1..4300d179629 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -543,7 +543,7 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result { let gen_ident = ¶m.ident; where_clause .predicates - .push(parse_quote!(#gen_ident: for<'_a> #pyo3_path::FromPyObject<'_a, 'py>)) + .push(parse_quote!(#gen_ident: #pyo3_path::conversion::FromPyObjectOwned<#lt_param>)) } let derives = match &tokens.data { From 0d06a0ef77dd9df13ae29abc5ae43abce51a5730 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sat, 3 Aug 2024 19:25:20 +0200 Subject: [PATCH 06/11] apply doc suggestions Co-authored-by: David Hewitt --- guide/src/migration.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/guide/src/migration.md b/guide/src/migration.md index 6a2716e1002..292db382a87 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -13,14 +13,14 @@ With the removal of the `gil-ref` API it is now possible to fully split the Pyth GIL lifetime. `FromPyObject` now takes an additional lifetime `'a` describing the input lifetime. The argument -type of the `extract` method changed from `&Bound<'_, PyAny>` to `Borrowed<'_, '_, PyAny>`. This was -done to lift the implicit restriction `'py: 'a` due to the reference type. `extract_bound` with it's +type of the `extract` method changed from `&Bound<'py, PyAny>` to `Borrowed<'a, 'py, PyAny>`. This was +done because `&'a Bound<'py, PyAny>` would have an implicit restriction `'py: 'a` due to the reference type. `extract_bound` with its old signature is deprecated, but still available during migration. This new form was partly implemented already in 0.22 using the internal `FromPyObjectBound` trait and is now extended to all types. -Most implementation can just add an elided lifetime to migrate, +Most implementations can just add an elided lifetime to migrate. Before: ```rust,ignore @@ -42,7 +42,7 @@ impl<'py> FromPyObject<'_, 'py> for IpAddr { } ``` -but occasually more steps are neccessary. For generic types, the bounds need to be adjusted. The +Occasionally, more steps are necessary. For generic types, the bounds need to be adjusted. The correct bound depends on how the type is used. For simple wrapper types usually it's possible to just forward the bound. From 828159be16c14a78b3cb5850c9295df79627a8bd Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sun, 4 Aug 2024 00:14:03 +0200 Subject: [PATCH 07/11] improve `FromPyObject` docs --- src/conversion.rs | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/conversion.rs b/src/conversion.rs index f472fcef45a..ca8c3168004 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -247,7 +247,8 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// Extract a type from a Python object. /// /// -/// Normal usage is through the `extract` methods on [`Bound`] and [`Py`], which forward to this trait. +/// Normal usage is through the `extract` methods on [`Bound`], [`Borrowed`] and +/// [`Py`], which forward to this trait. /// /// # Examples /// @@ -271,15 +272,38 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// # } /// ``` /// -/// Note: depending on the implementation, the lifetime of the extracted result may -/// depend on the lifetime of the `obj` or the `prepared` variable. +/// Note: depending on the implementation, the extracted result may depend on +/// the Python lifetime `'py` or the input lifetime `'a` of `obj`. /// -/// For example, when extracting `&str` from a Python byte string, the resulting string slice will -/// point to the existing string data (lifetime: `'py`). -/// On the other hand, when extracting `&str` from a Python Unicode string, the preparation step -/// will convert the string to UTF-8, and the resulting string slice will have lifetime `'prepared`. -/// Since which case applies depends on the runtime type of the Python object, -/// both the `obj` and `prepared` variables must outlive the resulting string slice. +/// For example, when extracting a [`Cow<'a, str>`] the result may or may not +/// borrow from the input lifetime `'a`. The behavior depends on the runtime +/// type of the Python object. For a Python byte string, the existing string +/// data can be borrowed (lifetime: `'a`) into a [`Cow::Borrowed`]. For a Python +/// Unicode string, the data may have to be reencoded to UTF-8, and copied into +/// a [`Cow::Owned`]. It does _not_ depend on the Python lifetime `'py` +/// +/// An example of a type depending on the Python lifetime `'py` would be +/// [`Bound<'py, PyString>`]. This type holds the invariant of beeing allowed to +/// interact with the Python interpreter, so it inherits the Python lifetime +/// from the input. It is however _not_ tied to the input lifetime `'a` and can +/// be passed around independently of `obj`. +/// +/// Special care needs to be taken for collection types, for example [`PyList`]. +/// In contrast to a Rust's [`Vec`] a Python list will not hand out references +/// tied to its own lifetime, but "owned" references independent of it. (Similar +/// to [`Vec>`] where you clone the [`Arc`] out). This makes it +/// impossible to collect borrowed types in a collection, since they would not +/// borrow from the original input list, but the much shorter lived element +/// reference. This restriction is represented in PyO3 using +/// [`FromPyObjectOwned`]. It is used by [`FromPyObject`] implementations on +/// collection types to specify it can only collect types which do _not_ borrow +/// from the input. +/// +/// [`Cow<'a, str>`]: std::borrow::Cow +/// [`Cow::Borrowed`]: std::borrow::Cow::Borrowed +/// [`Cow::Owned`]: std::borrow::Cow::Owned +/// [`PyList`]: crate::types::PyList +/// [`Arc`]: std::sync::Arc pub trait FromPyObject<'a, 'py>: Sized { /// Provides the type hint information for this type when it appears as an argument. /// @@ -292,7 +316,7 @@ pub trait FromPyObject<'a, 'py>: Sized { /// /// Users are advised against calling this method directly: instead, use this via /// [`Bound<'_, PyAny>::extract`](crate::types::any::PyAnyMethods::extract) or [`Py::extract`]. - fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult; + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult; /// Extracts the type hint information for this type when it appears as an argument. /// From 6473af1109c7a1eea520c0674d11000a5529fa22 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sun, 4 Aug 2024 19:19:08 +0200 Subject: [PATCH 08/11] `FromPyObject` docs take 2 --- src/conversion.rs | 60 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/conversion.rs b/src/conversion.rs index ca8c3168004..dd83e0b7b82 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -247,8 +247,8 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// Extract a type from a Python object. /// /// -/// Normal usage is through the `extract` methods on [`Bound`], [`Borrowed`] and -/// [`Py`], which forward to this trait. +/// Normal usage is through the `extract` methods on [`Bound`], [`Borrowed`] and [`Py`], which +/// forward to this trait. /// /// # Examples /// @@ -272,38 +272,27 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// # } /// ``` /// -/// Note: depending on the implementation, the extracted result may depend on -/// the Python lifetime `'py` or the input lifetime `'a` of `obj`. +/// Note: Depending on the Python version and implementation, some [`FromPyObject`] implementations +/// may produce a result that borrows into the Python type. This is described by the input lifetime +/// `'a` of `obj`. /// -/// For example, when extracting a [`Cow<'a, str>`] the result may or may not -/// borrow from the input lifetime `'a`. The behavior depends on the runtime -/// type of the Python object. For a Python byte string, the existing string -/// data can be borrowed (lifetime: `'a`) into a [`Cow::Borrowed`]. For a Python -/// Unicode string, the data may have to be reencoded to UTF-8, and copied into -/// a [`Cow::Owned`]. It does _not_ depend on the Python lifetime `'py` +/// Types that must not borrow from the input can use [`FromPyObjectOwned`] as a restriction. This +/// is most often the case for collection types. See its documentation for more details. /// -/// An example of a type depending on the Python lifetime `'py` would be -/// [`Bound<'py, PyString>`]. This type holds the invariant of beeing allowed to -/// interact with the Python interpreter, so it inherits the Python lifetime -/// from the input. It is however _not_ tied to the input lifetime `'a` and can -/// be passed around independently of `obj`. +/// # Details +/// [`Cow<'a, str>`] is an example of an output type that may or may not borrow from the input +/// lifetime `'a`. Which variant will be produced depends on the runtime type of the Python object. +/// For a Python byte string, the existing string data can be borrowed for `'a` into a +/// [`Cow::Borrowed`]. For a Python Unicode string, the data may have to be reencoded to UTF-8, and +/// copied into a [`Cow::Owned`]. It does _not_ depend on the Python lifetime `'py`. /// -/// Special care needs to be taken for collection types, for example [`PyList`]. -/// In contrast to a Rust's [`Vec`] a Python list will not hand out references -/// tied to its own lifetime, but "owned" references independent of it. (Similar -/// to [`Vec>`] where you clone the [`Arc`] out). This makes it -/// impossible to collect borrowed types in a collection, since they would not -/// borrow from the original input list, but the much shorter lived element -/// reference. This restriction is represented in PyO3 using -/// [`FromPyObjectOwned`]. It is used by [`FromPyObject`] implementations on -/// collection types to specify it can only collect types which do _not_ borrow -/// from the input. +/// The output type may also depend on the Python lifetime `'py`. This allows the output type to +/// keep interacting with the Python interpreter. See also [`Bound<'py, T>`]. /// /// [`Cow<'a, str>`]: std::borrow::Cow /// [`Cow::Borrowed`]: std::borrow::Cow::Borrowed /// [`Cow::Owned`]: std::borrow::Cow::Owned -/// [`PyList`]: crate::types::PyList -/// [`Arc`]: std::sync::Arc + pub trait FromPyObject<'a, 'py>: Sized { /// Provides the type hint information for this type when it appears as an argument. /// @@ -332,11 +321,17 @@ pub trait FromPyObject<'a, 'py>: Sized { } } -/// A data structure that can be extracted without borrowing any data from the input +/// A data structure that can be extracted without borrowing any data from the input. +/// +/// This is primarily useful for trait bounds. For example a [`FromPyObject`] implementation of a +/// wrapper type may be able to borrow data from the input, but a [`FromPyObject`] implementation of +/// a collection type may only extract owned data. /// -/// This is primarily useful for trait bounds. For example a `FromPyObject` implementation of a -/// wrapper type may be able to borrow data from the input, but a `FromPyObject` implementation of a -/// collection type may only extract owned data. +/// For example [`PyList`] will not hand out references tied to its own lifetime, but "owned" +/// references independent of it. (Similar to [`Vec>`] where you clone the [`Arc`] out). +/// This makes it impossible to collect borrowed types in a collection, since they would not borrow +/// from the original [`PyList`], but the much shorter lived element reference. See the example +/// below. /// /// ```,no_run /// # use pyo3::prelude::*; @@ -369,6 +364,9 @@ pub trait FromPyObject<'a, 'py>: Sized { /// } /// } /// ``` +/// +/// [`PyList`]: crate::types::PyList +/// [`Arc`]: std::sync::Arc pub trait FromPyObjectOwned<'py>: for<'a> FromPyObject<'a, 'py> {} impl<'py, T> FromPyObjectOwned<'py> for T where T: for<'a> FromPyObject<'a, 'py> {} From 312e003e439a33c77f2621b08c1ff191bff4413e Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sun, 3 Nov 2024 21:33:21 +0100 Subject: [PATCH 09/11] fix clippy --- src/conversion.rs | 1 - src/conversions/std/osstr.rs | 1 + src/conversions/std/path.rs | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conversion.rs b/src/conversion.rs index dd83e0b7b82..c98540ff410 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -292,7 +292,6 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// [`Cow<'a, str>`]: std::borrow::Cow /// [`Cow::Borrowed`]: std::borrow::Cow::Borrowed /// [`Cow::Owned`]: std::borrow::Cow::Owned - pub trait FromPyObject<'a, 'py>: Sized { /// Provides the type hint information for this type when it appears as an argument. /// diff --git a/src/conversions/std/osstr.rs b/src/conversions/std/osstr.rs index df38f9f9b48..1c396dcf81b 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -179,6 +179,7 @@ mod tests { #[cfg(not(windows))] fn test_non_utf8_conversion() { Python::attach(|py| { + use crate::types::PyAnyMethods; #[cfg(not(target_os = "wasi"))] use std::os::unix::ffi::OsStrExt; #[cfg(target_os = "wasi")] diff --git a/src/conversions/std/path.rs b/src/conversions/std/path.rs index 4ba21fe098b..212d0d2ebcf 100644 --- a/src/conversions/std/path.rs +++ b/src/conversions/std/path.rs @@ -98,6 +98,7 @@ mod tests { #[cfg(not(windows))] fn test_non_utf8_conversion() { Python::attach(|py| { + use crate::types::PyAnyMethods; use std::ffi::OsStr; #[cfg(not(target_os = "wasi"))] use std::os::unix::ffi::OsStrExt; From a744920b13a8be25ded79c305d375c2b30f65243 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sun, 3 Nov 2024 21:26:00 +0100 Subject: [PATCH 10/11] add `FromPyObject::Error` --- guide/src/class.md | 2 ++ guide/src/migration.md | 10 +++++--- pyo3-macros-backend/src/frompyobject.rs | 3 ++- pyo3-macros-backend/src/pyclass.rs | 3 +++ src/buffer.rs | 6 +++-- src/conversion.rs | 27 +++++++++++++++------ src/conversions/bigdecimal.rs | 2 ++ src/conversions/bytes.rs | 8 ++++--- src/conversions/chrono.rs | 32 ++++++++++++++++++------- src/conversions/chrono_tz.rs | 6 +++-- src/conversions/either.rs | 6 +++-- src/conversions/hashbrown.rs | 19 ++++++++++++--- src/conversions/indexmap.rs | 9 +++++-- src/conversions/jiff.rs | 22 +++++++++++++++-- src/conversions/num_bigint.rs | 8 +++++-- src/conversions/num_complex.rs | 6 +++-- src/conversions/num_rational.rs | 4 +++- src/conversions/ordered_float.rs | 10 ++++---- src/conversions/rust_decimal.rs | 4 +++- src/conversions/smallvec.rs | 6 +++-- src/conversions/std/array.rs | 9 +++++-- src/conversions/std/cell.rs | 6 +++-- src/conversions/std/ipaddr.rs | 6 +++-- src/conversions/std/map.rs | 16 ++++++++++--- src/conversions/std/num.rs | 28 ++++++++++++++++------ src/conversions/std/option.rs | 7 +++--- src/conversions/std/osstr.rs | 6 +++-- src/conversions/std/path.rs | 6 +++-- src/conversions/std/set.rs | 30 +++++++++++++++++------ src/conversions/std/slice.rs | 14 +++++++---- src/conversions/std/string.rs | 18 ++++++++++---- src/conversions/std/time.rs | 8 +++++-- src/conversions/time.rs | 28 ++++++++++++++++------ src/conversions/uuid.rs | 2 ++ src/impl_/extract_argument.rs | 31 ++++++++++++++++-------- src/impl_/frompyobject.rs | 4 ++-- src/instance.rs | 26 +++++++++++--------- src/pybacked.rs | 17 +++++++++---- src/pyclass/guard.rs | 10 +++++--- src/types/any.rs | 4 ++-- src/types/boolobject.rs | 7 ++++-- src/types/float.rs | 13 ++++++---- src/types/sequence.rs | 6 +++-- src/types/tuple.rs | 8 ++++--- 44 files changed, 362 insertions(+), 141 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 911d1e7f72b..942effed147 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -1410,6 +1410,7 @@ impl pyo3::PyClass for MyClass { impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder MyClass { type Holder = ::std::option::Option>; + type Error = pyo3::PyErr; #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "MyClass"; @@ -1422,6 +1423,7 @@ impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'ho impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder mut MyClass { type Holder = ::std::option::Option>; + type Error = pyo3::PyErr; #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "MyClass"; diff --git a/guide/src/migration.md b/guide/src/migration.md index 292db382a87..01af5be325f 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -70,7 +70,9 @@ impl<'a, 'py, T> FromPyObject<'a, 'py> for MyWrapper where T: FromPyObject<'a, 'py> { - fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { + type Error = T::Error; + + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result { obj.extract().map(MyWrapper) } } @@ -108,10 +110,12 @@ where T: FromPyObjectOwned<'py> // 👈 can only extract owned values, because each `item` below // is a temporary short lived owned reference { - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { let mut v = MyVec(Vec::new()); for item in obj.try_iter()? { - v.0.push(item?.extract::()?); + v.0.push(item?.extract::().map_err(Into::into)?); } Ok(v) } diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index 4300d179629..27b83673982 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -606,7 +606,8 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result { Ok(quote!( #[automatically_derived] impl #impl_generics #pyo3_path::FromPyObject<'_, #lt_param> for #ident #ty_generics #where_clause { - fn extract(obj: #pyo3_path::Borrowed<'_, #lt_param, #pyo3_path::PyAny>) -> #pyo3_path::PyResult { + type Error = #pyo3_path::PyErr; + fn extract(obj: #pyo3_path::Borrowed<'_, #lt_param, #pyo3_path::PyAny>) -> ::std::result::Result { let obj: &#pyo3_path::Bound<'_, _> = &*obj; #derives } diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index ee225399fe3..91f9117fc9e 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -2327,6 +2327,7 @@ impl<'a> PyClassImplsBuilder<'a> { impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder #cls { type Holder = ::std::option::Option<#pyo3_path::PyClassGuard<'a, #cls>>; + type Error = #pyo3_path::PyErr; #input_type @@ -2341,6 +2342,7 @@ impl<'a> PyClassImplsBuilder<'a> { impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder #cls { type Holder = ::std::option::Option<#pyo3_path::PyClassGuard<'a, #cls>>; + type Error = #pyo3_path::PyErr; #input_type @@ -2353,6 +2355,7 @@ impl<'a> PyClassImplsBuilder<'a> { impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder mut #cls { type Holder = ::std::option::Option<#pyo3_path::PyClassGuardMut<'a, #cls>>; + type Error =#pyo3_path::PyErr; #input_type diff --git a/src/buffer.rs b/src/buffer.rs index 6d1d8683fcb..bdd0ee214fe 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -19,7 +19,7 @@ //! `PyBuffer` implementation use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python}; -use crate::{Borrowed, Bound}; +use crate::{Borrowed, Bound, PyErr}; use std::ffi::{ c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort, c_void, @@ -186,7 +186,9 @@ pub unsafe trait Element: Copy { } impl FromPyObject<'_, '_> for PyBuffer { - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult> { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result, Self::Error> { Self::get(&obj) } } diff --git a/src/conversion.rs b/src/conversion.rs index c98540ff410..0ee10f2a05d 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -293,6 +293,9 @@ impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {} /// [`Cow::Borrowed`]: std::borrow::Cow::Borrowed /// [`Cow::Owned`]: std::borrow::Cow::Owned pub trait FromPyObject<'a, 'py>: Sized { + /// The type returned in the event of a conversion error. + type Error: Into; + /// Provides the type hint information for this type when it appears as an argument. /// /// For example, `Vec` would be `collections.abc.Sequence[int]`. @@ -304,7 +307,7 @@ pub trait FromPyObject<'a, 'py>: Sized { /// /// Users are advised against calling this method directly: instead, use this via /// [`Bound<'_, PyAny>::extract`](crate::types::any::PyAnyMethods::extract) or [`Py::extract`]. - fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult; + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result; /// Extracts the type hint information for this type when it appears as an argument. /// @@ -341,7 +344,9 @@ pub trait FromPyObject<'a, 'py>: Sized { /// where /// T: FromPyObject<'a, 'py> /// { -/// fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { +/// type Error = T::Error; +/// +/// fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result { /// obj.extract().map(MyWrapper) /// } /// } @@ -354,10 +359,12 @@ pub trait FromPyObject<'a, 'py>: Sized { /// T: FromPyObjectOwned<'py> // 👈 can only extract owned values, because each `item` below /// // is a temporary short lived owned reference /// { -/// fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { +/// type Error = PyErr; +/// +/// fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { /// let mut v = MyVec(Vec::new()); /// for item in obj.try_iter()? { -/// v.0.push(item?.extract::()?); +/// v.0.push(item?.extract::().map_err(Into::into)?); /// } /// Ok(v) /// } @@ -373,10 +380,12 @@ impl FromPyObject<'_, '_> for T where T: PyClass + Clone, { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = ::TYPE_NAME; - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { let bound = obj.cast::()?; Ok(bound.try_borrow()?.clone()) } @@ -386,10 +395,12 @@ impl<'py, T> FromPyObject<'_, 'py> for PyRef<'py, T> where T: PyClass, { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = ::TYPE_NAME; - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { obj.cast::()?.try_borrow().map_err(Into::into) } } @@ -398,10 +409,12 @@ impl<'py, T> FromPyObject<'_, 'py> for PyRefMut<'py, T> where T: PyClass, { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = ::TYPE_NAME; - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { obj.cast::()?.try_borrow_mut().map_err(Into::into) } } diff --git a/src/conversions/bigdecimal.rs b/src/conversions/bigdecimal.rs index ffd77ff5621..991c5567cce 100644 --- a/src/conversions/bigdecimal.rs +++ b/src/conversions/bigdecimal.rs @@ -72,6 +72,8 @@ fn get_invalid_operation_error_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType } impl FromPyObject<'_, '_> for BigDecimal { + type Error = PyErr; + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let py_str = &obj.str()?; let rs_str = &py_str.to_cow()?; diff --git a/src/conversions/bytes.rs b/src/conversions/bytes.rs index 934a3bc1444..0aba7409578 100644 --- a/src/conversions/bytes.rs +++ b/src/conversions/bytes.rs @@ -68,10 +68,12 @@ use crate::conversion::IntoPyObject; use crate::instance::Bound; use crate::pybacked::PyBackedBytes; use crate::types::PyBytes; -use crate::{Borrowed, FromPyObject, PyAny, PyErr, PyResult, Python}; +use crate::{Borrowed, DowncastError, FromPyObject, PyAny, PyErr, Python}; -impl FromPyObject<'_, '_> for Bytes { - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { +impl<'a, 'py> FromPyObject<'a, 'py> for Bytes { + type Error = DowncastError<'a, 'py>; + + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result { Ok(Bytes::from_owner(obj.extract::()?)) } } diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index b841c9804ed..d250cfc03d5 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -109,7 +109,9 @@ impl<'py> IntoPyObject<'py> for &Duration { } impl FromPyObject<'_, '_> for Duration { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { let delta = ob.cast::()?; // Python size are much lower than rust size so we do not need bound checks. // 0 <= microseconds < 1000000 @@ -163,7 +165,9 @@ impl<'py> IntoPyObject<'py> for &NaiveDate { } impl FromPyObject<'_, '_> for NaiveDate { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { let date = &*ob.cast::()?; py_date_to_naive_date(date) } @@ -205,7 +209,9 @@ impl<'py> IntoPyObject<'py> for &NaiveTime { } impl FromPyObject<'_, '_> for NaiveTime { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { let time = &*ob.cast::()?; py_time_to_naive_time(time) } @@ -248,7 +254,9 @@ impl<'py> IntoPyObject<'py> for &NaiveDateTime { } impl FromPyObject<'_, '_> for NaiveDateTime { - fn extract(dt: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(dt: Borrowed<'_, '_, PyAny>) -> Result { let dt = &*dt.cast::()?; // If the user tries to convert a timezone aware datetime into a naive one, @@ -328,12 +336,14 @@ impl<'py, Tz> FromPyObject<'_, 'py> for DateTime where Tz: TimeZone + FromPyObjectOwned<'py>, { - fn extract(dt: Borrowed<'_, 'py, PyAny>) -> PyResult> { + type Error = PyErr; + + fn extract(dt: Borrowed<'_, 'py, PyAny>) -> Result { let dt = &*dt.cast::()?; let tzinfo = dt.get_tzinfo(); let tz = if let Some(tzinfo) = tzinfo { - tzinfo.extract()? + tzinfo.extract().map_err(Into::into)? } else { return Err(PyTypeError::new_err( "expected a datetime with non-None tzinfo", @@ -386,11 +396,13 @@ impl<'py> IntoPyObject<'py> for &FixedOffset { } impl FromPyObject<'_, '_> for FixedOffset { + type Error = PyErr; + /// Convert python tzinfo to rust [`FixedOffset`]. /// /// Note that the conversion will result in precision lost in microseconds as chrono offset /// does not supports microseconds. - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { let ob = ob.cast::()?; // Passing Python's None to the `utcoffset` function will only @@ -435,7 +447,9 @@ impl<'py> IntoPyObject<'py> for &Utc { } impl FromPyObject<'_, '_> for Utc { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { let py_utc = PyTzInfo::utc(ob.py())?; if ob.eq(py_utc)? { Ok(Utc) @@ -479,6 +493,8 @@ impl<'py> IntoPyObject<'py> for &Local { #[cfg(feature = "chrono-local")] impl FromPyObject<'_, '_> for Local { + type Error = PyErr; + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { let local_tz = Local.into_pyobject(ob.py())?; if ob.eq(local_tz)? { diff --git a/src/conversions/chrono_tz.rs b/src/conversions/chrono_tz.rs index bd652e3a25b..6926559c084 100644 --- a/src/conversions/chrono_tz.rs +++ b/src/conversions/chrono_tz.rs @@ -38,7 +38,7 @@ use crate::conversion::IntoPyObject; use crate::exceptions::PyValueError; use crate::pybacked::PyBackedStr; use crate::types::{any::PyAnyMethods, PyTzInfo}; -use crate::{intern, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python}; +use crate::{intern, Borrowed, Bound, FromPyObject, PyAny, PyErr, Python}; use chrono_tz::Tz; use std::str::FromStr; @@ -64,7 +64,9 @@ impl<'py> IntoPyObject<'py> for &Tz { } impl FromPyObject<'_, '_> for Tz { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { Tz::from_str( &ob.getattr(intern!(ob.py(), "key"))? .extract::()?, diff --git a/src/conversions/either.rs b/src/conversions/either.rs index 9b76ed0f9be..6a5acebae75 100644 --- a/src/conversions/either.rs +++ b/src/conversions/either.rs @@ -48,7 +48,7 @@ use crate::inspect::types::TypeInfo; use crate::{ exceptions::PyTypeError, Borrowed, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, PyAny, - PyErr, PyResult, Python, + PyErr, Python, }; use either::Either; @@ -94,8 +94,10 @@ where L: FromPyObject<'a, 'py>, R: FromPyObject<'a, 'py>, { + type Error = PyErr; + #[inline] - fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result { if let Ok(l) = obj.extract::() { Ok(Either::Left(l)) } else if let Ok(r) = obj.extract::() { diff --git a/src/conversions/hashbrown.rs b/src/conversions/hashbrown.rs index ddb4b71dfa9..85bc350999b 100644 --- a/src/conversions/hashbrown.rs +++ b/src/conversions/hashbrown.rs @@ -73,11 +73,16 @@ where V: FromPyObjectOwned<'py>, S: hash::BuildHasher + Default, { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default()); for (k, v) in dict.iter() { - ret.insert(k.extract()?, v.extract()?); + ret.insert( + k.extract().map_err(Into::into)?, + v.extract().map_err(Into::into)?, + ); } Ok(ret) } @@ -116,12 +121,20 @@ where K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, S: hash::BuildHasher + Default, { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { match ob.cast::() { - Ok(set) => set.iter().map(|any| any.extract()).collect(), + Ok(set) => set + .iter() + .map(|any| any.extract().map_err(Into::into)) + .collect(), Err(err) => { if let Ok(frozen_set) = ob.cast::() { - frozen_set.iter().map(|any| any.extract()).collect() + frozen_set + .iter() + .map(|any| any.extract().map_err(Into::into)) + .collect() } else { Err(PyErr::from(err)) } diff --git a/src/conversions/indexmap.rs b/src/conversions/indexmap.rs index c8cf4da7724..89ef8172961 100644 --- a/src/conversions/indexmap.rs +++ b/src/conversions/indexmap.rs @@ -136,11 +136,16 @@ where V: FromPyObjectOwned<'py>, S: hash::BuildHasher + Default, { - fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; let mut ret = indexmap::IndexMap::with_capacity_and_hasher(dict.len(), S::default()); for (k, v) in dict.iter() { - ret.insert(k.extract()?, v.extract()?); + ret.insert( + k.extract().map_err(Into::into)?, + v.extract().map_err(Into::into)?, + ); } Ok(ret) } diff --git a/src/conversions/jiff.rs b/src/conversions/jiff.rs index 72ca06cd2fa..7ad84516f47 100644 --- a/src/conversions/jiff.rs +++ b/src/conversions/jiff.rs @@ -122,8 +122,10 @@ impl<'py> IntoPyObject<'py> for &Timestamp { } } -impl<'py> FromPyObject<'_, 'py> for Timestamp { - fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { +impl<'a, 'py> FromPyObject<'a, 'py> for Timestamp { + type Error = >::Error; + + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let zoned = ob.extract::()?; Ok(zoned.timestamp()) } @@ -155,6 +157,8 @@ impl<'py> IntoPyObject<'py> for &Date { } impl<'py> FromPyObject<'_, 'py> for Date { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let date = ob.cast::()?; @@ -207,6 +211,8 @@ impl<'py> IntoPyObject<'py> for &Time { } impl<'py> FromPyObject<'_, 'py> for Time { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let ob = ob.cast::()?; #[allow(clippy::explicit_auto_deref)] @@ -235,6 +241,8 @@ impl<'py> IntoPyObject<'py> for &DateTime { } impl<'py> FromPyObject<'_, 'py> for DateTime { + type Error = PyErr; + fn extract(dt: Borrowed<'_, 'py, PyAny>) -> PyResult { let dt = dt.cast::()?; let has_tzinfo = dt.get_tzinfo().is_some(); @@ -285,6 +293,8 @@ impl<'py> IntoPyObject<'py> for &Zoned { } impl<'py> FromPyObject<'_, 'py> for Zoned { + type Error = PyErr; + fn extract(dt: Borrowed<'_, 'py, PyAny>) -> PyResult { let dt = dt.cast::()?; @@ -343,6 +353,8 @@ impl<'py> IntoPyObject<'py> for &TimeZone { } impl<'py> FromPyObject<'_, 'py> for TimeZone { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let ob = ob.cast::()?; @@ -380,6 +392,8 @@ impl<'py> IntoPyObject<'py> for Offset { } impl<'py> FromPyObject<'_, 'py> for Offset { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let py = ob.py(); let ob = ob.cast::()?; @@ -428,6 +442,8 @@ impl<'py> IntoPyObject<'py> for SignedDuration { } impl<'py> FromPyObject<'_, 'py> for SignedDuration { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let delta = ob.cast::()?; @@ -453,6 +469,8 @@ impl<'py> FromPyObject<'_, 'py> for SignedDuration { } impl<'py> FromPyObject<'_, 'py> for Span { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { let duration = ob.extract::()?; Ok(duration.try_into()?) diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index ef63c35f24b..4384231c801 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -124,7 +124,9 @@ bigint_conversion!(BigInt, true, BigInt::to_signed_bytes_le); #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] impl<'py> FromPyObject<'_, 'py> for BigInt { - fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let py = ob.py(); // fast path - checking for subclass of `int` just checks a bit in the type object let num_owned: Py; @@ -172,7 +174,9 @@ impl<'py> FromPyObject<'_, 'py> for BigInt { #[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))] impl<'py> FromPyObject<'_, 'py> for BigUint { - fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let py = ob.py(); // fast path - checking for subclass of `int` just checks a bit in the type object let num_owned: Py; diff --git a/src/conversions/num_complex.rs b/src/conversions/num_complex.rs index df416f7a0fb..778680153fe 100644 --- a/src/conversions/num_complex.rs +++ b/src/conversions/num_complex.rs @@ -95,7 +95,7 @@ //! ``` use crate::{ ffi, ffi_ptr_ext::FfiPtrExt, types::PyComplex, Borrowed, Bound, FromPyObject, PyAny, PyErr, - PyResult, Python, + Python, }; use num_complex::Complex; use std::ffi::c_double; @@ -147,7 +147,9 @@ macro_rules! complex_conversion { #[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))] impl FromPyObject<'_, '_> for Complex<$float> { - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult> { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result, Self::Error> { #[cfg(not(any(Py_LIMITED_API, PyPy)))] unsafe { let val = ffi::PyComplex_AsCComplex(obj.as_ptr()); diff --git a/src/conversions/num_rational.rs b/src/conversions/num_rational.rs index 9a648de9ebe..c64a48323a2 100644 --- a/src/conversions/num_rational.rs +++ b/src/conversions/num_rational.rs @@ -63,7 +63,9 @@ fn get_fraction_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { macro_rules! rational_conversion { ($int: ty) => { impl<'py> FromPyObject<'_, 'py> for Ratio<$int> { - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { let py = obj.py(); let py_numerator_obj = obj.getattr(crate::intern!(py, "numerator"))?; let py_denominator_obj = obj.getattr(crate::intern!(py, "denominator"))?; diff --git a/src/conversions/ordered_float.rs b/src/conversions/ordered_float.rs index fd9c81ba7d7..71bcd29bca7 100644 --- a/src/conversions/ordered_float.rs +++ b/src/conversions/ordered_float.rs @@ -54,14 +54,16 @@ use crate::conversion::IntoPyObject; use crate::exceptions::PyValueError; use crate::types::PyFloat; -use crate::{Borrowed, Bound, FromPyObject, PyAny, PyResult, Python}; +use crate::{Borrowed, Bound, FromPyObject, PyAny, Python}; use ordered_float::{NotNan, OrderedFloat}; use std::convert::Infallible; macro_rules! float_conversions { ($wrapper:ident, $float_type:ty, $constructor:expr) => { - impl FromPyObject<'_, '_> for $wrapper<$float_type> { - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + impl<'a, 'py> FromPyObject<'a, 'py> for $wrapper<$float_type> { + type Error = <$float_type as FromPyObject<'a, 'py>>::Error; + + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result { let val: $float_type = obj.extract()?; $constructor(val) } @@ -295,7 +297,7 @@ mod test_ordered_float { Python::attach(|py| { let nan_py = py.eval(c_str!("float('nan')"), None, None).unwrap(); - let nan_rs: PyResult> = nan_py.extract(); + let nan_rs: Result, _> = nan_py.extract(); assert!(nan_rs.is_err()); }) diff --git a/src/conversions/rust_decimal.rs b/src/conversions/rust_decimal.rs index 60495561e1e..d67a076869b 100644 --- a/src/conversions/rust_decimal.rs +++ b/src/conversions/rust_decimal.rs @@ -60,7 +60,9 @@ use rust_decimal::Decimal; use std::str::FromStr; impl FromPyObject<'_, '_> for Decimal { - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { // use the string representation to not be lossy if let Ok(val) = obj.extract() { Ok(Decimal::new(val, 0)) diff --git a/src/conversions/smallvec.rs b/src/conversions/smallvec.rs index 6594cdd16f6..525a8845754 100644 --- a/src/conversions/smallvec.rs +++ b/src/conversions/smallvec.rs @@ -76,7 +76,9 @@ where A: Array, A::Item: FromPyObjectOwned<'py>, { - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { if obj.is_instance_of::() { return Err(PyTypeError::new_err("Can't extract `str` to `SmallVec`")); } @@ -106,7 +108,7 @@ where let mut sv = SmallVec::with_capacity(seq.len().unwrap_or(0)); for item in seq.try_iter()? { - sv.push(item?.extract::()?); + sv.push(item?.extract::().map_err(Into::into)?); } Ok(sv) } diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index 2c806fb5ee7..7da9f13dc8b 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -40,7 +40,9 @@ impl<'py, T, const N: usize> FromPyObject<'_, 'py> for [T; N] where T: FromPyObjectOwned<'py>, { - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { create_array_from_obj(obj) } } @@ -62,7 +64,10 @@ where if seq_len != N { return Err(invalid_sequence_length(N, seq_len)); } - array_try_from_fn(|idx| seq.get_item(idx).and_then(|any| any.extract())) + array_try_from_fn(|idx| { + seq.get_item(idx) + .and_then(|any| any.extract().map_err(Into::into)) + }) } // TODO use std::array::try_from_fn, if that stabilises: diff --git a/src/conversions/std/cell.rs b/src/conversions/std/cell.rs index f123caf4a6d..108df8031cd 100644 --- a/src/conversions/std/cell.rs +++ b/src/conversions/std/cell.rs @@ -1,6 +1,6 @@ use std::cell::Cell; -use crate::{conversion::IntoPyObject, Borrowed, FromPyObject, PyAny, PyResult, Python}; +use crate::{conversion::IntoPyObject, Borrowed, FromPyObject, PyAny, Python}; impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for Cell { type Target = T::Target; @@ -31,10 +31,12 @@ impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for &Cell { } impl<'a, 'py, T: FromPyObject<'a, 'py>> FromPyObject<'a, 'py> for Cell { + type Error = T::Error; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = T::INPUT_TYPE; - fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result { ob.extract().map(Cell::new) } } diff --git a/src/conversions/std/ipaddr.rs b/src/conversions/std/ipaddr.rs index 4bd563fe3a9..6fb1859d196 100644 --- a/src/conversions/std/ipaddr.rs +++ b/src/conversions/std/ipaddr.rs @@ -6,10 +6,12 @@ use crate::sync::PyOnceLock; use crate::types::any::PyAnyMethods; use crate::types::string::PyStringMethods; use crate::types::PyType; -use crate::{intern, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; +use crate::{intern, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, Python}; impl FromPyObject<'_, '_> for IpAddr { - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { match obj.getattr(intern!(obj.py(), "packed")) { Ok(packed) => { if let Ok(packed) = packed.extract::<[u8; 4]>() { diff --git a/src/conversions/std/map.rs b/src/conversions/std/map.rs index a3b760361e9..676351abf4a 100644 --- a/src/conversions/std/map.rs +++ b/src/conversions/std/map.rs @@ -113,11 +113,16 @@ where V: FromPyObjectOwned<'py>, S: hash::BuildHasher + Default, { - fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; let mut ret = collections::HashMap::with_capacity_and_hasher(dict.len(), S::default()); for (k, v) in dict.iter() { - ret.insert(k.extract()?, v.extract()?); + ret.insert( + k.extract().map_err(Into::into)?, + v.extract().map_err(Into::into)?, + ); } Ok(ret) } @@ -133,11 +138,16 @@ where K: FromPyObjectOwned<'py> + cmp::Ord, V: FromPyObjectOwned<'py>, { + type Error = PyErr; + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { let dict = ob.cast::()?; let mut ret = collections::BTreeMap::new(); for (k, v) in dict.iter() { - ret.insert(k.extract()?, v.extract()?); + ret.insert( + k.extract().map_err(Into::into)?, + v.extract().map_err(Into::into)?, + ); } Ok(ret) } diff --git a/src/conversions/std/num.rs b/src/conversions/std/num.rs index 9cf327f220a..986179ad6d3 100644 --- a/src/conversions/std/num.rs +++ b/src/conversions/std/num.rs @@ -51,10 +51,12 @@ macro_rules! int_fits_larger_int { } impl FromPyObject<'_, '_> for $rust_type { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = <$larger_type>::INPUT_TYPE; - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { let val: $larger_type = obj.extract()?; <$rust_type>::try_from(val) .map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) @@ -130,10 +132,12 @@ macro_rules! int_convert_u64_or_i64 { } } impl FromPyObject<'_, '_> for $rust_type { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<$rust_type, Self::Error> { extract_int!(obj, !0, $pylong_as_ll_or_ull, $force_index_call) } @@ -189,10 +193,12 @@ macro_rules! int_fits_c_long { } impl<'py> FromPyObject<'_, 'py> for $rust_type { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { let val: c_long = extract_int!(obj, -1, ffi::PyLong_AsLong)?; <$rust_type>::try_from(val) .map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) @@ -272,10 +278,12 @@ impl<'py> IntoPyObject<'py> for &'_ u8 { } impl<'py> FromPyObject<'_, 'py> for u8 { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result { let val: c_long = extract_int!(obj, -1, ffi::PyLong_AsLong)?; u8::try_from(val).map_err(|e| exceptions::PyOverflowError::new_err(e.to_string())) } @@ -403,10 +411,12 @@ mod fast_128bit_int_conversion { } impl FromPyObject<'_, '_> for $rust_type { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<$rust_type, Self::Error> { let num = unsafe { ffi::PyNumber_Index(ob.as_ptr()).assume_owned_or_err(ob.py())? }; let mut buffer = [0u8; std::mem::size_of::<$rust_type>()]; @@ -522,10 +532,12 @@ mod slow_128bit_int_conversion { } impl FromPyObject<'_, '_> for $rust_type { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "int"; - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result<$rust_type, Self::Error> { let py = ob.py(); unsafe { let lower = err_if_invalid_value( @@ -610,10 +622,12 @@ macro_rules! nonzero_int_impl { } impl FromPyObject<'_, '_> for $nonzero_type { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = <$primitive_type>::INPUT_TYPE; - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { let val: $primitive_type = obj.extract()?; <$nonzero_type>::try_from(val) .map_err(|_| exceptions::PyValueError::new_err("invalid zero value")) diff --git a/src/conversions/std/option.rs b/src/conversions/std/option.rs index 04369bf79f9..8cec88b4e6b 100644 --- a/src/conversions/std/option.rs +++ b/src/conversions/std/option.rs @@ -1,6 +1,5 @@ use crate::{ - conversion::IntoPyObject, types::any::PyAnyMethods, BoundObject, FromPyObject, PyAny, PyResult, - Python, + conversion::IntoPyObject, types::any::PyAnyMethods, BoundObject, FromPyObject, PyAny, Python, }; use crate::{Borrowed, Bound}; @@ -42,7 +41,9 @@ impl<'a, 'py, T> FromPyObject<'a, 'py> for Option where T: FromPyObject<'a, 'py>, { - fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { + type Error = T::Error; + + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result { if obj.is_none() { Ok(None) } else { diff --git a/src/conversions/std/osstr.rs b/src/conversions/std/osstr.rs index 1c396dcf81b..3c480526d83 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -2,7 +2,7 @@ use crate::conversion::IntoPyObject; use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::Bound; use crate::types::PyString; -use crate::{ffi, Borrowed, FromPyObject, PyAny, PyResult, Python}; +use crate::{ffi, Borrowed, FromPyObject, PyAny, PyErr, Python}; use std::borrow::Cow; use std::convert::Infallible; use std::ffi::{OsStr, OsString}; @@ -71,7 +71,9 @@ impl<'py> IntoPyObject<'py> for &&OsStr { // be impossible to implement on Windows. Hence it's omitted entirely impl FromPyObject<'_, '_> for OsString { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { let pystring = ob.cast::()?; #[cfg(not(windows))] diff --git a/src/conversions/std/path.rs b/src/conversions/std/path.rs index 212d0d2ebcf..4d60facd186 100644 --- a/src/conversions/std/path.rs +++ b/src/conversions/std/path.rs @@ -2,7 +2,7 @@ use crate::conversion::IntoPyObject; use crate::ffi_ptr_ext::FfiPtrExt; use crate::sync::PyOnceLock; use crate::types::any::PyAnyMethods; -use crate::{ffi, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python}; +use crate::{ffi, Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, Python}; use std::borrow::Cow; use std::ffi::OsString; use std::path::{Path, PathBuf}; @@ -10,7 +10,9 @@ use std::path::{Path, PathBuf}; // See osstr.rs for why there's no FromPyObject impl for &Path impl FromPyObject<'_, '_> for PathBuf { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { // We use os.fspath to get the underlying path as bytes or str let path = unsafe { ffi::PyOS_FSPath(ob.as_ptr()).assume_owned_or_err(ob.py())? }; Ok(path.extract::()?.into()) diff --git a/src/conversions/std/set.rs b/src/conversions/std/set.rs index 0c1a90ec948..58c38f6ca1c 100644 --- a/src/conversions/std/set.rs +++ b/src/conversions/std/set.rs @@ -10,7 +10,7 @@ use crate::{ set::{try_new_from_iter, PySetMethods}, PyFrozenSet, PySet, }, - Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python, + Borrowed, Bound, FromPyObject, PyAny, PyErr, Python, }; impl<'py, K, S> IntoPyObject<'py> for collections::HashSet @@ -57,12 +57,20 @@ where K: FromPyObjectOwned<'py> + cmp::Eq + hash::Hash, S: hash::BuildHasher + Default, { - fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { match ob.cast::() { - Ok(set) => set.iter().map(|any| any.extract()).collect(), + Ok(set) => set + .iter() + .map(|any| any.extract().map_err(Into::into)) + .collect(), Err(err) => { if let Ok(frozen_set) = ob.cast::() { - frozen_set.iter().map(|any| any.extract()).collect() + frozen_set + .iter() + .map(|any| any.extract().map_err(Into::into)) + .collect() } else { Err(PyErr::from(err)) } @@ -117,12 +125,20 @@ impl<'py, K> FromPyObject<'_, 'py> for collections::BTreeSet where K: FromPyObjectOwned<'py> + cmp::Ord, { - fn extract(ob: Borrowed<'_, 'py, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, 'py, PyAny>) -> Result { match ob.cast::() { - Ok(set) => set.iter().map(|any| any.extract()).collect(), + Ok(set) => set + .iter() + .map(|any| any.extract().map_err(Into::into)) + .collect(), Err(err) => { if let Ok(frozen_set) = ob.cast::() { - frozen_set.iter().map(|any| any.extract()).collect() + frozen_set + .iter() + .map(|any| any.extract().map_err(Into::into)) + .collect() } else { Err(PyErr::from(err)) } diff --git a/src/conversions/std/slice.rs b/src/conversions/std/slice.rs index 796eb65e4f1..00c072e588f 100644 --- a/src/conversions/std/slice.rs +++ b/src/conversions/std/slice.rs @@ -5,7 +5,7 @@ use crate::inspect::types::TypeInfo; use crate::{ conversion::IntoPyObject, types::{PyByteArray, PyByteArrayMethods, PyBytes}, - Bound, PyAny, PyErr, PyResult, Python, + Bound, DowncastError, PyAny, PyErr, Python, }; impl<'a, 'py, T> IntoPyObject<'py> for &'a [T] @@ -35,8 +35,10 @@ where } } -impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a [u8] { - fn extract(obj: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a, 'py> crate::conversion::FromPyObject<'a, 'py> for &'a [u8] { + type Error = DowncastError<'a, 'py>; + + fn extract(obj: crate::Borrowed<'a, 'py, PyAny>) -> Result { Ok(obj.cast::()?.as_bytes()) } @@ -51,8 +53,10 @@ impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a [u8] { /// If the source object is a `bytes` object, the `Cow` will be borrowed and /// pointing into the source object, and no copying or heap allocations will happen. /// If it is a `bytearray`, its contents will be copied to an owned `Cow`. -impl<'a> crate::conversion::FromPyObject<'a, '_> for Cow<'a, [u8]> { - fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a, 'py> crate::conversion::FromPyObject<'a, 'py> for Cow<'a, [u8]> { + type Error = DowncastError<'a, 'py>; + + fn extract(ob: crate::Borrowed<'a, 'py, PyAny>) -> Result { if let Ok(bytes) = ob.cast::() { return Ok(Cow::Borrowed(bytes.as_bytes())); } diff --git a/src/conversions/std/string.rs b/src/conversions/std/string.rs index 4ee826d8f7d..3c6a300ecdf 100644 --- a/src/conversions/std/string.rs +++ b/src/conversions/std/string.rs @@ -4,7 +4,7 @@ use std::{borrow::Cow, convert::Infallible}; use crate::inspect::types::TypeInfo; use crate::{ conversion::IntoPyObject, instance::Bound, types::PyString, Borrowed, FromPyObject, PyAny, - PyResult, Python, + PyErr, Python, }; impl<'py> IntoPyObject<'py> for &str { @@ -160,10 +160,12 @@ impl<'py> IntoPyObject<'py> for &String { #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a str { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "str"; - fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> Result { ob.cast::()?.to_str() } @@ -174,10 +176,12 @@ impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a str { } impl<'a> crate::conversion::FromPyObject<'a, '_> for Cow<'a, str> { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "str"; - fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> Result { ob.cast::()?.to_cow() } @@ -190,10 +194,12 @@ impl<'a> crate::conversion::FromPyObject<'a, '_> for Cow<'a, str> { /// Allows extracting strings from Python objects. /// Accepts Python `str` and `unicode` objects. impl FromPyObject<'_, '_> for String { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "str"; - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { obj.cast::()?.to_cow().map(Cow::into_owned) } @@ -204,10 +210,12 @@ impl FromPyObject<'_, '_> for String { } impl FromPyObject<'_, '_> for char { + type Error = PyErr; + #[cfg(feature = "experimental-inspect")] const INPUT_TYPE: &'static str = "str"; - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { let s = obj.cast::()?.to_cow()?; let mut iter = s.chars(); if let (Some(ch), None) = (iter.next(), iter.next()) { diff --git a/src/conversions/std/time.rs b/src/conversions/std/time.rs index 4a750d2332c..18a7e7dec1c 100644 --- a/src/conversions/std/time.rs +++ b/src/conversions/std/time.rs @@ -13,7 +13,9 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; const SECONDS_PER_DAY: u64 = 24 * 60 * 60; impl FromPyObject<'_, '_> for Duration { - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { let delta = obj.cast::()?; #[cfg(not(Py_LIMITED_API))] let (days, seconds, microseconds) = { @@ -88,7 +90,9 @@ impl<'py> IntoPyObject<'py> for &Duration { // TODO: it might be nice to investigate using timestamps anyway, at least when the datetime is a safe range. impl FromPyObject<'_, '_> for SystemTime { - fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result { let duration_since_unix_epoch: Duration = obj.sub(unix_epoch_py(obj.py())?)?.extract()?; UNIX_EPOCH .checked_add(duration_since_unix_epoch) diff --git a/src/conversions/time.rs b/src/conversions/time.rs index a20346a2d9d..562eebd353e 100644 --- a/src/conversions/time.rs +++ b/src/conversions/time.rs @@ -181,7 +181,9 @@ impl<'py> IntoPyObject<'py> for Duration { } impl FromPyObject<'_, '_> for Duration { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { #[cfg(not(Py_LIMITED_API))] let (days, seconds, microseconds) = { let delta = ob.cast::()?; @@ -224,7 +226,9 @@ impl<'py> IntoPyObject<'py> for Date { } impl FromPyObject<'_, '_> for Date { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + type Error = PyErr; + + fn extract(ob: Borrowed<'_, '_, PyAny>) -> Result { let (year, month, day) = { #[cfg(not(Py_LIMITED_API))] { @@ -265,7 +269,9 @@ impl<'py> IntoPyObject<'py> for Time { } impl FromPyObject<'_, '_> for Time { - fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult