From a8a5f9bed662bcd8afbc975c87c17f6fb040dcd7 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/10] replace `FromPyObject` with `FromPyObjectBound` --- pyo3-macros-backend/src/frompyobject.rs | 7 +- src/buffer.rs | 8 +- src/conversion.rs | 123 +++++------------------- src/conversions/bigdecimal.rs | 6 +- src/conversions/chrono.rs | 47 +++++---- 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 | 31 +++--- src/conversions/num_complex.rs | 8 +- src/conversions/num_rational.rs | 6 +- 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 | 36 +++---- 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 | 6 +- src/impl_/frompyobject.rs | 12 +-- src/inspect/types.rs | 2 +- src/instance.rs | 22 +++-- src/pybacked.rs | 17 ++-- src/types/any.rs | 11 +-- src/types/boolobject.rs | 10 +- src/types/float.rs | 8 +- src/types/sequence.rs | 12 +-- src/types/tuple.rs | 6 +- tests/ui/deprecated.stderr | 4 +- tests/ui/invalid_cancel_handle.stderr | 6 +- 41 files changed, 305 insertions(+), 379 deletions(-) diff --git a/pyo3-macros-backend/src/frompyobject.rs b/pyo3-macros-backend/src/frompyobject.rs index 68f95e794a8..6c5b9371ea0 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -712,7 +712,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 { @@ -740,8 +740,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 } } diff --git a/src/buffer.rs b/src/buffer.rs index c5e5de568eb..188b0a50230 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::marker::PhantomData; use std::os::raw; use std::pin::Pin; @@ -182,9 +182,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 165175fae54..9e16b42e1ae 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -3,7 +3,6 @@ use crate::err::PyResult; #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::pyclass::boolean_struct::False; -use crate::types::any::PyAnyMethods; use crate::types::PyTuple; use crate::{Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyErr, PyRef, PyRefMut, Python}; use std::convert::Infallible; @@ -259,91 +258,27 @@ 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 { - /// 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 { - /// 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 &'_ 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 { /// Extracts `Self` from the bound smart pointer `obj`. /// /// Users are advised against calling this method directly: instead, use this via /// [`Bound<'_, PyAny>::extract`] or [`Py::extract`]. - fn from_py_object_bound(ob: Borrowed<'a, 'py, PyAny>) -> PyResult; + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult; + + /// Deprecated name for [`FromPyObject::extract`] + #[deprecated(since = "0.23.0", note = "replaced by `FromPyObject::extract`")] + fn extract_bound(ob: &'a Bound<'py, PyAny>) -> PyResult { + Self::extract(ob.as_borrowed()) + } /// Extracts the type hint information for this type when it appears as an argument. /// @@ -359,44 +294,30 @@ pub trait FromPyObjectBound<'a, 'py>: Sized + from_py_object_bound_sealed::Seale } } -impl<'py, T> FromPyObjectBound<'_, 'py> for T -where - T: FromPyObject<'py>, -{ - 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, { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let bound = obj.downcast::()?; 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, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { obj.downcast::()?.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, { - fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { obj.downcast::()?.try_borrow_mut().map_err(Into::into) } } diff --git a/src/conversions/bigdecimal.rs b/src/conversions/bigdecimal.rs index 446e38bf6c5..357c156e93d 100644 --- a/src/conversions/bigdecimal.rs +++ b/src/conversions/bigdecimal.rs @@ -55,7 +55,7 @@ use crate::{ exceptions::PyValueError, sync::GILOnceCell, types::{PyAnyMethods, PyStringMethods, PyType}, - Bound, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, + Borrowed, Bound, FromPyObject, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, }; use bigdecimal::BigDecimal; @@ -65,8 +65,8 @@ fn get_decimal_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { DECIMAL_CLS.import(py, "decimal", "Decimal") } -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/chrono.rs b/src/conversions/chrono.rs index 165fc5b9c43..de9a515aca8 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -100,8 +100,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.downcast::()?; // Python size are much lower than rust size so we do not need bound checks. // 0 <= microseconds < 1000000 @@ -154,9 +154,9 @@ impl<'py> IntoPyObject<'py> for &NaiveDate { } } -impl FromPyObject<'_> for NaiveDate { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { - let date = ob.downcast::()?; +impl FromPyObject<'_, '_> for NaiveDate { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + let date = &*ob.downcast::()?; py_date_to_naive_date(date) } } @@ -196,9 +196,9 @@ impl<'py> IntoPyObject<'py> for &NaiveTime { } } -impl FromPyObject<'_> for NaiveTime { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { - let time = ob.downcast::()?; +impl FromPyObject<'_, '_> for NaiveTime { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult { + let time = &*ob.downcast::()?; py_time_to_naive_time(time) } } @@ -239,9 +239,9 @@ impl<'py> IntoPyObject<'py> for &NaiveDateTime { } } -impl FromPyObject<'_> for NaiveDateTime { - fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult { - let dt = dt.downcast::()?; +impl FromPyObject<'_, '_> for NaiveDateTime { + fn extract(dt: Borrowed<'_, '_, PyAny>) -> PyResult { + let dt = &*dt.downcast::()?; // 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 @@ -316,9 +316,12 @@ where } } -impl FromPyObject<'py>> FromPyObject<'_> for DateTime { - fn extract_bound(dt: &Bound<'_, PyAny>) -> PyResult> { - let dt = dt.downcast::()?; +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.downcast::()?; let tzinfo = dt.get_tzinfo(); let tz = if let Some(tzinfo) = tzinfo { @@ -375,12 +378,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.downcast::()?; // Passing Python's None to the `utcoffset` function will only @@ -424,8 +427,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) @@ -488,7 +491,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(), @@ -508,7 +513,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 ee7bd504888..224eda2390a 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 7e8e3bfc2dc..b926b58ae9d 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 c302d28d2a1..7671e7caf1d 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.downcast::()?; 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.downcast::() { Ok(set) => set.iter().map(|any| any.extract()).collect(), Err(err) => { diff --git a/src/conversions/indexmap.rs b/src/conversions/indexmap.rs index d6995c14db2..ac433e88adf 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.downcast::()?; 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 b92787a70b8..e9dd1e0967b 100644 --- a/src/conversions/jiff.rs +++ b/src/conversions/jiff.rs @@ -52,7 +52,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}; @@ -123,8 +123,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()) } @@ -155,8 +155,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.downcast::()?; #[cfg(not(Py_LIMITED_API))] @@ -207,11 +207,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.downcast::()?; - - pytime_to_time(ob) + #[allow(clippy::explicit_auto_deref)] + pytime_to_time(&*ob) } } @@ -235,8 +235,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.downcast::()?; let has_tzinfo = dt.get_tzinfo().is_some(); @@ -244,7 +244,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.downcast::()?; 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))] @@ -341,8 +343,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.downcast::()?; let attr = intern!(ob.py(), "key"); @@ -378,8 +380,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.downcast::()?; @@ -427,8 +429,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.downcast::()?; #[cfg(not(Py_LIMITED_API))] @@ -452,8 +454,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 ebc4058b1a6..a718eec502b 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -52,9 +52,8 @@ use crate::types::{bytes::PyBytesMethods, PyBytes}; use crate::{ conversion::IntoPyObject, ffi, - instance::Bound, types::{any::PyAnyMethods, PyInt}, - FromPyObject, Py, PyAny, PyErr, PyResult, Python, + Borrowed, Bound, FromPyObject, Py, PyAny, PyErr, PyResult, Python, }; use num_bigint::{BigInt, BigUint}; @@ -125,8 +124,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; @@ -134,11 +133,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().map_or(false, |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) @@ -162,19 +161,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; @@ -182,20 +181,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 + 7) / 8, false)?; + let bytes = int_to_py_bytes(&num, (n_bits + 7) / 8, false)?; Ok(BigUint::from_bytes_le(bytes.as_bytes())) } } @@ -280,6 +279,7 @@ fn int_to_py_bytes<'py>( is_signed: bool, ) -> PyResult> { use crate::intern; + use crate::types::{PyAnyMethods, PyDictMethods}; let py = long.py(); let kwargs = if is_signed { let kwargs = crate::types::PyDict::new(py); @@ -313,6 +313,7 @@ fn int_n_bits(long: &Bound<'_, PyInt>) -> PyResult { #[cfg(Py_LIMITED_API)] { // slow path + use crate::types::PyAnyMethods; long.call_method0(crate::intern!(py, "bit_length")) .and_then(|any| any.extract()) } @@ -322,7 +323,7 @@ fn int_n_bits(long: &Bound<'_, PyInt>) -> PyResult { mod tests { use super::*; use crate::tests::common::generate_unique_module_name; - use crate::types::{PyDict, PyModule}; + use crate::types::{PyAnyMethods, PyDict, PyModule}; use indoc::indoc; use pyo3_ffi::c_str; diff --git a/src/conversions/num_complex.rs b/src/conversions/num_complex.rs index e9fb1096ff5..ace581fbdba 100644 --- a/src/conversions/num_complex.rs +++ b/src/conversions/num_complex.rs @@ -97,7 +97,7 @@ use crate::{ ffi, ffi_ptr_ext::FfiPtrExt, types::{any::PyAnyMethods, PyComplex}, - Bound, FromPyObject, PyAny, PyErr, PyResult, Python, + Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python, }; use num_complex::Complex; use std::os::raw::c_double; @@ -148,8 +148,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()); @@ -170,7 +170,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 dfdd80cf54f..a1994d08c94 100644 --- a/src/conversions/num_rational.rs +++ b/src/conversions/num_rational.rs @@ -48,7 +48,7 @@ use crate::ffi; use crate::sync::GILOnceCell; 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/rust_decimal.rs b/src/conversions/rust_decimal.rs index 392971a0b4b..405ba09bff1 100644 --- a/src/conversions/rust_decimal.rs +++ b/src/conversions/rust_decimal.rs @@ -55,12 +55,12 @@ use crate::sync::GILOnceCell; 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 d4229bc2865..09bc4679cb1 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.downcast_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 36db5ec640f..a26d02f52f2 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.downcast_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 983a0350228..12c08918e76 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; @@ -27,8 +24,8 @@ impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for &Cell { } } -impl<'py, T: FromPyObject<'py>> FromPyObject<'py> for Cell { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { +impl<'a, 'py, T: FromPyObject<'a, 'py>> FromPyObject<'a, 'py> for Cell { + 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 76f6a6927c2..16aceaefa6d 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::GILOnceCell; 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 b472957d1b1..f56b2b8258f 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.downcast::()?; 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.downcast::()?; 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 40073d8af69..af53870bd4c 100644 --- a/src/conversions/std/num.rs +++ b/src/conversions/std/num.rs @@ -5,7 +5,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; 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::num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, @@ -45,8 +45,8 @@ macro_rules! int_fits_larger_int { } } - impl FromPyObject<'_> for $rust_type { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + impl FromPyObject<'_, '_> for $rust_type { + 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())) @@ -114,14 +114,9 @@ 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 { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + impl FromPyObject<'_, '_> for $rust_type { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { extract_int!(obj, !0, $pylong_as_ll_or_ull, $force_index_call) } @@ -170,8 +165,8 @@ macro_rules! int_fits_c_long { } } - impl<'py> FromPyObject<'py> for $rust_type { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + impl<'py> FromPyObject<'_, 'py> for $rust_type { + 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())) @@ -244,8 +239,8 @@ impl<'py> IntoPyObject<'py> for &'_ u8 { } } -impl FromPyObject<'_> for u8 { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl<'py> FromPyObject<'_, 'py> for u8 { + 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())) } @@ -366,8 +361,8 @@ mod fast_128bit_int_conversion { } } - impl FromPyObject<'_> for $rust_type { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + impl FromPyObject<'_, '_> for $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>()]; @@ -475,8 +470,8 @@ mod slow_128bit_int_conversion { } } - impl FromPyObject<'_> for $rust_type { - fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> { + impl FromPyObject<'_, '_> for $rust_type { + fn extract(ob: Borrowed<'_, '_, PyAny>) -> PyResult<$rust_type> { let py = ob.py(); unsafe { let lower = err_if_invalid_value( @@ -554,8 +549,8 @@ macro_rules! nonzero_int_impl { } } - impl FromPyObject<'_> for $nonzero_type { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { + impl FromPyObject<'_, '_> for $nonzero_type { + 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")) @@ -585,6 +580,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 39fa56f373d..0a6871b8230 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -3,7 +3,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::Bound; use crate::types::any::PyAnyMethods; 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}; @@ -71,8 +71,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.downcast::()?; #[cfg(not(windows))] @@ -98,8 +98,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()); @@ -170,7 +168,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 3eb074d04d2..1c8e0df8da2 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::GILOnceCell; use crate::types::any::PyAnyMethods; -use crate::{ffi, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python}; +use crate::{ffi, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyObject, 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 ebb737f72c6..aff6fdf6c6d 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.downcast::() { 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.downcast::() { 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 5bfa5a3d48d..6ed11dd8bf9 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.downcast::()?.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.downcast::() { return Ok(Cow::Borrowed(bytes.as_bytes())); } diff --git a/src/conversions/std/string.rs b/src/conversions/std/string.rs index 8936bd35000..8a817418122 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::{any::PyAnyMethods, string::PyStringMethods, PyString}, - FromPyObject, PyAny, PyResult, Python, + conversion::IntoPyObject, instance::Bound, types::PyString, Borrowed, FromPyObject, PyAny, + PyResult, Python, }; impl<'py> IntoPyObject<'py> for &str { @@ -137,8 +135,8 @@ impl<'py> IntoPyObject<'py> for &String { } #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str { - fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a> crate::conversion::FromPyObject<'a, '_> for &'a str { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { ob.downcast::()?.to_str() } @@ -148,8 +146,8 @@ impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str { } } -impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> { - fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { +impl<'a> crate::conversion::FromPyObject<'a, '_> for Cow<'a, str> { + fn extract(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult { ob.downcast::()?.to_cow() } @@ -161,8 +159,8 @@ 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 { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for String { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { obj.downcast::()?.to_cow().map(Cow::into_owned) } @@ -172,8 +170,8 @@ impl FromPyObject<'_> for String { } } -impl FromPyObject<'_> for char { - fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { +impl FromPyObject<'_, '_> for char { + fn extract(obj: Borrowed<'_, '_, PyAny>) -> PyResult { let s = obj.downcast::()?.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 53e3bf1a641..ab0f49523db 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.downcast::()?; #[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 351fbb637d2..a73ff717f2b 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.downcast::()?; @@ -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 a26d02f52f2..452e8d75caa 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 f56b2b8258f..01e7f29fb39 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.downcast::()?; diff --git a/src/conversions/std/set.rs b/src/conversions/std/set.rs index aff6fdf6c6d..8c5cde6b6cc 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.downcast::() { diff --git a/src/prelude.rs b/src/prelude.rs index 258b522c103..2ff46688593 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}; pub use crate::instance::{Borrowed, Bound, Py, PyObject}; pub use crate::marker::Python; diff --git a/src/types/sequence.rs b/src/types/sequence.rs index 8f8f9f36f15..d7bbd356ec6 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 293f6f2d51aa67316cb1956c1802b658045b38b5 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/10] 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 66d9061365a1fc03017e88e7e105edb013cedfbf 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/10] 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 6c5b9371ea0..4784d2a9f9f 100644 --- a/pyo3-macros-backend/src/frompyobject.rs +++ b/pyo3-macros-backend/src/frompyobject.rs @@ -712,7 +712,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 a6d710955c4e1d9aca6d4f9694139cb9ec4d3eba 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/10] 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 be7e5f5eedf..6049ebbcd41 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -23,14 +23,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 @@ -52,7 +52,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 f81ae502117222eb68b12fbbca92cb7521c68b25 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/10] 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 448187c4d8e..a0be2f926f7 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -234,7 +234,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 /// @@ -258,21 +259,44 @@ 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 { /// Extracts `Self` from the bound smart pointer `obj`. /// /// Users are advised against calling this method directly: instead, use this via /// [`Bound<'_, PyAny>::extract`] or [`Py::extract`]. - fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult; + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult; /// Deprecated name for [`FromPyObject::extract`] #[deprecated(since = "0.23.0", note = "replaced by `FromPyObject::extract`")] From e2d6b59cb36d1a8abc98b2492573fdd0405b4e49 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/10] `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 a0be2f926f7..c189b170cd6 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -234,8 +234,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 /// @@ -259,38 +259,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 { /// Extracts `Self` from the bound smart pointer `obj`. /// @@ -318,11 +307,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. /// /// ``` /// # use pyo3::prelude::*; @@ -353,6 +348,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 7fd20a69ce245818ff98f649b8abd7dfcbb02961 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/10] 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 c189b170cd6..71b9b83efa2 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -279,7 +279,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 { /// Extracts `Self` from the bound smart pointer `obj`. /// diff --git a/src/conversions/std/osstr.rs b/src/conversions/std/osstr.rs index 0a6871b8230..54585d20dcb 100644 --- a/src/conversions/std/osstr.rs +++ b/src/conversions/std/osstr.rs @@ -180,6 +180,7 @@ mod tests { #[cfg(not(windows))] fn test_non_utf8_conversion() { Python::with_gil(|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 1c8e0df8da2..8131ff75734 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::with_gil(|py| { + use crate::types::PyAnyMethods; use std::ffi::OsStr; #[cfg(not(target_os = "wasi"))] use std::os::unix::ffi::OsStrExt; From 9abc0f02fef4300bfbab9022351da51c1eed0aed Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:39:14 +0100 Subject: [PATCH 10/10] add second lifetime to `PyRef` and `PyRefMut` --- guide/src/class.md | 65 ++- guide/src/class/numeric.md | 15 +- guide/src/class/protocols.md | 498 +++++++++--------- pyo3-macros-backend/src/method.rs | 2 +- pyo3-macros-backend/src/pyclass.rs | 18 +- pyo3-macros-backend/src/pymethod.rs | 12 +- pytests/src/awaitable.rs | 10 +- src/conversion.rs | 8 +- src/impl_/extract_argument.rs | 55 +- src/impl_/pyclass.rs | 6 +- src/impl_/pymethods.rs | 10 +- src/instance.rs | 65 ++- src/marker.rs | 4 +- src/pycell.rs | 142 +++-- src/pycell/impl_.rs | 46 +- src/tests/hygiene/pymethods.rs | 29 +- src/types/any.rs | 2 +- tests/test_arithmetics.rs | 119 +++-- tests/test_buffer.rs | 2 +- tests/test_buffer_protocol.rs | 4 +- tests/test_class_conversion.rs | 12 +- tests/test_gc.rs | 2 +- tests/test_getter_setter.rs | 4 +- tests/test_methods.rs | 4 +- tests/test_proto_methods.rs | 20 +- tests/test_pyself.rs | 6 +- tests/test_sequence.rs | 4 +- tests/test_various.rs | 2 +- tests/ui/invalid_cancel_handle.stderr | 30 +- tests/ui/invalid_frozen_pyclass_borrow.stderr | 12 +- tests/ui/invalid_pymethod_enum.stderr | 16 +- tests/ui/invalid_pymethods.stderr | 2 +- 32 files changed, 657 insertions(+), 569 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 8772e857d7e..5a360a35663 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -2,7 +2,7 @@ PyO3 exposes a group of attributes powered by Rust's proc macro system for defining Python classes as Rust structs. -The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or `enum` to generate a Python type for it. They will usually also have *one* `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`. +The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or `enum` to generate a Python type for it. They will usually also have _one_ `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`. This chapter will discuss the functionality and configuration these attributes offer. Below is a list of links to the relevant section of this chapter for each: @@ -22,6 +22,7 @@ This chapter will discuss the functionality and configuration these attributes o ## Defining a new class To define a custom Python class, add the `#[pyclass]` attribute to a Rust struct or enum. + ```rust # #![allow(dead_code)] use pyo3::prelude::*; @@ -202,13 +203,14 @@ fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> { It is often useful to turn a `#[pyclass]` type `T` into a Python object and access it from Rust code. The [`Py`] and [`Bound<'py, T>`] smart pointers are the ways to represent a Python object in PyO3's API. More detail can be found about them [in the Python objects](./types.md#pyo3s-smart-pointers) section of the guide. -Most Python objects do not offer exclusive (`&mut`) access (see the [section on Python's memory model](./python-from-rust.md#pythons-memory-model)). However, Rust structs wrapped as Python objects (called `pyclass` types) often *do* need `&mut` access. Due to the GIL, PyO3 *can* guarantee exclusive access to them. +Most Python objects do not offer exclusive (`&mut`) access (see the [section on Python's memory model](./python-from-rust.md#pythons-memory-model)). However, Rust structs wrapped as Python objects (called `pyclass` types) often _do_ need `&mut` access. Due to the GIL, PyO3 _can_ guarantee exclusive access to them. The Rust borrow checker cannot reason about `&mut` references once an object's ownership has been passed to the Python interpreter. This means that borrow checking is done at runtime using with a scheme very similar to `std::cell::RefCell`. This is known as [interior mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html). Users who are familiar with `RefCell` can use `Py` and `Bound<'py, T>` just like `RefCell`. For users who are not very familiar with `RefCell`, here is a reminder of Rust's rules of borrowing: + - At any given time, you can have either (but not both of) one mutable reference or any number of immutable references. - References can never outlast the data they refer to. @@ -309,11 +311,11 @@ Generally, `#[new]` methods have to return `T: Into>` o For constructors that may fail, you should wrap the return type in a PyResult as well. Consult the table below to determine which type your constructor should return: -| | **Cannot fail** | **May fail** | -|-----------------------------|---------------------------|-----------------------------------| -|**No inheritance** | `T` | `PyResult` | -|**Inheritance(T Inherits U)**| `(T, U)` | `PyResult<(T, U)>` | -|**Inheritance(General Case)**| [`PyClassInitializer`] | `PyResult>` | +| | **Cannot fail** | **May fail** | +| ----------------------------- | ------------------------- | --------------------------------- | +| **No inheritance** | `T` | `PyResult` | +| **Inheritance(T Inherits U)** | `(T, U)` | `PyResult<(T, U)>` | +| **Inheritance(General Case)** | [`PyClassInitializer`] | `PyResult>` | ## Inheritance @@ -323,7 +325,6 @@ Currently, only classes defined in Rust and builtins provided by PyO3 can be inh from; inheriting from other classes defined in Python is not yet supported ([#991](https://github.com/PyO3/pyo3/issues/991)). - For convenience, `(T, U)` implements `Into>` where `U` is the base class of `T`. But for a more deeply nested inheritance, you have to return `PyClassInitializer` @@ -370,7 +371,7 @@ impl SubClass { (SubClass { val2: 15 }, BaseClass::new()) } - fn method2(self_: PyRef<'_, Self>) -> PyResult { + fn method2(self_: PyRef<'_, '_, Self>) -> PyResult { let super_ = self_.as_super(); // Get &PyRef super_.method1().map(|x| x * self_.val2) } @@ -388,24 +389,24 @@ impl SubSubClass { PyClassInitializer::from(SubClass::new()).add_subclass(SubSubClass { val3: 20 }) } - fn method3(self_: PyRef<'_, Self>) -> PyResult { + fn method3(self_: PyRef<'_, '_, Self>) -> PyResult { let base = self_.as_super().as_super(); // Get &PyRef<'_, BaseClass> base.method1().map(|x| x * self_.val3) } - fn method4(self_: PyRef<'_, Self>) -> PyResult { + fn method4(self_: PyRef<'_, '_, Self>) -> PyResult { let v = self_.val3; let super_ = self_.into_super(); // Get PyRef<'_, SubClass> SubClass::method2(super_).map(|x| x * v) } - fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) { + fn get_values(self_: PyRef<'_, '_, Self>) -> (usize, usize, usize) { let val1 = self_.as_super().as_super().val1; let val2 = self_.as_super().val2; (val1, val2, self_.val3) } - fn double_values(mut self_: PyRefMut<'_, Self>) { + fn double_values(mut self_: PyRefMut<'_, '_, Self>) { self_.as_super().as_super().val1 *= 2; self_.as_super().val2 *= 2; self_.val3 *= 2; @@ -481,6 +482,7 @@ impl DictWithCounter { ``` If `SubClass` does not provide a base class initialization, the compilation fails. + ```rust,compile_fail # use pyo3::prelude::*; @@ -504,7 +506,7 @@ impl SubClass { ``` The `__new__` constructor of a native base class is called implicitly when -creating a new instance from Python. Be sure to accept arguments in the +creating a new instance from Python. Be sure to accept arguments in the `#[new]` method that you want the base class to get, even if they are not used in that `fn`: @@ -542,6 +544,7 @@ initial items, such as `MyDict(item_sequence)` or `MyDict(a=1, b=2)`. ## Object properties PyO3 supports two ways to add properties to your `#[pyclass]`: + - For simple struct fields with no side effects, a `#[pyo3(get, set)]` attribute can be added directly to the field definition in the `#[pyclass]`. - For properties which require computation you can define `#[getter]` and `#[setter]` functions in the [`#[pymethods]`](#instance-methods) block. @@ -566,6 +569,7 @@ The above would make the `num` field available for reading and writing as a `sel Properties can be readonly or writeonly by using just `#[pyo3(get)]` or `#[pyo3(set)]` respectively. To use these annotations, your field type must implement some conversion traits: + - For `get` the field type must implement both `IntoPy` and `Clone`. - For `set` the field type must implement `FromPyObject`. @@ -733,15 +737,16 @@ impl MyClass { Declares a class method callable from Python. -* The first parameter is the type object of the class on which the method is called. +- The first parameter is the type object of the class on which the method is called. This may be the type object of a derived class. -* The first parameter implicitly has type `&Bound<'_, PyType>`. -* For details on `parameter-list`, see the documentation of `Method arguments` section. -* The return type must be `PyResult` or `T` for some `T` that implements `IntoPy`. +- The first parameter implicitly has type `&Bound<'_, PyType>`. +- For details on `parameter-list`, see the documentation of `Method arguments` section. +- The return type must be `PyResult` or `T` for some `T` that implements `IntoPy`. ### Constructors which accept a class argument To create a constructor which takes a positional class argument, you can combine the `#[classmethod]` and `#[new]` modifiers: + ```rust # #![allow(dead_code)] # use pyo3::prelude::*; @@ -807,7 +812,7 @@ Python::with_gil(|py| { ``` > Note: if the method has a `Result` return type and returns an `Err`, PyO3 will panic during -class creation. +> class creation. If the class attribute is defined with `const` code only, one can also annotate associated constants: @@ -844,7 +849,7 @@ fn increment_field(my_class: &mut MyClass) { // Take a reference wrapper when borrowing should be automatic, // but interaction with the underlying `Bound` is desired. #[pyfunction] -fn print_field(my_class: PyRef<'_, MyClass>) { +fn print_field(my_class: PyRef<'_, '_, MyClass>) { println!("{}", my_class.my_field); } @@ -1193,7 +1198,7 @@ Python::with_gil(|py| { ``` Ordering of enum variants is optionally added using `#[pyo3(ord)]`. -*Note: Implementation of the `PartialOrd` trait is required when passing the `ord` argument. If not implemented, a compile time error is raised.* +_Note: Implementation of the `PartialOrd` trait is required when passing the `ord` argument. If not implemented, a compile time error is raised._ ```rust # use pyo3::prelude::*; @@ -1390,22 +1395,22 @@ impl pyo3::PyClass for MyClass { type Frozen = pyo3::pyclass::boolean_struct::False; } -impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py, false> for &'a MyClass +impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder MyClass { - type Holder = ::std::option::Option>; + type Holder = ::std::option::Option>; #[inline] - fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult { + fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'holder mut Self::Holder) -> pyo3::PyResult { pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder) } } -impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py, false> for &'a mut MyClass +impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder mut MyClass { - type Holder = ::std::option::Option>; + type Holder = ::std::option::Option>; #[inline] - fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult { + fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'holder mut Self::Holder) -> pyo3::PyResult { pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder) } } @@ -1452,22 +1457,16 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass { # } ``` - [`PyTypeInfo`]: {{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PyTypeInfo.html - [`Py`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Py.html [`Bound<'_, T>`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Bound.html [`PyClass`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/trait.PyClass.html [`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html [`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRefMut.html [`PyClassInitializer`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass_init/struct.PyClassInitializer.html - [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html - [classattr]: https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables - [`multiple-pymethods`]: features.md#multiple-pymethods - [lifetime-elision]: https://doc.rust-lang.org/reference/lifetime-elision.html [compiler-error-e0106]: https://doc.rust-lang.org/error_codes/E0106.html diff --git a/guide/src/class/numeric.md b/guide/src/class/numeric.md index 124bb8d27a6..0502cf1b8d8 100644 --- a/guide/src/class/numeric.md +++ b/guide/src/class/numeric.md @@ -3,11 +3,12 @@ At this point we have a `Number` class that we can't actually do any math on! Before proceeding, we should think about how we want to handle overflows. There are three obvious solutions: + - We can have infinite precision just like Python's `int`. However that would be quite boring - we'd - be reinventing the wheel. + be reinventing the wheel. - We can raise exceptions whenever `Number` overflows, but that makes the API painful to use. - We can wrap around the boundary of `i32`. This is the approach we'll take here. To do that we'll just forward to `i32`'s - `wrapping_*` methods. + `wrapping_*` methods. ### Fixing our constructor @@ -42,6 +43,7 @@ fn wrap(obj: &Bound<'_, PyAny>) -> PyResult { Ok(val as i32) } ``` + We also add documentation, via `///` comments, which are visible to Python users. ```rust,no_run @@ -68,7 +70,6 @@ impl Number { } ``` - With that out of the way, let's implement some operators: ```rust,no_run use pyo3::exceptions::{PyZeroDivisionError, PyValueError}; @@ -132,7 +133,7 @@ impl Number { # #[pymethods] impl Number { - fn __pos__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __pos__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } @@ -178,7 +179,7 @@ impl Number { We do not implement the in-place operations like `__iadd__` because we do not wish to mutate `Number`. Similarly we're not interested in supporting operations with different types, so we do not implement - the reflected operations like `__radd__` either. +the reflected operations like `__radd__` either. Now Python can use our `Number` class: @@ -405,13 +406,15 @@ function that does: unsigned long PyLong_AsUnsignedLongMask(PyObject *obj) ``` -We can call this function from Rust by using [`pyo3::ffi::PyLong_AsUnsignedLongMask`]. This is an *unsafe* +We can call this function from Rust by using [`pyo3::ffi::PyLong_AsUnsignedLongMask`]. This is an _unsafe_ function, which means we have to use an unsafe block to call it and take responsibility for upholding the contracts of this function. Let's review those contracts: + - The GIL must be held. If it's not, calling this function causes a data race. - The pointer must be valid, i.e. it must be properly aligned and point to a valid Python object. Let's create that helper function. The signature has to be `fn(&Bound<'_, PyAny>) -> PyResult`. + - `&Bound<'_, PyAny>` represents a checked borrowed reference, so the pointer derived from it is valid (and not null). - Whenever we have borrowed references to Python objects in scope, it is guaranteed that the GIL is held. This reference is also where we can get a [`Python`] token to use in our call to [`PyErr::take`]. diff --git a/guide/src/class/protocols.md b/guide/src/class/protocols.md index 44aa9199e36..7e8729a3883 100644 --- a/guide/src/class/protocols.md +++ b/guide/src/class/protocols.md @@ -3,6 +3,7 @@ Python's object model defines several protocols for different object behavior, such as the sequence, mapping, and number protocols. Python classes support these protocols by implementing "magic" methods, such as `__str__` or `__repr__`. Because of the double-underscores surrounding their name, these are also known as "dunder" methods. PyO3 makes it possible for every magic method to be implemented in `#[pymethods]` just as they would be done in a regular Python class, with a few notable differences: + - `__new__` and `__init__` are replaced by the [`#[new]` attribute](../class.md#constructor). - `__del__` is not yet supported, but may be in the future. - `__buffer__` and `__release_buffer__` are currently not supported and instead PyO3 supports [`__getbuffer__` and `__releasebuffer__`](#buffer-objects) methods (these predate [PEP 688](https://peps.python.org/pep-0688/#python-level-buffer-protocol)), again this may change in the future. @@ -18,138 +19,141 @@ If a function name in `#[pymethods]` is a magic method which is known to need sp The magic methods handled by PyO3 are very similar to the standard Python ones on [this page](https://docs.python.org/3/reference/datamodel.html#special-method-names) - in particular they are the subset which have slots as [defined here](https://docs.python.org/3/c-api/typeobj.html). When PyO3 handles a magic method, a couple of changes apply compared to other `#[pymethods]`: - - The Rust function signature is restricted to match the magic method. - - The `#[pyo3(signature = (...)]` and `#[pyo3(text_signature = "...")]` attributes are not allowed. - -The following sections list all magic methods for which PyO3 implements the necessary special handling. The -given signatures should be interpreted as follows: - - All methods take a receiver as first argument, shown as ``. It can be - `&self`, `&mut self` or a `Bound` reference like `self_: PyRef<'_, Self>` and - `self_: PyRefMut<'_, Self>`, as described [here](../class.md#inheritance). - - An optional `Python<'py>` argument is always allowed as the first argument. - - Return values can be optionally wrapped in `PyResult`. - - `object` means that any type is allowed that can be extracted from a Python - object (if argument) or converted to a Python object (if return value). - - Other types must match what's given, e.g. `pyo3::basic::CompareOp` for - `__richcmp__`'s second argument. - - For the comparison and arithmetic methods, extraction errors are not - propagated as exceptions, but lead to a return of `NotImplemented`. - - For some magic methods, the return values are not restricted by PyO3, but - checked by the Python interpreter. For example, `__str__` needs to return a - string object. This is indicated by `object (Python type)`. - -### Basic object customization - - `__str__() -> object (str)` - - `__repr__() -> object (str)` +- The Rust function signature is restricted to match the magic method. +- The `#[pyo3(signature = (...)]` and `#[pyo3(text_signature = "...")]` attributes are not allowed. - - `__hash__() -> isize` - - Objects that compare equal must have the same hash value. Any type up to 64 bits may be returned instead of `isize`, PyO3 will convert to an isize automatically (wrapping unsigned types like `u64` and `usize`). -
- Disabling Python's default hash - By default, all `#[pyclass]` types have a default hash implementation from Python. Types which should not be hashable can override this by setting `__hash__` to `None`. This is the same mechanism as for a pure-Python class. This is done like so: - - ```rust,no_run - # use pyo3::prelude::*; - # - #[pyclass] - struct NotHashable {} - - #[pymethods] - impl NotHashable { - #[classattr] - const __hash__: Option = None; - } - ``` -
- - - `__lt__(, object) -> object` - - `__le__(, object) -> object` - - `__eq__(, object) -> object` - - `__ne__(, object) -> object` - - `__gt__(, object) -> object` - - `__ge__(, object) -> object` - - The implementations of Python's "rich comparison" operators `<`, `<=`, `==`, `!=`, `>` and `>=` respectively. - - _Note that implementing any of these methods will cause Python not to generate a default `__hash__` implementation, so consider also implementing `__hash__`._ -
- Return type - The return type will normally be `bool` or `PyResult`, however any Python object can be returned. -
- - - `__richcmp__(, object, pyo3::basic::CompareOp) -> object` - - Implements Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`) in a single method. - The `CompareOp` argument indicates the comparison operation being performed. You can use - [`CompareOp::matches`] to adapt a Rust `std::cmp::Ordering` result to the requested comparison. - - _This method cannot be implemented in combination with any of `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__`, or `__ge__`._ - - _Note that implementing `__richcmp__` will cause Python not to generate a default `__hash__` implementation, so consider implementing `__hash__` when implementing `__richcmp__`._ -
- Return type - The return type will normally be `PyResult`, but any Python object can be returned. - - If you want to leave some operations unimplemented, you can return `py.NotImplemented()` - for some of the operations: - - ```rust,no_run - use pyo3::class::basic::CompareOp; - use pyo3::types::PyNotImplemented; - - # use pyo3::prelude::*; - # use pyo3::BoundObject; - # - # #[pyclass] - # struct Number(i32); - # - #[pymethods] - impl Number { - fn __richcmp__<'py>(&self, other: &Self, op: CompareOp, py: Python<'py>) -> PyResult> { - match op { - CompareOp::Eq => Ok((self.0 == other.0).into_pyobject(py)?.into_any()), - CompareOp::Ne => Ok((self.0 != other.0).into_pyobject(py)?.into_any()), - _ => Ok(PyNotImplemented::get(py).into_any()), - } - } - } - ``` - - If the second argument `object` is not of the type specified in the - signature, the generated code will automatically `return NotImplemented`. -
- - - `__getattr__(, object) -> object` - - `__getattribute__(, object) -> object` -
- Differences between `__getattr__` and `__getattribute__` - As in Python, `__getattr__` is only called if the attribute is not found - by normal attribute lookup. `__getattribute__`, on the other hand, is - called for *every* attribute access. If it wants to access existing - attributes on `self`, it needs to be very careful not to introduce - infinite recursion, and use `baseclass.__getattribute__()`. -
- - - `__setattr__(, value: object) -> ()` - - `__delattr__(, object) -> ()` - - Overrides attribute access. +The following sections list all magic methods for which PyO3 implements the necessary special handling. The +given signatures should be interpreted as follows: - - `__bool__() -> bool` +- All methods take a receiver as first argument, shown as ``. It can be + `&self`, `&mut self` or a `Bound` reference like `self_: PyRef<'_, Self>` and + `self_: PyRefMut<'_, Self>`, as described [here](../class.md#inheritance). +- An optional `Python<'py>` argument is always allowed as the first argument. +- Return values can be optionally wrapped in `PyResult`. +- `object` means that any type is allowed that can be extracted from a Python + object (if argument) or converted to a Python object (if return value). +- Other types must match what's given, e.g. `pyo3::basic::CompareOp` for + `__richcmp__`'s second argument. +- For the comparison and arithmetic methods, extraction errors are not + propagated as exceptions, but lead to a return of `NotImplemented`. +- For some magic methods, the return values are not restricted by PyO3, but + checked by the Python interpreter. For example, `__str__` needs to return a + string object. This is indicated by `object (Python type)`. - Determines the "truthyness" of an object. +### Basic object customization - - `__call__(, ...) -> object` - here, any argument list can be defined - as for normal `pymethods` +- `__str__() -> object (str)` +- `__repr__() -> object (str)` + +- `__hash__() -> isize` + + Objects that compare equal must have the same hash value. Any type up to 64 bits may be returned instead of `isize`, PyO3 will convert to an isize automatically (wrapping unsigned types like `u64` and `usize`). +
+ Disabling Python's default hash + By default, all `#[pyclass]` types have a default hash implementation from Python. Types which should not be hashable can override this by setting `__hash__` to `None`. This is the same mechanism as for a pure-Python class. This is done like so: + + ```rust,no_run + # use pyo3::prelude::*; + # + #[pyclass] + struct NotHashable {} + + #[pymethods] + impl NotHashable { + #[classattr] + const __hash__: Option = None; + } + ``` + +
+ +- `__lt__(, object) -> object` +- `__le__(, object) -> object` +- `__eq__(, object) -> object` +- `__ne__(, object) -> object` +- `__gt__(, object) -> object` +- `__ge__(, object) -> object` + + The implementations of Python's "rich comparison" operators `<`, `<=`, `==`, `!=`, `>` and `>=` respectively. + + _Note that implementing any of these methods will cause Python not to generate a default `__hash__` implementation, so consider also implementing `__hash__`._ +
+ Return type + The return type will normally be `bool` or `PyResult`, however any Python object can be returned. +
+ +- `__richcmp__(, object, pyo3::basic::CompareOp) -> object` + + Implements Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`) in a single method. + The `CompareOp` argument indicates the comparison operation being performed. You can use + [`CompareOp::matches`] to adapt a Rust `std::cmp::Ordering` result to the requested comparison. + + _This method cannot be implemented in combination with any of `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__`, or `__ge__`._ + + _Note that implementing `__richcmp__` will cause Python not to generate a default `__hash__` implementation, so consider implementing `__hash__` when implementing `__richcmp__`._ +
+ Return type + The return type will normally be `PyResult`, but any Python object can be returned. + + If you want to leave some operations unimplemented, you can return `py.NotImplemented()` + for some of the operations: + + ```rust,no_run + use pyo3::class::basic::CompareOp; + use pyo3::types::PyNotImplemented; + + # use pyo3::prelude::*; + # use pyo3::BoundObject; + # + # #[pyclass] + # struct Number(i32); + # + #[pymethods] + impl Number { + fn __richcmp__<'py>(&self, other: &Self, op: CompareOp, py: Python<'py>) -> PyResult> { + match op { + CompareOp::Eq => Ok((self.0 == other.0).into_pyobject(py)?.into_any()), + CompareOp::Ne => Ok((self.0 != other.0).into_pyobject(py)?.into_any()), + _ => Ok(PyNotImplemented::get(py).into_any()), + } + } + } + ``` + + If the second argument `object` is not of the type specified in the + signature, the generated code will automatically `return NotImplemented`. +
+ +- `__getattr__(, object) -> object` +- `__getattribute__(, object) -> object` +
+ Differences between `__getattr__` and `__getattribute__` + As in Python, `__getattr__` is only called if the attribute is not found + by normal attribute lookup. `__getattribute__`, on the other hand, is + called for *every* attribute access. If it wants to access existing + attributes on `self`, it needs to be very careful not to introduce + infinite recursion, and use `baseclass.__getattribute__()`. +
+ +- `__setattr__(, value: object) -> ()` +- `__delattr__(, object) -> ()` + + Overrides attribute access. + +- `__bool__() -> bool` + + Determines the "truthyness" of an object. + +- `__call__(, ...) -> object` - here, any argument list can be defined + as for normal `pymethods` ### Iterable objects Iterators can be defined using these methods: - - `__iter__() -> object` - - `__next__() -> Option or IterNextOutput` ([see details](#returning-a-value-from-iteration)) +- `__iter__() -> object` +- `__next__() -> Option or IterNextOutput` ([see details](#returning-a-value-from-iteration)) Returning `None` from `__next__` indicates that that there are no further items. @@ -167,17 +171,17 @@ struct MyIterator { #[pymethods] impl MyIterator { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __next__(slf: PyRefMut<'_, Self>) -> Option { + fn __next__(slf: PyRefMut<'_, '_, Self>) -> Option { slf.iter.lock().unwrap().next() } } ``` In many cases you'll have a distinction between the type being iterated over -(i.e. the *iterable*) and the iterator it provides. In this case, the iterable +(i.e. the _iterable_) and the iterator it provides. In this case, the iterable only needs to implement `__iter__()` while the iterator must implement both `__iter__()` and `__next__()`. For example: @@ -191,11 +195,11 @@ struct Iter { #[pymethods] impl Iter { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { + fn __next__(mut slf: PyRefMut<'_, '_, Self>) -> Option { slf.inner.next() } } @@ -207,7 +211,7 @@ struct Container { #[pymethods] impl Container { - fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { + fn __iter__(slf: PyRef<'_, '_, Self>) -> PyResult> { let iter = Iter { inner: slf.iter.clone().into_iter(), }; @@ -229,15 +233,15 @@ documentation](https://docs.python.org/library/stdtypes.html#iterator-types). #### Returning a value from iteration This guide has so far shown how to use `Option` to implement yielding values -during iteration. In Python a generator can also return a value. This is done by +during iteration. In Python a generator can also return a value. This is done by raising a `StopIteration` exception. To express this in Rust, return `PyResult::Err` with a `PyStopIteration` as the error. ### Awaitable objects - - `__await__() -> object` - - `__aiter__() -> object` - - `__anext__() -> Option` +- `__await__() -> object` +- `__aiter__() -> object` +- `__anext__() -> Option` ### Mapping & Sequence types @@ -248,6 +252,7 @@ The Python C-API which PyO3 is built upon has separate "slots" for sequences and By default PyO3 reproduces the Python behaviour of filling both mapping and sequence slots. This makes sense for the "simple" case which matches Python, and also for sequences, where the mapping slot is used anyway to implement slice indexing. Mapping types usually will not want the sequence slots filled. Having them filled will lead to outcomes which may be unwanted, such as: + - The mapping type will successfully cast to [`PySequence`]. This may lead to consumers of the type handling it incorrectly. - Python provides a default implementation of `__iter__` for sequences, which calls `__getitem__` with consecutive positive integers starting from 0 until an `IndexError` is returned. Unless the mapping only contains consecutive positive integer keys, this `__iter__` implementation will likely not be the intended behavior. @@ -255,91 +260,92 @@ Use the `#[pyclass(mapping)]` annotation to instruct PyO3 to only fill the mappi Use the `#[pyclass(sequence)]` annotation to instruct PyO3 to fill the `sq_length` slot instead of the `mp_length` slot for `__len__`. This will help libraries such as `numpy` recognise the class as a sequence, however will also cause CPython to automatically add the sequence length to any negative indices before passing them to `__getitem__`. (`__getitem__`, `__setitem__` and `__delitem__` mapping slots are still used for sequences, for slice operations.) - - `__len__() -> usize` +- `__len__() -> usize` - Implements the built-in function `len()`. + Implements the built-in function `len()`. - - `__contains__(, object) -> bool` +- `__contains__(, object) -> bool` - Implements membership test operators. - Should return true if `item` is in `self`, false otherwise. - For objects that don’t define `__contains__()`, the membership test simply - traverses the sequence until it finds a match. + Implements membership test operators. + Should return true if `item` is in `self`, false otherwise. + For objects that don’t define `__contains__()`, the membership test simply + traverses the sequence until it finds a match. -
- Disabling Python's default contains +
+ Disabling Python's default contains - By default, all `#[pyclass]` types with an `__iter__` method support a - default implementation of the `in` operator. Types which do not want this - can override this by setting `__contains__` to `None`. This is the same - mechanism as for a pure-Python class. This is done like so: + By default, all `#[pyclass]` types with an `__iter__` method support a + default implementation of the `in` operator. Types which do not want this + can override this by setting `__contains__` to `None`. This is the same + mechanism as for a pure-Python class. This is done like so: - ```rust,no_run - # use pyo3::prelude::*; - # - #[pyclass] - struct NoContains {} + ```rust,no_run + # use pyo3::prelude::*; + # + #[pyclass] + struct NoContains {} - #[pymethods] - impl NoContains { - #[classattr] - const __contains__: Option = None; - } - ``` -
+ #[pymethods] + impl NoContains { + #[classattr] + const __contains__: Option = None; + } + ``` + +
- - `__getitem__(, object) -> object` +- `__getitem__(, object) -> object` - Implements retrieval of the `self[a]` element. + Implements retrieval of the `self[a]` element. - *Note:* Negative integer indexes are not handled specially by PyO3. - However, for classes with `#[pyclass(sequence)]`, when a negative index is - accessed via `PySequence::get_item`, the underlying C API already adjusts - the index to be positive. + _Note:_ Negative integer indexes are not handled specially by PyO3. + However, for classes with `#[pyclass(sequence)]`, when a negative index is + accessed via `PySequence::get_item`, the underlying C API already adjusts + the index to be positive. - - `__setitem__(, object, object) -> ()` +- `__setitem__(, object, object) -> ()` - Implements assignment to the `self[a]` element. - Should only be implemented if elements can be replaced. + Implements assignment to the `self[a]` element. + Should only be implemented if elements can be replaced. - Same behavior regarding negative indices as for `__getitem__`. + Same behavior regarding negative indices as for `__getitem__`. - - `__delitem__(, object) -> ()` +- `__delitem__(, object) -> ()` - Implements deletion of the `self[a]` element. - Should only be implemented if elements can be deleted. + Implements deletion of the `self[a]` element. + Should only be implemented if elements can be deleted. - Same behavior regarding negative indices as for `__getitem__`. + Same behavior regarding negative indices as for `__getitem__`. - * `fn __concat__(&self, other: impl FromPyObject) -> PyResult` +* `fn __concat__(&self, other: impl FromPyObject) -> PyResult` - Concatenates two sequences. - Used by the `+` operator, after trying the numeric addition via - the `__add__` and `__radd__` methods. + Concatenates two sequences. + Used by the `+` operator, after trying the numeric addition via + the `__add__` and `__radd__` methods. - * `fn __repeat__(&self, count: isize) -> PyResult` +* `fn __repeat__(&self, count: isize) -> PyResult` - Repeats the sequence `count` times. - Used by the `*` operator, after trying the numeric multiplication via - the `__mul__` and `__rmul__` methods. + Repeats the sequence `count` times. + Used by the `*` operator, after trying the numeric multiplication via + the `__mul__` and `__rmul__` methods. - * `fn __inplace_concat__(&self, other: impl FromPyObject) -> PyResult` +* `fn __inplace_concat__(&self, other: impl FromPyObject) -> PyResult` - Concatenates two sequences. - Used by the `+=` operator, after trying the numeric addition via - the `__iadd__` method. + Concatenates two sequences. + Used by the `+=` operator, after trying the numeric addition via + the `__iadd__` method. - * `fn __inplace_repeat__(&self, count: isize) -> PyResult` +* `fn __inplace_repeat__(&self, count: isize) -> PyResult` - Concatenates two sequences. - Used by the `*=` operator, after trying the numeric multiplication via - the `__imul__` method. + Concatenates two sequences. + Used by the `*=` operator, after trying the numeric multiplication via + the `__imul__` method. ### Descriptors - - `__get__(, object, object) -> object` - - `__set__(, object, object) -> ()` - - `__delete__(, object) -> ()` +- `__get__(, object, object) -> object` +- `__set__(, object, object) -> ()` +- `__delete__(, object) -> ()` ### Numeric types @@ -349,84 +355,84 @@ Binary arithmetic operations (`+`, `-`, `*`, `@`, `/`, `//`, `%`, `divmod()`, (If the `object` is not of the type specified in the signature, the generated code will automatically `return NotImplemented`.) - - `__add__(, object) -> object` - - `__radd__(, object) -> object` - - `__sub__(, object) -> object` - - `__rsub__(, object) -> object` - - `__mul__(, object) -> object` - - `__rmul__(, object) -> object` - - `__matmul__(, object) -> object` - - `__rmatmul__(, object) -> object` - - `__floordiv__(, object) -> object` - - `__rfloordiv__(, object) -> object` - - `__truediv__(, object) -> object` - - `__rtruediv__(, object) -> object` - - `__divmod__(, object) -> object` - - `__rdivmod__(, object) -> object` - - `__mod__(, object) -> object` - - `__rmod__(, object) -> object` - - `__lshift__(, object) -> object` - - `__rlshift__(, object) -> object` - - `__rshift__(, object) -> object` - - `__rrshift__(, object) -> object` - - `__and__(, object) -> object` - - `__rand__(, object) -> object` - - `__xor__(, object) -> object` - - `__rxor__(, object) -> object` - - `__or__(, object) -> object` - - `__ror__(, object) -> object` - - `__pow__(, object, object) -> object` - - `__rpow__(, object, object) -> object` +- `__add__(, object) -> object` +- `__radd__(, object) -> object` +- `__sub__(, object) -> object` +- `__rsub__(, object) -> object` +- `__mul__(, object) -> object` +- `__rmul__(, object) -> object` +- `__matmul__(, object) -> object` +- `__rmatmul__(, object) -> object` +- `__floordiv__(, object) -> object` +- `__rfloordiv__(, object) -> object` +- `__truediv__(, object) -> object` +- `__rtruediv__(, object) -> object` +- `__divmod__(, object) -> object` +- `__rdivmod__(, object) -> object` +- `__mod__(, object) -> object` +- `__rmod__(, object) -> object` +- `__lshift__(, object) -> object` +- `__rlshift__(, object) -> object` +- `__rshift__(, object) -> object` +- `__rrshift__(, object) -> object` +- `__and__(, object) -> object` +- `__rand__(, object) -> object` +- `__xor__(, object) -> object` +- `__rxor__(, object) -> object` +- `__or__(, object) -> object` +- `__ror__(, object) -> object` +- `__pow__(, object, object) -> object` +- `__rpow__(, object, object) -> object` In-place assignment operations (`+=`, `-=`, `*=`, `@=`, `/=`, `//=`, `%=`, `**=`, `<<=`, `>>=`, `&=`, `^=`, `|=`): - - `__iadd__(, object) -> ()` - - `__isub__(, object) -> ()` - - `__imul__(, object) -> ()` - - `__imatmul__(, object) -> ()` - - `__itruediv__(, object) -> ()` - - `__ifloordiv__(, object) -> ()` - - `__imod__(, object) -> ()` - - `__ipow__(, object, object) -> ()` - - `__ilshift__(, object) -> ()` - - `__irshift__(, object) -> ()` - - `__iand__(, object) -> ()` - - `__ixor__(, object) -> ()` - - `__ior__(, object) -> ()` +- `__iadd__(, object) -> ()` +- `__isub__(, object) -> ()` +- `__imul__(, object) -> ()` +- `__imatmul__(, object) -> ()` +- `__itruediv__(, object) -> ()` +- `__ifloordiv__(, object) -> ()` +- `__imod__(, object) -> ()` +- `__ipow__(, object, object) -> ()` +- `__ilshift__(, object) -> ()` +- `__irshift__(, object) -> ()` +- `__iand__(, object) -> ()` +- `__ixor__(, object) -> ()` +- `__ior__(, object) -> ()` Unary operations (`-`, `+`, `abs()` and `~`): - - `__pos__() -> object` - - `__neg__() -> object` - - `__abs__() -> object` - - `__invert__() -> object` +- `__pos__() -> object` +- `__neg__() -> object` +- `__abs__() -> object` +- `__invert__() -> object` Coercions: - - `__index__() -> object (int)` - - `__int__() -> object (int)` - - `__float__() -> object (float)` +- `__index__() -> object (int)` +- `__int__() -> object (int)` +- `__float__() -> object (float)` ### Buffer objects - - `__getbuffer__(, *mut ffi::Py_buffer, flags) -> ()` - - `__releasebuffer__(, *mut ffi::Py_buffer) -> ()` - Errors returned from `__releasebuffer__` will be sent to `sys.unraiseablehook`. It is strongly advised to never return an error from `__releasebuffer__`, and if it really is necessary, to make best effort to perform any required freeing operations before returning. `__releasebuffer__` will not be called a second time; anything not freed will be leaked. +- `__getbuffer__(, *mut ffi::Py_buffer, flags) -> ()` +- `__releasebuffer__(, *mut ffi::Py_buffer) -> ()` + Errors returned from `__releasebuffer__` will be sent to `sys.unraiseablehook`. It is strongly advised to never return an error from `__releasebuffer__`, and if it really is necessary, to make best effort to perform any required freeing operations before returning. `__releasebuffer__` will not be called a second time; anything not freed will be leaked. ### Garbage Collector Integration If your type owns references to other Python objects, you will need to integrate -with Python's garbage collector so that the GC is aware of those references. To -do this, implement the two methods `__traverse__` and `__clear__`. These +with Python's garbage collector so that the GC is aware of those references. To +do this, implement the two methods `__traverse__` and `__clear__`. These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API. `__traverse__` must call `visit.call()` for each reference to another Python -object. `__clear__` must clear out any mutable references to other Python +object. `__clear__` must clear out any mutable references to other Python objects (thus breaking reference cycles). Immutable references do not have to be cleared, as every cycle must contain at least one mutable reference. - - `__traverse__(, pyo3::class::gc::PyVisit<'_>) -> Result<(), pyo3::class::gc::PyTraverseError>` - - `__clear__() -> ()` +- `__traverse__(, pyo3::class::gc::PyVisit<'_>) -> Result<(), pyo3::class::gc::PyTraverseError>` +- `__clear__() -> ()` Example: diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index dfcd5dd2203..dccace7c352 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -1126,7 +1126,7 @@ fn parse_method_attributes(attrs: &mut Vec) -> Result` or `slf: PyRefMut<'_, '_, Self>`."; fn ensure_signatures_on_valid_method( fn_type: &FnType, diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 82d4d54c7ec..c9ebe5d20af 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -2071,34 +2071,34 @@ impl<'a> PyClassImplsBuilder<'a> { let cls = self.cls; if self.attr.options.frozen.is_some() { quote! { - impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py, false> for &'a #cls + impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder #cls { - type Holder = ::std::option::Option<#pyo3_path::PyRef<'py, #cls>>; + type Holder = ::std::option::Option<#pyo3_path::PyRef<'a, 'py, #cls>>; #[inline] - fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult { + fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult { #pyo3_path::impl_::extract_argument::extract_pyclass_ref(obj, holder) } } } } else { quote! { - impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py, false> for &'a #cls + impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder #cls { - type Holder = ::std::option::Option<#pyo3_path::PyRef<'py, #cls>>; + type Holder = ::std::option::Option<#pyo3_path::PyRef<'a, 'py, #cls>>; #[inline] - fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult { + fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult { #pyo3_path::impl_::extract_argument::extract_pyclass_ref(obj, holder) } } - impl<'a, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'py, false> for &'a mut #cls + 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::PyRefMut<'py, #cls>>; + type Holder = ::std::option::Option<#pyo3_path::PyRefMut<'a, 'py, #cls>>; #[inline] - fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'a mut Self::Holder) -> #pyo3_path::PyResult { + fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult { #pyo3_path::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder) } } diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index b4637b48012..a3138474d85 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -1142,7 +1142,7 @@ impl Ty { extract_error_mode, holders, arg, - quote! { #ident.as_ptr() }, + quote! { #ident }, ctx ), Ty::IPowModulo => extract_object( @@ -1470,6 +1470,15 @@ impl SlotFragmentDef { let method = syn::Ident::new(fragment, Span::call_site()); let wrapper_ident = format_ident!("__pymethod_{}__", fragment); let arg_types: &Vec<_> = &arguments.iter().map(|arg| arg.ffi_type(ctx)).collect(); + let nn = arguments + .iter() + .enumerate() + .filter(|(_, arg)| matches!(arg, Ty::NonNullObject)) + .map(|(i, _)| { + let i = format_ident!("arg{}", i); + quote! { let #i = #i.as_ptr(); } + }) + .collect::(); let arg_idents: &Vec<_> = &(0..arguments.len()) .map(|i| format_ident!("arg{}", i)) .collect(); @@ -1494,6 +1503,7 @@ impl SlotFragmentDef { #(#arg_idents: #arg_types),* ) -> #pyo3_path::PyResult<#ret_ty> { let _slf = _raw_slf; + #nn #holders #body } diff --git a/pytests/src/awaitable.rs b/pytests/src/awaitable.rs index 01a93c70a0d..451a925f9bd 100644 --- a/pytests/src/awaitable.rs +++ b/pytests/src/awaitable.rs @@ -23,11 +23,11 @@ impl IterAwaitable { } } - fn __await__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__<'a, 'py>(pyself: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { pyself } - fn __iter__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(pyself: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { pyself } @@ -59,15 +59,15 @@ impl FutureAwaitable { } } - fn __await__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__<'a, 'py>(pyself: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { pyself } - fn __iter__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(pyself: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { pyself } - fn __next__(mut pyself: PyRefMut<'_, Self>) -> PyResult> { + fn __next__<'a, 'py>(mut pyself: PyRefMut<'a, 'py, Self>) -> PyResult> { match pyself.result { Some(_) => match pyself.result.take().unwrap() { Ok(v) => Err(PyStopIteration::new_err(v)), diff --git a/src/conversion.rs b/src/conversion.rs index 71b9b83efa2..0a5ce374e08 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -363,20 +363,20 @@ where } } -impl<'py, T> FromPyObject<'_, 'py> for PyRef<'py, T> +impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRef<'a, 'py, T> where T: PyClass, { - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { obj.downcast::()?.try_borrow().map_err(Into::into) } } -impl<'py, T> FromPyObject<'_, 'py> for PyRefMut<'py, T> +impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRefMut<'a, 'py, T> where T: PyClass, { - fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { + fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult { obj.downcast::()?.try_borrow_mut().map_err(Into::into) } } diff --git a/src/impl_/extract_argument.rs b/src/impl_/extract_argument.rs index 91d8dd42ce3..a1635a31141 100644 --- a/src/impl_/extract_argument.rs +++ b/src/impl_/extract_argument.rs @@ -20,43 +20,44 @@ type PyArg<'py> = Borrowed<'py, 'py, PyAny>; /// will be dropped as soon as the pyfunction call ends. /// /// There exists a trivial blanket implementation for `T: FromPyObject` with `Holder = ()`. -pub trait PyFunctionArgument<'a, 'py, const IS_OPTION: bool>: Sized + 'a { +pub trait PyFunctionArgument<'a, 'holder, 'py, const IS_OPTION: bool>: Sized { type Holder: FunctionArgumentHolder; - fn extract(obj: &'a Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> PyResult; + fn extract(obj: &'a Bound<'py, PyAny>, holder: &'holder mut Self::Holder) -> PyResult; } -impl<'a, 'py, T> PyFunctionArgument<'a, 'py, false> for T +impl<'a, 'holder, 'py, T> PyFunctionArgument<'a, 'holder, 'py, false> for T where - T: FromPyObject<'a, 'py> + 'a, + T: FromPyObject<'a, 'py>, { type Holder = (); #[inline] - fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut ()) -> PyResult { + fn extract(obj: &'a Bound<'py, PyAny>, _: &'holder mut ()) -> PyResult { obj.extract() } } -impl<'a, 'py, T: 'py> PyFunctionArgument<'a, 'py, false> for &'a Bound<'py, T> +impl<'a: 'holder, 'holder, 'py, T> PyFunctionArgument<'a, 'holder, 'py, false> + for &'holder Bound<'py, T> where T: PyTypeCheck, { type Holder = (); #[inline] - fn extract(obj: &'a Bound<'py, PyAny>, _: &'a mut ()) -> PyResult { + fn extract(obj: &'a Bound<'py, PyAny>, _: &'holder mut ()) -> PyResult { obj.downcast().map_err(Into::into) } } -impl<'a, 'py, T> PyFunctionArgument<'a, 'py, true> for Option +impl<'a, 'holder, 'py, T> PyFunctionArgument<'a, 'holder, 'py, true> for Option where - T: PyFunctionArgument<'a, 'py, false>, // inner `Option`s will use `FromPyObject` + T: PyFunctionArgument<'a, 'holder, 'py, false>, // inner `Option`s will use `FromPyObject` { type Holder = T::Holder; #[inline] - fn extract(obj: &'a Bound<'py, PyAny>, holder: &'a mut T::Holder) -> PyResult { + fn extract(obj: &'a Bound<'py, PyAny>, holder: &'holder mut T::Holder) -> PyResult { if obj.is_none() { Ok(None) } else { @@ -66,13 +67,13 @@ where } #[cfg(all(Py_LIMITED_API, not(Py_3_10)))] -impl<'a> PyFunctionArgument<'a, '_, false> for &'a str { +impl<'a: 'holder, 'holder> PyFunctionArgument<'a, 'holder, '_, false> for &'holder str { type Holder = Option>; #[inline] fn extract( obj: &'a Bound<'_, PyAny>, - holder: &'a mut Option>, + holder: &'holder mut Option>, ) -> PyResult { Ok(holder.insert(obj.extract()?)) } @@ -93,30 +94,30 @@ impl FunctionArgumentHolder for Option { } #[inline] -pub fn extract_pyclass_ref<'a, 'py: 'a, T: PyClass>( +pub fn extract_pyclass_ref<'a, 'holder, 'py: 'a, T: PyClass>( obj: &'a Bound<'py, PyAny>, - holder: &'a mut Option>, -) -> PyResult<&'a T> { + holder: &'holder mut Option>, +) -> PyResult<&'holder T> { Ok(&*holder.insert(obj.extract()?)) } #[inline] -pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( +pub fn extract_pyclass_ref_mut<'a, 'holder, 'py: 'a, T: PyClass>( obj: &'a Bound<'py, PyAny>, - holder: &'a mut Option>, -) -> PyResult<&'a mut T> { + holder: &'holder mut Option>, +) -> PyResult<&'holder mut T> { Ok(&mut *holder.insert(obj.extract()?)) } /// The standard implementation of how PyO3 extracts a `#[pyfunction]` or `#[pymethod]` function argument. #[doc(hidden)] -pub fn extract_argument<'a, 'py, T, const IS_OPTION: bool>( +pub fn extract_argument<'a, 'holder, 'py, T, const IS_OPTION: bool>( obj: &'a Bound<'py, PyAny>, - holder: &'a mut T::Holder, + holder: &'holder mut T::Holder, arg_name: &str, ) -> PyResult where - T: PyFunctionArgument<'a, 'py, IS_OPTION>, + T: PyFunctionArgument<'a, 'holder, 'py, IS_OPTION>, { match PyFunctionArgument::extract(obj, holder) { Ok(value) => Ok(value), @@ -127,14 +128,14 @@ where /// Alternative to [`extract_argument`] used for `Option` arguments. This is necessary because Option<&T> /// does not implement `PyFunctionArgument` for `T: PyClass`. #[doc(hidden)] -pub fn extract_optional_argument<'a, 'py, T, const IS_OPTION: bool>( +pub fn extract_optional_argument<'a, 'holder, 'py, T, const IS_OPTION: bool>( obj: Option<&'a Bound<'py, PyAny>>, - holder: &'a mut T::Holder, + holder: &'holder mut T::Holder, arg_name: &str, default: fn() -> Option, ) -> PyResult> where - T: PyFunctionArgument<'a, 'py, IS_OPTION>, + T: PyFunctionArgument<'a, 'holder, 'py, IS_OPTION>, { match obj { Some(obj) => { @@ -151,14 +152,14 @@ where /// Alternative to [`extract_argument`] used when the argument has a default value provided by an annotation. #[doc(hidden)] -pub fn extract_argument_with_default<'a, 'py, T, const IS_OPTION: bool>( +pub fn extract_argument_with_default<'a, 'holder, 'py, T, const IS_OPTION: bool>( obj: Option<&'a Bound<'py, PyAny>>, - holder: &'a mut T::Holder, + holder: &'holder mut T::Holder, arg_name: &str, default: fn() -> T, ) -> PyResult where - T: PyFunctionArgument<'a, 'py, IS_OPTION>, + T: PyFunctionArgument<'a, 'holder, 'py, IS_OPTION>, { match obj { Some(obj) => extract_argument(obj, holder, arg_name), diff --git a/src/impl_/pyclass.rs b/src/impl_/pyclass.rs index c646b35ecfe..7962a555338 100644 --- a/src/impl_/pyclass.rs +++ b/src/impl_/pyclass.rs @@ -1367,10 +1367,10 @@ impl< /// ensures `obj` is not mutably aliased #[inline] -unsafe fn ensure_no_mutable_alias<'py, ClassT: PyClass>( +unsafe fn ensure_no_mutable_alias<'a, 'py: 'a, ClassT: PyClass>( py: Python<'py>, - obj: &*mut ffi::PyObject, -) -> Result, PyBorrowError> { + obj: &'a *mut ffi::PyObject, +) -> Result, PyBorrowError> { unsafe { BoundRef::ref_from_ptr(py, obj) .downcast_unchecked::() diff --git a/src/impl_/pymethods.rs b/src/impl_/pymethods.rs index 13039912762..63006853fb8 100644 --- a/src/impl_/pymethods.rs +++ b/src/impl_/pymethods.rs @@ -654,7 +654,13 @@ impl<'a, 'py> BoundRef<'a, 'py, PyAny> { } } -impl<'a, 'py, T: PyClass> TryFrom> for PyRef<'py, T> { +impl<'a, 'py, T: PyClass> BoundRef<'a, 'py, T> { + pub fn try_borrow(self) -> Result, PyBorrowError> { + PyRef::try_borrow(self.0.as_borrowed()) + } +} + +impl<'a, 'py, T: PyClass> TryFrom> for PyRef<'a, 'py, T> { type Error = PyBorrowError; #[inline] fn try_from(value: BoundRef<'a, 'py, T>) -> Result { @@ -662,7 +668,7 @@ impl<'a, 'py, T: PyClass> TryFrom> for PyRef<'py, T> { } } -impl<'a, 'py, T: PyClass> TryFrom> for PyRefMut<'py, T> { +impl<'a, 'py, T: PyClass> TryFrom> for PyRefMut<'a, 'py, T> { type Error = PyBorrowMutError; #[inline] fn try_from(value: BoundRef<'a, 'py, T>) -> Result { diff --git a/src/instance.rs b/src/instance.rs index d6b43d500a0..2fbb99d4a40 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -264,8 +264,8 @@ where /// [`try_borrow`](#method.try_borrow). #[inline] #[track_caller] - pub fn borrow(&self) -> PyRef<'py, T> { - PyRef::borrow(self) + pub fn borrow<'a>(&'a self) -> PyRef<'a, 'py, T> { + PyRef::borrow(self.as_borrowed()) } /// Mutably borrows the value `T`. @@ -299,11 +299,11 @@ where /// [`try_borrow_mut`](#method.try_borrow_mut). #[inline] #[track_caller] - pub fn borrow_mut(&self) -> PyRefMut<'py, T> + pub fn borrow_mut<'a>(&'a self) -> PyRefMut<'a, 'py, T> where T: PyClass, { - PyRefMut::borrow(self) + PyRefMut::borrow(self.as_borrowed()) } /// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed. @@ -314,8 +314,8 @@ where /// /// For frozen classes, the simpler [`get`][Self::get] is available. #[inline] - pub fn try_borrow(&self) -> Result, PyBorrowError> { - PyRef::try_borrow(self) + pub fn try_borrow<'a>(&'a self) -> Result, PyBorrowError> { + self.as_borrowed().try_borrow() } /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed. @@ -324,11 +324,11 @@ where /// /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). #[inline] - pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> + pub fn try_borrow_mut<'a>(&'a self) -> Result, PyBorrowMutError> where T: PyClass, { - PyRefMut::try_borrow(self) + PyRefMut::try_borrow(self.as_borrowed()) } /// Provide an immutable borrow of the value `T` without acquiring the GIL. @@ -711,6 +711,22 @@ impl<'a, 'py, T> Borrowed<'a, 'py, T> { { FromPyObject::extract(self.to_any()) } + + #[inline] + pub(crate) fn try_borrow(self) -> Result, PyBorrowError> + where + T: PyClass, + { + PyRef::try_borrow(self) + } + + #[inline] + pub(crate) fn try_borrow_mut(self) -> Result, PyBorrowMutError> + where + T: PyClass, + { + PyRefMut::try_borrow(self) + } } impl<'a, 'py> Borrowed<'a, 'py, PyAny> { @@ -1188,8 +1204,8 @@ where /// [`try_borrow`](#method.try_borrow). #[inline] #[track_caller] - pub fn borrow<'py>(&'py self, py: Python<'py>) -> PyRef<'py, T> { - self.bind(py).borrow() + pub fn borrow<'a, 'py>(&'a self, py: Python<'py>) -> PyRef<'a, 'py, T> { + PyRef::borrow(self.bind_borrowed(py)) } /// Mutably borrows the value `T`. @@ -1225,11 +1241,11 @@ where /// [`try_borrow_mut`](#method.try_borrow_mut). #[inline] #[track_caller] - pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T> + pub fn borrow_mut<'a, 'py>(&'a self, py: Python<'py>) -> PyRefMut<'a, 'py, T> where T: PyClass, { - self.bind(py).borrow_mut() + PyRefMut::borrow(self.bind_borrowed(py)) } /// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed. @@ -1242,8 +1258,11 @@ where /// /// Equivalent to `self.bind(py).try_borrow()` - see [`Bound::try_borrow`]. #[inline] - pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result, PyBorrowError> { - self.bind(py).try_borrow() + pub fn try_borrow<'a, 'py>( + &'a self, + py: Python<'py>, + ) -> Result, PyBorrowError> { + self.bind_borrowed(py).try_borrow() } /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed. @@ -1254,14 +1273,14 @@ where /// /// Equivalent to `self.bind(py).try_borrow_mut()` - see [`Bound::try_borrow_mut`]. #[inline] - pub fn try_borrow_mut<'py>( - &'py self, + pub fn try_borrow_mut<'a, 'py>( + &'a self, py: Python<'py>, - ) -> Result, PyBorrowMutError> + ) -> Result, PyBorrowMutError> where T: PyClass, { - self.bind(py).try_borrow_mut() + self.bind_borrowed(py).try_borrow_mut() } /// Provide an immutable borrow of the value `T` without acquiring the GIL. @@ -1749,20 +1768,20 @@ impl std::convert::From> for Py { } } -impl<'a, T> std::convert::From> for Py +impl<'py, T> std::convert::From> for Py where T: PyClass, { - fn from(pyref: PyRef<'a, T>) -> Self { + fn from(pyref: PyRef<'_, 'py, T>) -> Self { unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) } } } -impl<'a, T> std::convert::From> for Py +impl<'py, T> std::convert::From> for Py where T: PyClass, { - fn from(pyref: PyRefMut<'a, T>) -> Self { + fn from(pyref: PyRefMut<'_, 'py, T>) -> Self { unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) } } } @@ -1887,7 +1906,7 @@ impl PyObject { /// class_bound.borrow_mut().i += 1; /// /// // Alternatively you can get a `PyRefMut` directly - /// let class_ref: PyRefMut<'_, Class> = class.extract(py)?; + /// let class_ref: PyRefMut<'_, '_, Class> = class.extract(py)?; /// assert_eq!(class_ref.i, 1); /// Ok(()) /// }) diff --git a/src/marker.rs b/src/marker.rs index a4a7be8c0c5..33f16a24807 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -278,8 +278,8 @@ mod nightly { // This means that PyString, PyList, etc all inherit !Ungil from this. impl !Ungil for crate::PyAny {} - impl !Ungil for crate::PyRef<'_, T> {} - impl !Ungil for crate::PyRefMut<'_, T> {} + impl !Ungil for crate::PyRef<'_, '_, T> {} + impl !Ungil for crate::PyRefMut<'_, '_, T> {} // FFI pointees impl !Ungil for crate::ffi::PyObject {} diff --git a/src/pycell.rs b/src/pycell.rs index ff279000daf..40eae85673a 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -97,7 +97,7 @@ //! //! // We borrow the guard and then dereference //! // it to get a mutable reference to Number -//! let mut guard: PyRefMut<'_, Number> = n.bind(py).borrow_mut(); +//! let mut guard: PyRefMut<'_, '_, Number> = n.bind(py).borrow_mut(); //! let n_mutable: &mut Number = &mut *guard; //! //! n_mutable.increment(); @@ -195,10 +195,8 @@ use crate::conversion::IntoPyObject; use crate::exceptions::PyRuntimeError; -use crate::ffi_ptr_ext::FfiPtrExt; use crate::internal_tricks::{ptr_from_mut, ptr_from_ref}; use crate::pyclass::{boolean_struct::False, PyClass}; -use crate::types::any::PyAnyMethods; use crate::{ffi, Borrowed, Bound, PyErr, Python}; use std::convert::Infallible; use std::fmt; @@ -236,7 +234,7 @@ use impl_::{PyClassBorrowChecker, PyClassObjectLayout}; /// (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" }) /// } /// -/// fn format(slf: PyRef<'_, Self>) -> String { +/// fn format(slf: PyRef<'_, '_, Self>) -> String { /// // We can get *mut ffi::PyObject from PyRef /// let refcnt = unsafe { pyo3::ffi::Py_REFCNT(slf.as_ptr()) }; /// // We can get &Self::BaseType by as_ref @@ -246,26 +244,24 @@ use impl_::{PyClassBorrowChecker, PyClassObjectLayout}; /// } /// # Python::with_gil(|py| { /// # let sub = Py::new(py, Child::new()).unwrap(); -/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 4)', sub.format()"); +/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 3)', sub.format()"); /// # }); /// ``` /// /// See the [module-level documentation](self) for more information. #[repr(transparent)] -pub struct PyRef<'p, T: PyClass> { - // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to - // store `Borrowed` here instead, avoiding reference counting overhead. - inner: Bound<'p, T>, +pub struct PyRef<'a, 'py, T: PyClass> { + inner: Borrowed<'a, 'py, T>, } -impl<'p, T: PyClass> PyRef<'p, T> { +impl<'py, T: PyClass> PyRef<'_, 'py, T> { /// Returns a `Python` token that is bound to the lifetime of the `PyRef`. - pub fn py(&self) -> Python<'p> { + pub fn py(&self) -> Python<'py> { self.inner.py() } } -impl AsRef for PyRef<'_, T> +impl AsRef for PyRef<'_, '_, T> where T: PyClass, U: PyClass, @@ -275,7 +271,7 @@ where } } -impl<'py, T: PyClass> PyRef<'py, T> { +impl<'a, 'py, T: PyClass> PyRef<'a, 'py, T> { /// Returns the raw FFI pointer represented by self. /// /// # Safety @@ -297,24 +293,24 @@ impl<'py, T: PyClass> PyRef<'py, T> { /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). #[inline] pub fn into_ptr(self) -> *mut ffi::PyObject { - self.inner.clone().into_ptr() + self.inner.to_owned().into_ptr() } #[track_caller] - pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self { + pub(crate) fn borrow(obj: Borrowed<'a, 'py, T>) -> Self { Self::try_borrow(obj).expect("Already mutably borrowed") } - pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result { + pub(crate) fn try_borrow(obj: Borrowed<'a, 'py, T>) -> Result { let cell = obj.get_class_object(); cell.ensure_threadsafe(); cell.borrow_checker() .try_borrow() - .map(|_| Self { inner: obj.clone() }) + .map(|_| Self { inner: obj }) } } -impl<'p, T, U> PyRef<'p, T> +impl<'a, 'py, T, U> PyRef<'a, 'py, T> where T: PyClass, U: PyClass, @@ -353,7 +349,7 @@ where /// .add_subclass(Base2 { name2: "base2" }) /// .add_subclass(Self { name3: "sub" }) /// } - /// fn name(slf: PyRef<'_, Self>) -> String { + /// fn name(slf: PyRef<'_, '_, Self>) -> String { /// let subname = slf.name3; /// let super_ = slf.into_super(); /// format!("{} {} {}", super_.as_ref().name1, super_.name2, subname) @@ -364,15 +360,9 @@ where /// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'") /// # }); /// ``` - pub fn into_super(self) -> PyRef<'p, U> { - let py = self.py(); + pub fn into_super(self) -> PyRef<'a, 'py, U> { PyRef { - inner: unsafe { - ManuallyDrop::new(self) - .as_ptr() - .assume_owned_unchecked(py) - .downcast_into_unchecked() - }, + inner: unsafe { ManuallyDrop::new(self).inner.to_any().downcast_unchecked() }, } } @@ -410,7 +400,7 @@ where /// fn sub_name_len(&self) -> usize { /// self.sub_name.len() /// } - /// fn format_name_lengths(slf: PyRef<'_, Self>) -> String { + /// fn format_name_lengths(slf: PyRef<'_, '_, Self>) -> String { /// format!("{} {}", slf.as_super().base_name_len(), slf.sub_name_len()) /// } /// } @@ -419,17 +409,17 @@ where /// # pyo3::py_run!(py, sub, "assert sub.format_name_lengths() == '9 8'") /// # }); /// ``` - pub fn as_super(&self) -> &PyRef<'p, U> { - let ptr = ptr_from_ref::>(&self.inner) - // `Bound` has the same layout as `Bound` - .cast::>() - // `Bound` has the same layout as `PyRef` - .cast::>(); + pub fn as_super(&self) -> &PyRef<'a, 'py, U> { + let ptr = ptr_from_ref::>(&self.inner) + // `Borrowed` has the same layout as `Borrowed` + .cast::>() + // `Borrowed` has the same layout as `PyRef` + .cast::>(); unsafe { &*ptr } } } -impl Deref for PyRef<'_, T> { +impl Deref for PyRef<'_, '_, T> { type Target = T; #[inline] @@ -438,7 +428,7 @@ impl Deref for PyRef<'_, T> { } } -impl Drop for PyRef<'_, T> { +impl Drop for PyRef<'_, '_, T> { fn drop(&mut self) { self.inner .get_class_object() @@ -447,27 +437,27 @@ impl Drop for PyRef<'_, T> { } } -impl<'py, T: PyClass> IntoPyObject<'py> for PyRef<'py, T> { +impl<'a, 'py, T: PyClass> IntoPyObject<'py> for PyRef<'a, 'py, T> { type Target = T; - type Output = Bound<'py, T>; + type Output = Borrowed<'a, 'py, T>; type Error = Infallible; fn into_pyobject(self, _py: Python<'py>) -> Result { - Ok(self.inner.clone()) + Ok(self.inner) } } -impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRef<'py, T> { +impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &PyRef<'a, 'py, T> { type Target = T; type Output = Borrowed<'a, 'py, T>; type Error = Infallible; fn into_pyobject(self, _py: Python<'py>) -> Result { - Ok(self.inner.as_borrowed()) + Ok(self.inner) } } -impl fmt::Debug for PyRef<'_, T> { +impl fmt::Debug for PyRef<'_, '_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } @@ -477,20 +467,18 @@ impl fmt::Debug for PyRef<'_, T> { /// /// See the [module-level documentation](self) for more information. #[repr(transparent)] -pub struct PyRefMut<'p, T: PyClass> { - // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to - // store `Borrowed` here instead, avoiding reference counting overhead. - inner: Bound<'p, T>, +pub struct PyRefMut<'a, 'py, T: PyClass> { + inner: Borrowed<'a, 'py, T>, } -impl<'p, T: PyClass> PyRefMut<'p, T> { +impl<'py, T: PyClass> PyRefMut<'_, 'py, T> { /// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`. - pub fn py(&self) -> Python<'p> { + pub fn py(&self) -> Python<'py> { self.inner.py() } } -impl AsRef for PyRefMut<'_, T> +impl AsRef for PyRefMut<'_, '_, T> where T: PyClass, U: PyClass, @@ -500,7 +488,7 @@ where } } -impl AsMut for PyRefMut<'_, T> +impl AsMut for PyRefMut<'_, '_, T> where T: PyClass, U: PyClass, @@ -510,7 +498,7 @@ where } } -impl<'py, T: PyClass> PyRefMut<'py, T> { +impl<'a, 'py, T: PyClass> PyRefMut<'a, 'py, T> { /// Returns the raw FFI pointer represented by self. /// /// # Safety @@ -532,30 +520,30 @@ impl<'py, T: PyClass> PyRefMut<'py, T> { /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). #[inline] pub fn into_ptr(self) -> *mut ffi::PyObject { - self.inner.clone().into_ptr() + self.inner.to_owned().into_ptr() } #[inline] #[track_caller] - pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self { + pub(crate) fn borrow(obj: Borrowed<'a, 'py, T>) -> Self { Self::try_borrow(obj).expect("Already borrowed") } - pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result { + pub(crate) fn try_borrow(obj: Borrowed<'a, 'py, T>) -> Result { let cell = obj.get_class_object(); cell.ensure_threadsafe(); cell.borrow_checker() .try_borrow_mut() - .map(|_| Self { inner: obj.clone() }) + .map(|_| Self { inner: obj }) } - pub(crate) fn downgrade(slf: &Self) -> &PyRef<'py, T> { + pub(crate) fn downgrade(slf: &Self) -> &PyRef<'a, 'py, T> { // `PyRefMut` and `PyRef` have the same layout unsafe { &*ptr_from_ref(slf).cast() } } } -impl<'p, T, U> PyRefMut<'p, T> +impl<'a, 'py, T, U> PyRefMut<'a, 'py, T> where T: PyClass, U: PyClass, @@ -563,15 +551,9 @@ where /// Gets a `PyRef`. /// /// See [`PyRef::into_super`] for more. - pub fn into_super(self) -> PyRefMut<'p, U> { - let py = self.py(); + pub fn into_super(self) -> PyRefMut<'a, 'py, U> { PyRefMut { - inner: unsafe { - ManuallyDrop::new(self) - .as_ptr() - .assume_owned_unchecked(py) - .downcast_into_unchecked() - }, + inner: unsafe { ManuallyDrop::new(self).inner.to_any().downcast_unchecked() }, } } @@ -582,18 +564,18 @@ where /// can also be chained to access the super-superclass (and so on). /// /// See [`PyRef::as_super`] for more. - pub fn as_super(&mut self) -> &mut PyRefMut<'p, U> { - let ptr = ptr_from_mut::>(&mut self.inner) - // `Bound` has the same layout as `Bound` - .cast::>() - // `Bound` has the same layout as `PyRefMut`, + pub fn as_super(&mut self) -> &mut PyRefMut<'a, 'py, U> { + let ptr = ptr_from_mut::>(&mut self.inner) + // `Borrowed` has the same layout as `Borrowed` + .cast::>() + // `Borrowed` has the same layout as `PyRefMut`, // and the mutable borrow on `self` prevents aliasing - .cast::>(); + .cast::>(); unsafe { &mut *ptr } } } -impl> Deref for PyRefMut<'_, T> { +impl> Deref for PyRefMut<'_, '_, T> { type Target = T; #[inline] @@ -602,14 +584,14 @@ impl> Deref for PyRefMut<'_, T> { } } -impl> DerefMut for PyRefMut<'_, T> { +impl> DerefMut for PyRefMut<'_, '_, T> { #[inline] fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.inner.get_class_object().get_ptr() } } } -impl> Drop for PyRefMut<'_, T> { +impl> Drop for PyRefMut<'_, '_, T> { fn drop(&mut self) { self.inner .get_class_object() @@ -618,17 +600,17 @@ impl> Drop for PyRefMut<'_, T> { } } -impl<'py, T: PyClass> IntoPyObject<'py> for PyRefMut<'py, T> { +impl<'py, T: PyClass> IntoPyObject<'py> for PyRefMut<'_, 'py, T> { type Target = T; type Output = Bound<'py, T>; type Error = Infallible; fn into_pyobject(self, _py: Python<'py>) -> Result { - Ok(self.inner.clone()) + Ok(self.inner.to_owned()) } } -impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRefMut<'py, T> { +impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRefMut<'_, 'py, T> { type Target = T; type Output = Borrowed<'a, 'py, T>; type Error = Infallible; @@ -638,7 +620,7 @@ impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRefMut<'py } } -impl + fmt::Debug> fmt::Debug for PyRefMut<'_, T> { +impl + fmt::Debug> fmt::Debug for PyRefMut<'_, '_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.deref(), f) } @@ -759,13 +741,13 @@ mod tests { crate::Py::new(py, init).expect("allocation error") } - fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) { + fn get_values(self_: PyRef<'_, '_, Self>) -> (usize, usize, usize) { let val1 = self_.as_super().as_super().val1; let val2 = self_.as_super().val2; (val1, val2, self_.val3) } - fn double_values(mut self_: PyRefMut<'_, Self>) { + fn double_values(mut self_: PyRefMut<'_, '_, Self>) { self_.as_super().as_super().val1 *= 2; self_.as_super().val2 *= 2; self_.val3 *= 2; diff --git a/src/pycell/impl_.rs b/src/pycell/impl_.rs index b59314bc54a..db13fcd42fc 100644 --- a/src/pycell/impl_.rs +++ b/src/pycell/impl_.rs @@ -464,37 +464,39 @@ mod tests { // Cannot take any other mutable or immutable borrows whilst the object is borrowed mutably assert!(mmm_bound - .extract::>() + .extract::>() .is_err()); assert!(mmm_bound - .extract::>() + .extract::>() .is_err()); - assert!(mmm_bound.extract::>().is_err()); + assert!(mmm_bound.extract::>().is_err()); assert!(mmm_bound - .extract::>() + .extract::>() .is_err()); assert!(mmm_bound - .extract::>() + .extract::>() + .is_err()); + assert!(mmm_bound + .extract::>() .is_err()); - assert!(mmm_bound.extract::>().is_err()); // With the borrow dropped, all other borrow attempts will succeed drop(mmm_refmut); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); + assert!(mmm_bound.extract::>().is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); + assert!(mmm_bound.extract::>().is_ok()); }) } @@ -515,32 +517,34 @@ mod tests { // Further immutable borrows are ok assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); + assert!(mmm_bound.extract::>().is_ok()); // Further mutable borrows are not ok assert!(mmm_bound - .extract::>() + .extract::>() + .is_err()); + assert!(mmm_bound + .extract::>() .is_err()); assert!(mmm_bound - .extract::>() + .extract::>() .is_err()); - assert!(mmm_bound.extract::>().is_err()); // With the borrow dropped, all mutable borrow attempts will succeed drop(mmm_refmut); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); + assert!(mmm_bound.extract::>().is_ok()); }) } diff --git a/src/tests/hygiene/pymethods.rs b/src/tests/hygiene/pymethods.rs index a5856e6413e..9220acfffa4 100644 --- a/src/tests/hygiene/pymethods.rs +++ b/src/tests/hygiene/pymethods.rs @@ -123,7 +123,10 @@ impl Dummy { fn __delitem__(&self, key: u32) {} - fn __iter__(_: crate::pycell::PyRef<'_, Self>, py: crate::Python<'_>) -> crate::Py { + fn __iter__( + _: crate::pycell::PyRef<'_, '_, Self>, + py: crate::Python<'_>, + ) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() } @@ -132,7 +135,7 @@ impl Dummy { } fn __reversed__( - slf: crate::pycell::PyRef<'_, Self>, + slf: crate::pycell::PyRef<'_, '_, Self>, py: crate::Python<'_>, ) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() @@ -274,19 +277,27 @@ impl Dummy { fn __ior__(&mut self, other: &Self) {} - fn __neg__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __neg__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } - fn __pos__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __pos__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } - fn __abs__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __abs__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } - fn __invert__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __invert__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } @@ -344,7 +355,9 @@ impl Dummy { // Awaitable Objects ////////////////////// - fn __await__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __await__<'a, 'py>( + slf: crate::pycell::PyRef<'a, 'py, Self>, + ) -> crate::pycell::PyRef<'a, 'py, Self> { slf } @@ -354,7 +367,7 @@ impl Dummy { ////////////////////// fn __aiter__( - slf: crate::pycell::PyRef<'_, Self>, + slf: crate::pycell::PyRef<'_, '_, Self>, py: crate::Python<'_>, ) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() diff --git a/src/types/any.rs b/src/types/any.rs index 7c661290c12..8070fea8b5c 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -765,7 +765,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed { /// class_bound.borrow_mut().i += 1; /// /// // Alternatively you can get a `PyRefMut` directly - /// let class_ref: PyRefMut<'_, Class> = class.extract()?; + /// let class_ref: PyRefMut<'_, '_, Class> = class.extract()?; /// assert_eq!(class_ref.i, 1); /// Ok(()) /// }) diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index 21e32d4c13c..8e49a5af5b5 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -379,43 +379,43 @@ impl LhsAndRhs { // "BA" // } - fn __add__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __add__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} + {:?}", lhs, rhs) } - fn __sub__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __sub__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} - {:?}", lhs, rhs) } - fn __mul__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __mul__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} * {:?}", lhs, rhs) } - fn __lshift__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __lshift__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} << {:?}", lhs, rhs) } - fn __rshift__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __rshift__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} >> {:?}", lhs, rhs) } - fn __and__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __and__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} & {:?}", lhs, rhs) } - fn __xor__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __xor__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} ^ {:?}", lhs, rhs) } - fn __or__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __or__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} | {:?}", lhs, rhs) } - fn __pow__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>, _mod: Option) -> String { + fn __pow__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>, _mod: Option) -> String { format!("{:?} ** {:?}", lhs, rhs) } - fn __matmul__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __matmul__(lhs: PyRef<'_, '_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{:?} @ {:?}", lhs, rhs) } @@ -608,67 +608,110 @@ mod return_not_implemented { "RC_Self" } - fn __richcmp__(&self, other: PyRef<'_, Self>, _op: CompareOp) -> PyObject { + fn __richcmp__(&self, other: PyRef<'_, '_, Self>, _op: CompareOp) -> PyObject { other.py().None() } - fn __add__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __add__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __sub__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __sub__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __mul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __mul__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __matmul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __matmul__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __truediv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __truediv__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __floordiv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __floordiv__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __mod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __mod__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __pow__(slf: PyRef<'_, Self>, _other: u8, _modulo: Option) -> PyRef<'_, Self> { + fn __pow__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: u8, + _modulo: Option, + ) -> PyRef<'a, 'py, Self> { slf } - fn __lshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __lshift__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __rshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __rshift__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __divmod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __divmod__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __and__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __and__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __or__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __or__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } - fn __xor__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __xor__<'a, 'py>( + slf: PyRef<'a, 'py, Self>, + _other: PyRef<'a, 'py, Self>, + ) -> PyRef<'a, 'py, Self> { slf } // Inplace assignments - fn __iadd__(&mut self, _other: PyRef<'_, Self>) {} - fn __isub__(&mut self, _other: PyRef<'_, Self>) {} - fn __imul__(&mut self, _other: PyRef<'_, Self>) {} - fn __imatmul__(&mut self, _other: PyRef<'_, Self>) {} - fn __itruediv__(&mut self, _other: PyRef<'_, Self>) {} - fn __ifloordiv__(&mut self, _other: PyRef<'_, Self>) {} - fn __imod__(&mut self, _other: PyRef<'_, Self>) {} - fn __ilshift__(&mut self, _other: PyRef<'_, Self>) {} - fn __irshift__(&mut self, _other: PyRef<'_, Self>) {} - fn __iand__(&mut self, _other: PyRef<'_, Self>) {} - fn __ior__(&mut self, _other: PyRef<'_, Self>) {} - fn __ixor__(&mut self, _other: PyRef<'_, Self>) {} - fn __ipow__(&mut self, _other: PyRef<'_, Self>, _modulo: Option) {} + fn __iadd__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __isub__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __imul__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __imatmul__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __itruediv__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ifloordiv__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __imod__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ilshift__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __irshift__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __iand__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ior__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ixor__(&mut self, _other: PyRef<'_, '_, Self>) {} + fn __ipow__(&mut self, _other: PyRef<'_, '_, Self>, _modulo: Option) {} } fn _test_binary_dunder(dunder: &str) { diff --git a/tests/test_buffer.rs b/tests/test_buffer.rs index 8591b6a0e1f..38d96d9b6cc 100644 --- a/tests/test_buffer.rs +++ b/tests/test_buffer.rs @@ -29,7 +29,7 @@ struct TestBufferErrors { #[pymethods] impl TestBufferErrors { unsafe fn __getbuffer__( - slf: PyRefMut<'_, Self>, + slf: PyRefMut<'_, '_, Self>, view: *mut ffi::Py_buffer, flags: c_int, ) -> PyResult<()> { diff --git a/tests/test_buffer_protocol.rs b/tests/test_buffer_protocol.rs index f3c316d50c7..e19052e0f5a 100644 --- a/tests/test_buffer_protocol.rs +++ b/tests/test_buffer_protocol.rs @@ -29,7 +29,9 @@ impl TestBufferClass { view: *mut ffi::Py_buffer, flags: c_int, ) -> PyResult<()> { - unsafe { fill_view_from_readonly_data(view, flags, &slf.borrow().vec, slf.into_any()) } + unsafe { + fill_view_from_readonly_data(view, flags, &slf.clone().borrow().vec, slf.into_any()) + } } unsafe fn __releasebuffer__(&self, view: *mut ffi::Py_buffer) { diff --git a/tests/test_class_conversion.rs b/tests/test_class_conversion.rs index a1e2188e83a..29e8ebf8950 100644 --- a/tests/test_class_conversion.rs +++ b/tests/test_class_conversion.rs @@ -22,11 +22,11 @@ fn test_cloneable_pyclass() { let c2: Cloneable = py_c.extract(py).unwrap(); assert_eq!(c, c2); { - let rc: PyRef<'_, Cloneable> = py_c.extract(py).unwrap(); + let rc: PyRef<'_, '_, Cloneable> = py_c.extract(py).unwrap(); assert_eq!(&c, &*rc); // Drops PyRef before taking PyRefMut } - let mrc: PyRefMut<'_, Cloneable> = py_c.extract(py).unwrap(); + let mrc: PyRefMut<'_, '_, Cloneable> = py_c.extract(py).unwrap(); assert_eq!(&c, &*mrc); }); } @@ -126,16 +126,16 @@ fn test_pyref_as_base() { let cell = Bound::new(py, (SubClass {}, BaseClass { value: 120 })).unwrap(); // First try PyRefMut - let sub: PyRefMut<'_, SubClass> = cell.borrow_mut(); - let mut base: PyRefMut<'_, BaseClass> = sub.into_super(); + let sub: PyRefMut<'_, '_, SubClass> = cell.borrow_mut(); + let mut base: PyRefMut<'_, '_, BaseClass> = sub.into_super(); assert_eq!(120, base.value); base.value = 999; assert_eq!(999, base.value); drop(base); // Repeat for PyRef - let sub: PyRef<'_, SubClass> = cell.borrow(); - let base: PyRef<'_, BaseClass> = sub.into_super(); + let sub: PyRef<'_, '_, SubClass> = cell.borrow(); + let base: PyRef<'_, '_, BaseClass> = sub.into_super(); assert_eq!(999, base.value); }); } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 8bc1e53cadb..142e4a1dbf3 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -473,7 +473,7 @@ fn traverse_cannot_be_hijacked() { fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>; } - impl Traversable for PyRef<'_, HijackedTraverse> { + impl Traversable for PyRef<'_, '_, HijackedTraverse> { fn __traverse__(&self, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { self.hijacked.store(true, Ordering::Release); Ok(()) diff --git a/tests/test_getter_setter.rs b/tests/test_getter_setter.rs index 82a50442ec5..14a4894c4bd 100644 --- a/tests/test_getter_setter.rs +++ b/tests/test_getter_setter.rs @@ -138,12 +138,12 @@ struct RefGetterSetter { #[pymethods] impl RefGetterSetter { #[getter] - fn get_num(slf: PyRef<'_, Self>) -> i32 { + fn get_num(slf: PyRef<'_, '_, Self>) -> i32 { slf.num } #[setter] - fn set_num(mut slf: PyRefMut<'_, Self>, value: i32) { + fn set_num(mut slf: PyRefMut<'_, '_, Self>, value: i32) { slf.num = value; } } diff --git a/tests/test_methods.rs b/tests/test_methods.rs index bed81f02bc6..fe874fcbf0a 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -775,7 +775,7 @@ impl MethodWithPyClassArg { value: self.value + other.value, } } - fn add_pyref(&self, other: PyRef<'_, MethodWithPyClassArg>) -> MethodWithPyClassArg { + fn add_pyref(&self, other: PyRef<'_, '_, MethodWithPyClassArg>) -> MethodWithPyClassArg { MethodWithPyClassArg { value: self.value + other.value, } @@ -783,7 +783,7 @@ impl MethodWithPyClassArg { fn inplace_add(&self, other: &mut MethodWithPyClassArg) { other.value += self.value; } - fn inplace_add_pyref(&self, mut other: PyRefMut<'_, MethodWithPyClassArg>) { + fn inplace_add_pyref(&self, mut other: PyRefMut<'_, '_, MethodWithPyClassArg>) { other.value += self.value; } #[pyo3(signature=(other = None))] diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index 56039502f24..3355b56a3ba 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -367,11 +367,11 @@ struct Iterator { #[pymethods] impl Iterator { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __next__(slf: PyRefMut<'_, Self>) -> Option { + fn __next__(slf: PyRefMut<'_, '_, Self>) -> Option { slf.iter.lock().unwrap().next() } } @@ -648,11 +648,11 @@ impl OnceFuture { } } - fn __await__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } fn __next__<'py>(&mut self, py: Python<'py>) -> Option<&Bound<'py, PyAny>> { @@ -708,7 +708,7 @@ impl AsyncIterator { } } - fn __aiter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __aiter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } @@ -767,11 +767,11 @@ impl DescrCounter { DescrCounter { count: 0 } } /// Each access will increase the count - fn __get__<'a>( - mut slf: PyRefMut<'a, Self>, - _instance: &Bound<'_, PyAny>, - _owner: Option<&Bound<'_, PyType>>, - ) -> PyRefMut<'a, Self> { + fn __get__<'a, 'py>( + mut slf: PyRefMut<'a, 'py, Self>, + _instance: &Bound<'py, PyAny>, + _owner: Option<&Bound<'py, PyType>>, + ) -> PyRefMut<'a, 'py, Self> { slf.count += 1; slf } diff --git a/tests/test_pyself.rs b/tests/test_pyself.rs index c4f3e6d6fa6..5e310e1133f 100644 --- a/tests/test_pyself.rs +++ b/tests/test_pyself.rs @@ -35,7 +35,7 @@ impl Reader { } } fn get_iter_and_reset( - mut slf: PyRefMut<'_, Self>, + mut slf: PyRefMut<'_, '_, Self>, keys: Py, py: Python<'_>, ) -> PyResult { @@ -60,11 +60,11 @@ struct Iter { #[pymethods] impl Iter { #[allow(clippy::self_named_constructors)] - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> { slf } - fn __next__(mut slf: PyRefMut<'_, Self>) -> PyResult> { + fn __next__(mut slf: PyRefMut<'_, '_, Self>) -> PyResult> { let bytes = slf.keys.bind(slf.py()).as_bytes(); match bytes.get(slf.idx) { Some(&b) => { diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index e8f61e80e42..ed3e37e5084 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -74,7 +74,7 @@ impl ByteSequence { Self { elements } } - fn __inplace_concat__(mut slf: PyRefMut<'_, Self>, other: &Self) -> Py { + fn __inplace_concat__(mut slf: PyRefMut<'_, '_, Self>, other: &Self) -> Py { slf.elements.extend_from_slice(&other.elements); slf.into() } @@ -91,7 +91,7 @@ impl ByteSequence { } } - fn __inplace_repeat__(mut slf: PyRefMut<'_, Self>, count: isize) -> PyResult> { + fn __inplace_repeat__(mut slf: PyRefMut<'_, '_, Self>, count: isize) -> PyResult> { if count >= 0 { let mut elements = Vec::with_capacity(slf.elements.len() * count as usize); for _ in 0..count { diff --git a/tests/test_various.rs b/tests/test_various.rs index a8aa6b7a71f..1b3684bd4fa 100644 --- a/tests/test_various.rs +++ b/tests/test_various.rs @@ -19,7 +19,7 @@ impl MutRefArg { fn get(&self) -> i32 { self.n } - fn set_other(&self, mut other: PyRefMut<'_, MutRefArg>) { + fn set_other(&self, mut other: PyRefMut<'_, '_, MutRefArg>) { other.n = 100; } } diff --git a/tests/ui/invalid_cancel_handle.stderr b/tests/ui/invalid_cancel_handle.stderr index cccec3928f5..1a163de82fd 100644 --- a/tests/ui/invalid_cancel_handle.stderr +++ b/tests/ui/invalid_cancel_handle.stderr @@ -38,7 +38,7 @@ note: function defined here | ^^^^^^^^^^^^^^^^^^^^^^^^ -------------- = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_, false>` is not satisfied +error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_, '_, false>` is not satisfied --> tests/ui/invalid_cancel_handle.rs:20:50 | 20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} @@ -46,34 +46,34 @@ error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_, false>` | = help: the trait `PyClass` is implemented for `pyo3::coroutine::Coroutine` = note: required for `CancelHandle` to implement `FromPyObject<'_, '_>` - = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_, false>` + = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_, '_, false>` note: required by a bound in `extract_argument` --> src/impl_/extract_argument.rs | - | pub fn extract_argument<'a, 'py, T, const IS_OPTION: bool>( + | pub fn extract_argument<'a, 'holder, 'py, T, const IS_OPTION: bool>( | ---------------- required by a bound in this function ... - | T: PyFunctionArgument<'a, 'py, IS_OPTION>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` + | T: PyFunctionArgument<'a, 'holder, 'py, IS_OPTION>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` -error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_, false>` is not satisfied +error[E0277]: the trait bound `CancelHandle: PyFunctionArgument<'_, '_, '_, false>` is not satisfied --> tests/ui/invalid_cancel_handle.rs:20:50 | 20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} | ^^^^ the trait `Clone` is not implemented for `CancelHandle` | - = help: the following other types implement trait `PyFunctionArgument<'a, 'py, IS_OPTION>`: - `&'a mut pyo3::coroutine::Coroutine` implements `PyFunctionArgument<'a, 'py, false>` - `&'a pyo3::Bound<'py, T>` implements `PyFunctionArgument<'a, 'py, false>` - `&'a pyo3::coroutine::Coroutine` implements `PyFunctionArgument<'a, 'py, false>` - `Option` implements `PyFunctionArgument<'a, 'py, true>` + = help: the following other types implement trait `PyFunctionArgument<'a, 'holder, 'py, IS_OPTION>`: + `&'holder mut pyo3::coroutine::Coroutine` implements `PyFunctionArgument<'a, 'holder, 'py, false>` + `&'holder pyo3::Bound<'py, T>` implements `PyFunctionArgument<'a, 'holder, 'py, false>` + `&'holder pyo3::coroutine::Coroutine` implements `PyFunctionArgument<'a, 'holder, 'py, false>` + `Option` implements `PyFunctionArgument<'a, 'holder, 'py, true>` = note: required for `CancelHandle` to implement `FromPyObject<'_, '_>` - = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_, false>` + = note: required for `CancelHandle` to implement `PyFunctionArgument<'_, '_, '_, false>` note: required by a bound in `extract_argument` --> src/impl_/extract_argument.rs | - | pub fn extract_argument<'a, 'py, T, const IS_OPTION: bool>( + | pub fn extract_argument<'a, 'holder, 'py, T, const IS_OPTION: bool>( | ---------------- required by a bound in this function ... - | T: PyFunctionArgument<'a, 'py, IS_OPTION>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` + | T: PyFunctionArgument<'a, 'holder, 'py, IS_OPTION>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_argument` diff --git a/tests/ui/invalid_frozen_pyclass_borrow.stderr b/tests/ui/invalid_frozen_pyclass_borrow.stderr index 52a0623f282..b4230e7c245 100644 --- a/tests/ui/invalid_frozen_pyclass_borrow.stderr +++ b/tests/ui/invalid_frozen_pyclass_borrow.stderr @@ -13,8 +13,8 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `extract_pyclass_ref_mut` --> src/impl_/extract_argument.rs | - | pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( - | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` + | pub fn extract_pyclass_ref_mut<'a, 'holder, 'py: 'a, T: PyClass>( + | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` error[E0271]: type mismatch resolving `::Frozen == False` --> tests/ui/invalid_frozen_pyclass_borrow.rs:9:1 @@ -25,8 +25,8 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `PyRefMut` --> src/pycell.rs | - | pub struct PyRefMut<'p, T: PyClass> { - | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + | pub struct PyRefMut<'a, 'py, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving `::Frozen == False` @@ -38,7 +38,7 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `pyo3::Bound::<'py, T>::borrow_mut` --> src/instance.rs | - | pub fn borrow_mut(&self) -> PyRefMut<'py, T> + | pub fn borrow_mut<'a>(&'a self) -> PyRefMut<'a, 'py, T> | ---------- required by a bound in this associated function | where | T: PyClass, @@ -53,7 +53,7 @@ error[E0271]: type mismatch resolving `::Frozen == Fa note: required by a bound in `pyo3::Bound::<'py, T>::borrow_mut` --> src/instance.rs | - | pub fn borrow_mut(&self) -> PyRefMut<'py, T> + | pub fn borrow_mut<'a>(&'a self) -> PyRefMut<'a, 'py, T> | ---------- required by a bound in this associated function | where | T: PyClass, diff --git a/tests/ui/invalid_pymethod_enum.stderr b/tests/ui/invalid_pymethod_enum.stderr index bc377d2a055..72307c756f3 100644 --- a/tests/ui/invalid_pymethod_enum.stderr +++ b/tests/ui/invalid_pymethod_enum.stderr @@ -7,8 +7,8 @@ error[E0271]: type mismatch resolving `::Frozen == False note: required by a bound in `extract_pyclass_ref_mut` --> src/impl_/extract_argument.rs | - | pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( - | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` + | pub fn extract_pyclass_ref_mut<'a, 'holder, 'py: 'a, T: PyClass>( + | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` error[E0271]: type mismatch resolving `::Frozen == False` --> tests/ui/invalid_pymethod_enum.rs:9:1 @@ -19,8 +19,8 @@ error[E0271]: type mismatch resolving `::Frozen == False note: required by a bound in `PyRefMut` --> src/pycell.rs | - | pub struct PyRefMut<'p, T: PyClass> { - | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + | pub struct PyRefMut<'a, 'py, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving `::Frozen == False` @@ -32,8 +32,8 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `extract_pyclass_ref_mut` --> src/impl_/extract_argument.rs | - | pub fn extract_pyclass_ref_mut<'a, 'py: 'a, T: PyClass>( - | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` + | pub fn extract_pyclass_ref_mut<'a, 'holder, 'py: 'a, T: PyClass>( + | ^^^^^^^^^^^^^^ required by this bound in `extract_pyclass_ref_mut` error[E0271]: type mismatch resolving `::Frozen == False` --> tests/ui/invalid_pymethod_enum.rs:25:1 @@ -44,6 +44,6 @@ error[E0271]: type mismatch resolving `::Frozen == False` note: required by a bound in `PyRefMut` --> src/pycell.rs | - | pub struct PyRefMut<'p, T: PyClass> { - | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` + | pub struct PyRefMut<'a, 'py, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyRefMut` = note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/invalid_pymethods.stderr b/tests/ui/invalid_pymethods.stderr index eb8f429c937..6d2f580ff1b 100644 --- a/tests/ui/invalid_pymethods.stderr +++ b/tests/ui/invalid_pymethods.stderr @@ -166,7 +166,7 @@ error: `pass_module` cannot be used on Python methods | ^^^^^^^^^^^ error: Python objects are shared, so 'self' cannot be moved out of the Python interpreter. - Try `&self`, `&mut self, `slf: PyRef<'_, Self>` or `slf: PyRefMut<'_, Self>`. + Try `&self`, `&mut self, `slf: PyRef<'_, '_, Self>` or `slf: PyRefMut<'_, '_, Self>`. --> tests/ui/invalid_pymethods.rs:188:29 | 188 | fn method_self_by_value(self) {}