From 62c855b93cae65c578a46566c3a090b0e8f1bdc7 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 5 Jun 2026 13:30:08 +0200 Subject: [PATCH 1/2] deprecate `PyRef(Mut)` types --- guide/src/class.md | 29 ++++---- guide/src/class/numeric.md | 2 +- guide/src/class/protocols.md | 14 ++-- guide/src/conversions/tables.md | 8 +- guide/src/conversions/traits.md | 6 +- newsfragments/6120.changed.md | 1 + pytests/src/awaitable.rs | 30 ++++---- pytests/src/pyclasses.rs | 4 +- src/conversion.rs | 7 +- src/instance.rs | 24 ++++-- src/lib.rs | 1 + src/marker.rs | 2 + src/prelude.rs | 1 + src/pycell.rs | 30 ++++++++ src/pycell/impl_.rs | 52 +++++++------ src/tests/hygiene/pymethods.rs | 22 +++--- tests/test_arithmetics.rs | 126 ++++++++++++++++++++++---------- tests/test_buffer.rs | 7 +- tests/test_class_conversion.rs | 14 ++-- tests/test_gc.rs | 1 + tests/test_getter_setter.rs | 4 +- tests/test_methods.rs | 10 +-- tests/test_proto_methods.rs | 14 ++-- tests/test_pyself.rs | 9 +-- tests/test_sequence.rs | 15 +++- tests/test_various.rs | 2 +- tests/ui/invalid_async.rs | 2 +- tests/ui/invalid_async.stderr | 2 +- tests/ui/traverse.rs | 12 +-- tests/ui/traverse.stderr | 20 ++--- 30 files changed, 294 insertions(+), 177 deletions(-) create mode 100644 newsfragments/6120.changed.md diff --git a/guide/src/class.md b/guide/src/class.md index d0dcf262259..0e70a741a76 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -415,9 +415,8 @@ Currently, only classes defined in Rust and builtins provided by PyO3 can be inh To initialize a class, which inherits from another class, use the `PyClassInitializer` API. -To get a parent class from a child, use [`PyRef`] instead of `&self` for methods, or [`PyRefMut`] instead of `&mut self`. -Then you can access a parent class by `self_.as_super()` as `&PyRef`, or by `self_.into_super()` as `PyRef` (and similar for the `PyRefMut` case). -For convenience, `self_.as_ref()` can also be used to get `&Self::BaseClass` directly; however, this approach does not let you access base classes higher in the inheritance hierarchy, for which you would need to chain multiple `as_super` or `into_super` calls. +To get a parent class from a child, use [`PyClassGuard`] instead of `&self` for methods, or [`PyClassGuardMut`] instead of `&mut self`. +Then you can access a parent class by `self_.as_super()` as `&PyClassGuard`, or by `self_.into_super()` as `PyClassGuard` (and similar for the `PyRefMut` case). ```rust # use pyo3::prelude::*; @@ -452,8 +451,8 @@ impl SubClass { .add_subclass(SubClass { val2: 15 }) } - fn method2(self_: PyRef<'_, Self>) -> PyResult { - let super_ = self_.as_super(); // Get &PyRef + fn method2(self_: PyClassGuard<'_, Self>) -> PyResult { + let super_ = self_.as_super(); // Get &PyClassGuard super_.method1().map(|x| x * self_.val2) } } @@ -470,24 +469,24 @@ impl SubSubClass { PyClassInitializer::from(SubClass::new()).add_subclass(SubSubClass { val3: 20 }) } - fn method3(self_: PyRef<'_, Self>) -> PyResult { - let base = self_.as_super().as_super(); // Get &PyRef<'_, BaseClass> + fn method3(self_: PyClassGuard<'_, Self>) -> PyResult { + let base = self_.as_super().as_super(); // Get &PyClassGuard<'_, BaseClass> base.method1().map(|x| x * self_.val3) } - fn method4(self_: PyRef<'_, Self>) -> PyResult { + fn method4(self_: PyClassGuard<'_, Self>) -> PyResult { let v = self_.val3; - let super_ = self_.into_super(); // Get PyRef<'_, SubClass> + let super_ = self_.into_super(); // Get PyClassGuard<'_, SubClass> SubClass::method2(super_).map(|x| x * v) } - fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) { + fn get_values(self_: PyClassGuard<'_, 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_: PyClassGuardMut<'_, Self>) { self_.as_super().as_super().val1 *= 2; self_.as_super().val2 *= 2; self_.val3 *= 2; @@ -940,7 +939,7 @@ Class objects can be used as arguments to `#[pyfunction]`s and `#[pymethods]` in - `Py` or `Bound<'py, T>` smart pointers to the class Python object, - `&T` or `&mut T` references to the Rust data contained in the Python object, or -- `PyRef` and `PyRefMut` reference wrappers. +- `PyClassGuard` and `PyClassGuardMut` reference wrappers. Examples of each of these below: @@ -961,7 +960,7 @@ fn increment_field(my_class: &mut MyClass) { // Take a reference wrapper when borrowing should be automatic, // but access to the Python object is still needed #[pyfunction] -fn print_field_and_return_me(my_class: PyRef<'_, MyClass>) -> PyRef<'_, MyClass> { +fn print_field_and_return_me(my_class: PyClassGuard<'_, MyClass>) -> PyClassGuard<'_, MyClass> { println!("{}", my_class.my_field); my_class } @@ -1575,8 +1574,8 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass { [`Py`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Py.html [`Bound<'py, 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 +[`PyClassGuard`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/struct.PyClassGuard.html +[`PyClassGuardMut`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/struct.PyClassGuardMut.html [`PyClassInitializer`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass_init/struct.PyClassInitializer.html [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html diff --git a/guide/src/class/numeric.md b/guide/src/class/numeric.md index 8957838dcb9..13adebb72b2 100644 --- a/guide/src/class/numeric.md +++ b/guide/src/class/numeric.md @@ -135,7 +135,7 @@ impl Number { # #[pymethods] impl Number { - fn __pos__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __pos__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } diff --git a/guide/src/class/protocols.md b/guide/src/class/protocols.md index a0d73fb37c0..8db611f1017 100644 --- a/guide/src/class/protocols.md +++ b/guide/src/class/protocols.md @@ -33,7 +33,7 @@ The following sections list all magic methods for which PyO3 implements the nece 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 [in the parent section](../class.md#inheritance). + It can be `&self`, `&mut self` or a `Bound` reference like `self_: PyClassGuard<'_, Self>` and `self_: PyClassGuardMut<'_, Self>`, as described [in the parent section](../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 @@ -196,10 +196,10 @@ struct MyIterator { #[pymethods] impl MyIterator { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } - fn __next__(slf: PyRefMut<'_, Self>) -> Option> { + fn __next__(slf: PyClassGuardMut<'_, Self>) -> Option> { slf.iter.lock().unwrap().next() } } @@ -219,11 +219,11 @@ struct Iter { #[pymethods] impl Iter { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } - fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { + fn __next__(mut slf: PyClassGuardMut<'_, Self>) -> Option { slf.inner.next() } } @@ -235,11 +235,11 @@ struct Container { #[pymethods] impl Container { - fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { + fn __iter__(slf: PyClassGuard<'_, Self>, py: Python<'_>) -> PyResult> { let iter = Iter { inner: slf.iter.clone().into_iter(), }; - Py::new(slf.py(), iter) + Py::new(py, iter) } } diff --git a/guide/src/conversions/tables.md b/guide/src/conversions/tables.md index d62ae5ad00f..0382ec257ab 100644 --- a/guide/src/conversions/tables.md +++ b/guide/src/conversions/tables.md @@ -54,8 +54,8 @@ It is also worth remembering the following special types: | `Python<'py>` | A token used to prove attachment to the Python interpreter. | | `Bound<'py, T>` | A Python object with a lifetime which binds it to the attachment to the Python interpreter. This provides access to most of PyO3's APIs. | | `Py` | A Python object not connected to any lifetime of attachment to the Python interpreter. This can be sent to other threads. | -| `PyRef` | A `#[pyclass]` borrowed immutably. | -| `PyRefMut` | A `#[pyclass]` borrowed mutably. | +| `PyClassGuard` | A `#[pyclass]` borrowed immutably. | +| `PyClassGuardMut` | A `#[pyclass]` borrowed mutably. | For more detail on accepting `#[pyclass]` values as function arguments, see [the section of this guide on Python Classes](../class.md). @@ -105,8 +105,8 @@ Finally, the following Rust types are also able to convert to Python as return v | `BTreeSet` | `Set[T]` | | `Py` | `T` | | `Bound` | `T` | -| `PyRef` | `T` | -| `PyRefMut` | `T` | +| `PyClassGuard` | `T` | +| `PyClassGuardMut` | `T` | [^1]: Requires the `num-bigint` optional feature. diff --git a/guide/src/conversions/traits.md b/guide/src/conversions/traits.md index 83e958a7c9a..e0bee7720bf 100644 --- a/guide/src/conversions/traits.md +++ b/guide/src/conversions/traits.md @@ -23,7 +23,7 @@ let v: Vec = list.extract()?; This method is available for many Python object types, and can produce a wide variety of Rust types, which you can check out in the implementor list of [`FromPyObject`]. [`FromPyObject`] is also implemented for your own Rust types wrapped as Python objects (see [the chapter about classes](../class.md)). -There, in order to both be able to operate on mutable references *and* satisfy Rust's rules of non-aliasing mutable references, you have to extract the PyO3 reference wrappers [`PyRef`] and [`PyRefMut`]. +There, in order to both be able to operate on mutable references *and* satisfy Rust's rules of non-aliasing mutable references, you have to extract the PyO3 reference wrappers [`PyClassGuard`] and [`PyClassGuardMut`]. They work like the reference wrappers of `std::cell::RefCell` and ensure (at runtime) that Rust borrows are allowed. ### Deriving [`FromPyObject`] @@ -756,8 +756,8 @@ In the example above we used `BoundObject::into_any` and `BoundObject::unbind` t [`IntoPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.IntoPyObject.html [`IntoPyObjectExt`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.IntoPyObjectExt.html -[`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html -[`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRefMut.html +[`PyClassGuard`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/struct.PyClassGuard.html +[`PyClassGuardMut`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/struct.PyClassGuardMut.html [`BoundObject`]: {{#PYO3_DOCS_URL}}/pyo3/instance/trait.BoundObject.html [`Result`]: https://doc.rust-lang.org/stable/std/result/enum.Result.html diff --git a/newsfragments/6120.changed.md b/newsfragments/6120.changed.md new file mode 100644 index 00000000000..589581c8112 --- /dev/null +++ b/newsfragments/6120.changed.md @@ -0,0 +1 @@ +deprecate `PyRef` and `PyRefMut` in favor of `PyClassGuard` and `PyClassGuardMut` respectively diff --git a/pytests/src/awaitable.rs b/pytests/src/awaitable.rs index e13a569c3c6..e64e76ce79d 100644 --- a/pytests/src/awaitable.rs +++ b/pytests/src/awaitable.rs @@ -27,11 +27,11 @@ pub mod awaitable { } } - fn __await__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__(pyself: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { pyself } - fn __iter__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__(pyself: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { pyself } @@ -79,15 +79,15 @@ pub mod awaitable { } } - fn __await__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__(pyself: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { pyself } - fn __iter__(pyself: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__(pyself: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { pyself } - fn __next__(mut pyself: PyRefMut<'_, Self>) -> PyResult> { + fn __next__(mut pyself: PyClassGuardMut<'_, Self>) -> PyResult> { match pyself.result { Some(_) => match pyself.result.take().unwrap() { Ok(v) => Err(PyStopIteration::new_err(v)), @@ -97,20 +97,20 @@ pub mod awaitable { } } - fn send<'py>( - pyself: PyRefMut<'py, Self>, - _value: Bound<'py, PyAny>, - ) -> PyResult> { + fn send<'a>( + pyself: PyClassGuardMut<'a, Self>, + _value: Bound<'_, PyAny>, + ) -> PyResult> { Self::__next__(pyself) } #[pyo3(signature = (_value, _a = None, _b = None))] - fn throw<'py>( - pyself: PyRefMut<'py, Self>, - _value: Bound<'py, PyAny>, - _a: Option>, - _b: Option>, - ) -> PyResult> { + fn throw<'a>( + pyself: PyClassGuardMut<'a, Self>, + _value: Bound<'_, PyAny>, + _a: Option>, + _b: Option>, + ) -> PyResult> { Self::__next__(pyself) } diff --git a/pytests/src/pyclasses.rs b/pytests/src/pyclasses.rs index a1caa584d46..989baac1f8f 100644 --- a/pytests/src/pyclasses.rs +++ b/pytests/src/pyclasses.rs @@ -297,7 +297,7 @@ impl Number { Self(self.0 ^ other.0) } - fn __pos__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __pos__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } @@ -309,7 +309,7 @@ impl Number { } } - fn __abs__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __abs__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } diff --git a/src/conversion.rs b/src/conversion.rs index 200793ab81f..463975df40e 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -8,9 +8,10 @@ use crate::pyclass::{PyClassGuardError, PyClassGuardMutError}; use crate::types::PyList; use crate::types::PyTuple; use crate::{ - Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyClassGuard, PyErr, PyRef, PyRefMut, - PyTypeCheck, Python, + Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyClassGuard, PyErr, PyTypeCheck, Python, }; +#[expect(deprecated)] +use crate::{PyRef, PyRefMut}; use core::convert::Infallible; use core::marker::PhantomData; @@ -524,6 +525,7 @@ where } } +#[expect(deprecated)] impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRef<'py, T> where T: PyClass, @@ -541,6 +543,7 @@ where } } +#[expect(deprecated)] impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRefMut<'py, T> where T: PyClass, diff --git a/src/instance.rs b/src/instance.rs index 8aa8371d04f..beaa22f675b 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -12,10 +12,12 @@ use crate::pyclass::boolean_struct::{False, True}; use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods}; use crate::types::{DerefToPyAny, PyDict, PyString}; use crate::{ - ffi, CastError, CastIntoError, FromPyObject, PyAny, PyClass, PyClassInitializer, PyRef, - PyRefMut, PyTypeInfo, Python, + ffi, CastError, CastIntoError, FromPyObject, PyAny, PyClass, PyClassInitializer, PyTypeInfo, + Python, }; use crate::{internal::state, PyTypeCheck}; +#[expect(deprecated)] +use crate::{PyRef, PyRefMut}; use core::marker::PhantomData; use core::mem::ManuallyDrop; use core::ops::Deref; @@ -150,8 +152,8 @@ impl<'py, T> Bound<'py, T> { /// /// class_bound.borrow_mut().i += 1; /// - /// // Alternatively you can get a `PyRefMut` directly - /// let class_ref: PyRefMut<'_, Class> = class.extract()?; + /// // Alternatively you can get a `PyClassGuardMut` directly + /// let class_ref: PyClassGuardMut<'_, Class> = class.extract()?; /// assert_eq!(class_ref.i, 1); /// Ok(()) /// }) @@ -546,6 +548,7 @@ where /// /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use /// [`try_borrow`](#method.try_borrow). + #[expect(deprecated)] #[inline] #[track_caller] pub fn borrow(&self) -> PyRef<'py, T> { @@ -581,6 +584,7 @@ where /// # Panics /// Panics if the value is currently borrowed. For a non-panicking variant, use /// [`try_borrow_mut`](#method.try_borrow_mut). + #[expect(deprecated)] #[inline] #[track_caller] pub fn borrow_mut(&self) -> PyRefMut<'py, T> @@ -597,6 +601,7 @@ where /// This is the non-panicking variant of [`borrow`](#method.borrow). /// /// For frozen classes, the simpler [`get`][Self::get] is available. + #[expect(deprecated)] #[inline] pub fn try_borrow(&self) -> Result, PyBorrowError> { PyRef::try_borrow(self) @@ -607,6 +612,7 @@ where /// The borrow lasts while the returned [`PyRefMut`] exists. /// /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). + #[expect(deprecated)] #[inline] pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> where @@ -1613,6 +1619,7 @@ where /// /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use /// [`try_borrow`](#method.try_borrow). + #[expect(deprecated)] #[inline] #[track_caller] pub fn borrow<'py>(&'py self, py: Python<'py>) -> PyRef<'py, T> { @@ -1650,6 +1657,7 @@ where /// # Panics /// Panics if the value is currently borrowed. For a non-panicking variant, use /// [`try_borrow_mut`](#method.try_borrow_mut). + #[expect(deprecated)] #[inline] #[track_caller] pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T> @@ -1668,6 +1676,7 @@ where /// For frozen classes, the simpler [`get`][Self::get] is available. /// /// Equivalent to `self.bind(py).try_borrow()` - see [`Bound::try_borrow`]. + #[expect(deprecated)] #[inline] pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result, PyBorrowError> { self.bind(py).try_borrow() @@ -1680,6 +1689,7 @@ where /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). /// /// Equivalent to `self.bind(py).try_borrow_mut()` - see [`Bound::try_borrow_mut`]. + #[expect(deprecated)] #[inline] pub fn try_borrow_mut<'py>( &'py self, @@ -2215,6 +2225,7 @@ impl core::convert::From> for Py { } } +#[expect(deprecated)] impl<'py, T> core::convert::From> for Py where T: PyClass, @@ -2226,6 +2237,7 @@ where } } +#[expect(deprecated)] impl<'py, T> core::convert::From> for Py where T: PyClass, @@ -2394,8 +2406,8 @@ impl Py { /// /// class_bound.borrow_mut().i += 1; /// - /// // Alternatively you can get a `PyRefMut` directly - /// let class_ref: PyRefMut<'_, Class> = class.extract(py)?; + /// // Alternatively you can get a `PyClassGuardMut` directly + /// let class_ref: PyClassGuardMut<'_, Class> = class.extract(py)?; /// assert_eq!(class_ref.i, 1); /// Ok(()) /// }) diff --git a/src/lib.rs b/src/lib.rs index 8dd6148b675..23f37782c38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -350,6 +350,7 @@ pub use crate::instance::{Borrowed, Bound, BoundObject, Py}; #[cfg(not(any(PyPy, GraalPy)))] pub use crate::interpreter_lifecycle::with_embedded_python_interpreter; pub use crate::marker::Python; +#[expect(deprecated)] pub use crate::pycell::{PyRef, PyRefMut}; pub use crate::pyclass::{PyClass, PyClassGuard, PyClassGuardMut}; pub use crate::pyclass_init::PyClassInitializer; diff --git a/src/marker.rs b/src/marker.rs index 99d21bd1593..81788aacfb0 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -277,7 +277,9 @@ mod nightly { // This means that PyString, PyList, etc all inherit !Ungil from this. impl !Ungil for crate::PyAny {} + #[expect(deprecated)] impl !Ungil for crate::PyRef<'_, T> {} + #[expect(deprecated)] impl !Ungil for crate::PyRefMut<'_, T> {} // FFI pointees diff --git a/src/prelude.rs b/src/prelude.rs index 33a6f23534e..92e79150a22 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -12,6 +12,7 @@ pub use crate::conversion::{FromPyObject, FromPyObjectOwned, IntoPyObject}; pub use crate::err::{PyErr, PyResult}; pub use crate::instance::{Borrowed, Bound, Py}; pub use crate::marker::Python; +#[expect(deprecated)] pub use crate::pycell::{PyRef, PyRefMut}; pub use crate::pyclass_init::PyClassInitializer; pub use crate::types::{PyAny, PyModule}; diff --git a/src/pycell.rs b/src/pycell.rs index 5b3edbc9b37..a2370bfd50d 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -100,6 +100,7 @@ //! //! // We borrow the guard and then dereference //! // it to get a mutable reference to Number +//! # #[expect(deprecated)] //! let mut guard: PyRefMut<'_, Number> = n.bind(py).borrow_mut(); //! let n_mutable: &mut Number = &mut *guard; //! @@ -241,6 +242,7 @@ use impl_::{PyClassBorrowChecker, PyClassObjectBaseLayout, PyClassObjectLayout}; /// .add_subclass(Child { name: "Caterpillar" }) /// } /// +/// # #[expect(deprecated)] /// fn format(slf: PyRef<'_, Self>) -> String { /// // We can get *mut ffi::PyObject from PyRef /// let refcnt = unsafe { pyo3::ffi::Py_REFCNT(slf.as_ptr()) }; @@ -257,12 +259,14 @@ use impl_::{PyClassBorrowChecker, PyClassObjectBaseLayout, PyClassObjectLayout}; /// /// See the [module-level documentation](self) for more information. #[repr(transparent)] +#[deprecated(since = "0.30.0", note = "use `PyClassGuard` instead")] 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>, } +#[expect(deprecated)] impl<'p, T: PyClass> PyRef<'p, T> { /// Returns a `Python` token that is bound to the lifetime of the `PyRef`. pub fn py(&self) -> Python<'p> { @@ -270,6 +274,7 @@ impl<'p, T: PyClass> PyRef<'p, T> { } } +#[expect(deprecated)] impl AsRef for PyRef<'_, T> where T: PyClass, @@ -280,6 +285,7 @@ where } } +#[expect(deprecated)] impl<'py, T: PyClass> PyRef<'py, T> { /// Returns the raw FFI pointer represented by self. /// @@ -319,6 +325,7 @@ impl<'py, T: PyClass> PyRef<'py, T> { } } +#[expect(deprecated)] impl<'p, T> PyRef<'p, T> where T: PyClass, @@ -358,6 +365,7 @@ where /// .add_subclass(Base2 { name2: "base2" }) /// .add_subclass(Self { name3: "sub" }) /// } + /// # #[expect(deprecated)] /// fn name(slf: PyRef<'_, Self>) -> String { /// let subname = slf.name3; /// let super_ = slf.into_super(); @@ -441,6 +449,7 @@ where /// fn sub_name_len(&self) -> usize { /// self.sub_name.len() /// } + /// # #[expect(deprecated)] /// fn format_name_lengths(slf: PyRef<'_, Self>) -> String { /// format!("{} {}", slf.as_super().base_name_len(), slf.sub_name_len()) /// } @@ -461,6 +470,7 @@ where } } +#[expect(deprecated)] impl Deref for PyRef<'_, T> { type Target = T; @@ -470,6 +480,7 @@ impl Deref for PyRef<'_, T> { } } +#[expect(deprecated)] impl Drop for PyRef<'_, T> { fn drop(&mut self) { self.inner @@ -479,6 +490,7 @@ impl Drop for PyRef<'_, T> { } } +#[expect(deprecated)] impl<'py, T: PyClass> IntoPyObject<'py> for PyRef<'py, T> { type Target = T; type Output = Bound<'py, T>; @@ -492,6 +504,7 @@ impl<'py, T: PyClass> IntoPyObject<'py> for PyRef<'py, T> { } } +#[expect(deprecated)] impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRef<'py, T> { type Target = T; type Output = Borrowed<'a, 'py, T>; @@ -505,12 +518,14 @@ impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRef<'py, T> { } } +#[expect(deprecated)] impl fmt::Debug for PyRef<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } +#[expect(deprecated)] impl<'py, T: PyClass> TryFrom<&Bound<'py, T>> for PyRef<'py, T> { type Error = PyBorrowError; #[inline] @@ -522,6 +537,7 @@ impl<'py, T: PyClass> TryFrom<&Bound<'py, T>> for PyRef<'py, T> { /// A wrapper type for a mutably borrowed value from a [`Bound<'py, T>`]. /// /// See the [module-level documentation](self) for more information. +#[deprecated(since = "0.30.0", note = "use `PyClassGuardMut` instead")] #[repr(transparent)] pub struct PyRefMut<'p, T: PyClass> { // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to @@ -529,6 +545,7 @@ pub struct PyRefMut<'p, T: PyClass> { inner: Bound<'p, T>, } +#[expect(deprecated)] impl<'p, T: PyClass> PyRefMut<'p, T> { /// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`. pub fn py(&self) -> Python<'p> { @@ -536,6 +553,7 @@ impl<'p, T: PyClass> PyRefMut<'p, T> { } } +#[expect(deprecated)] impl AsRef for PyRefMut<'_, T> where T: PyClass, @@ -546,6 +564,7 @@ where } } +#[expect(deprecated)] impl AsMut for PyRefMut<'_, T> where T: PyClass, @@ -556,6 +575,7 @@ where } } +#[expect(deprecated)] impl<'py, T: PyClass> PyRefMut<'py, T> { /// Returns the raw FFI pointer represented by self. /// @@ -602,6 +622,7 @@ impl<'py, T: PyClass> PyRefMut<'py, T> { } } +#[expect(deprecated)] impl<'p, T> PyRefMut<'p, T> where T: PyClass, @@ -641,6 +662,7 @@ where } } +#[expect(deprecated)] impl> Deref for PyRefMut<'_, T> { type Target = T; @@ -650,6 +672,7 @@ impl> Deref for PyRefMut<'_, T> { } } +#[expect(deprecated)] impl> DerefMut for PyRefMut<'_, T> { #[inline] fn deref_mut(&mut self) -> &mut T { @@ -657,6 +680,7 @@ impl> DerefMut for PyRefMut<'_, T> { } } +#[expect(deprecated)] impl> Drop for PyRefMut<'_, T> { fn drop(&mut self) { self.inner @@ -666,6 +690,7 @@ impl> Drop for PyRefMut<'_, T> { } } +#[expect(deprecated)] impl<'py, T: PyClass> IntoPyObject<'py> for PyRefMut<'py, T> { type Target = T; type Output = Bound<'py, T>; @@ -679,6 +704,7 @@ impl<'py, T: PyClass> IntoPyObject<'py> for PyRefMut<'py, T> { } } +#[expect(deprecated)] impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRefMut<'py, T> { type Target = T; type Output = Borrowed<'a, 'py, T>; @@ -692,12 +718,14 @@ impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRefMut<'py } } +#[expect(deprecated)] impl + fmt::Debug> fmt::Debug for PyRefMut<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.deref(), f) } } +#[expect(deprecated)] impl<'py, T: PyClass> TryFrom<&Bound<'py, T>> for PyRefMut<'py, T> { type Error = PyBorrowMutError; #[inline] @@ -833,12 +861,14 @@ mod tests { crate::Py::new(py, init).expect("allocation error") } + #[expect(deprecated)] 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) } + #[expect(deprecated)] fn double_values(mut self_: PyRefMut<'_, Self>) { self_.as_super().as_super().val1 *= 2; self_.as_super().val2 *= 2; diff --git a/src/pycell/impl_.rs b/src/pycell/impl_.rs index 2475acd937f..6bd405b0271 100644 --- a/src/pycell/impl_.rs +++ b/src/pycell/impl_.rs @@ -736,37 +736,43 @@ 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::>() + .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::>().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::>() .is_ok()); + assert!(mmm_bound.extract::>().is_ok()); assert!(mmm_bound - .extract::>() + .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()); }) } @@ -787,32 +793,36 @@ 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::>() .is_ok()); assert!(mmm_bound - .extract::>() + .extract::>() .is_ok()); - assert!(mmm_bound.extract::>().is_ok()); }) } diff --git a/src/tests/hygiene/pymethods.rs b/src/tests/hygiene/pymethods.rs index ded2123d939..ff87f18a524 100644 --- a/src/tests/hygiene/pymethods.rs +++ b/src/tests/hygiene/pymethods.rs @@ -123,7 +123,7 @@ impl Dummy { fn __delitem__(&self, key: u32) {} - fn __iter__(_: crate::pycell::PyRef<'_, Self>, py: crate::Python<'_>) -> crate::Py { + fn __iter__(_: crate::PyClassGuard<'_, Self>, py: crate::Python<'_>) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() } @@ -132,7 +132,7 @@ impl Dummy { } fn __reversed__( - slf: crate::pycell::PyRef<'_, Self>, + slf: crate::PyClassGuard<'_, Self>, py: crate::Python<'_>, ) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() @@ -274,19 +274,19 @@ impl Dummy { fn __ior__(&mut self, other: &Self) {} - fn __neg__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __neg__(slf: crate::PyClassGuard<'_, Self>) -> crate::PyClassGuard<'_, Self> { slf } - fn __pos__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __pos__(slf: crate::PyClassGuard<'_, Self>) -> crate::PyClassGuard<'_, Self> { slf } - fn __abs__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __abs__(slf: crate::PyClassGuard<'_, Self>) -> crate::PyClassGuard<'_, Self> { slf } - fn __invert__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __invert__(slf: crate::PyClassGuard<'_, Self>) -> crate::PyClassGuard<'_, Self> { slf } @@ -344,7 +344,7 @@ impl Dummy { // Awaitable Objects ////////////////////// - fn __await__(slf: crate::pycell::PyRef<'_, Self>) -> crate::pycell::PyRef<'_, Self> { + fn __await__(slf: crate::PyClassGuard<'_, Self>) -> crate::PyClassGuard<'_, Self> { slf } @@ -354,7 +354,7 @@ impl Dummy { ////////////////////// fn __aiter__( - slf: crate::pycell::PyRef<'_, Self>, + slf: crate::PyClassGuard<'_, Self>, py: crate::Python<'_>, ) -> crate::Py { crate::Py::new(py, DummyIter {}).unwrap() @@ -484,10 +484,10 @@ impl WarningDummy { } #[pyo3(warn(message = "this method raises warning"))] - fn method_with_warning(_slf: crate::PyRef<'_, Self>) {} + fn method_with_warning(_slf: crate::PyClassGuard<'_, Self>) {} #[pyo3(warn(message = "this method raises warning", category = crate::exceptions::PyFutureWarning))] - fn method_with_warning_and_custom_category(_slf: crate::PyRef<'_, Self>) {} + fn method_with_warning_and_custom_category(_slf: crate::PyClassGuard<'_, Self>) {} #[cfg(any(not(Py_LIMITED_API), Py_3_12))] #[pyo3(warn(message = "this method raises user-defined warning", category = UserDefinedWarning))] @@ -525,7 +525,7 @@ impl WarningDummy { } #[pyo3(warn(message = "the + op method raises warning"))] - fn __add__(&self, other: crate::PyRef<'_, Self>) -> Self { + fn __add__(&self, other: crate::PyClassGuard<'_, Self>) -> Self { Self { value: self.value + other.value, } diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index 2cf21e59601..ee867e47e89 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -403,43 +403,43 @@ impl LhsAndRhs { // "BA" // } - fn __add__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __add__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} + {rhs:?}") } - fn __sub__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __sub__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} - {rhs:?}") } - fn __mul__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __mul__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} * {rhs:?}") } - fn __lshift__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __lshift__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} << {rhs:?}") } - fn __rshift__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __rshift__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} >> {rhs:?}") } - fn __and__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __and__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} & {rhs:?}") } - fn __xor__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __xor__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} ^ {rhs:?}") } - fn __or__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __or__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} | {rhs:?}") } - fn __pow__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>, _mod: Option) -> String { + fn __pow__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>, _mod: Option) -> String { format!("{lhs:?} ** {rhs:?}") } - fn __matmul__(lhs: PyRef<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { + fn __matmul__(lhs: PyClassGuard<'_, Self>, rhs: &Bound<'_, PyAny>) -> String { format!("{lhs:?} @ {rhs:?}") } @@ -640,67 +640,115 @@ mod return_not_implemented { "RC_Self" } - fn __richcmp__(&self, other: PyRef<'_, Self>, _op: CompareOp) -> Py { - other.py().None() + fn __richcmp__( + &self, + py: Python<'_>, + _other: PyClassGuard<'_, Self>, + _op: CompareOp, + ) -> Py { + py.None() } - fn __add__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __add__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __sub__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __sub__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __mul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __mul__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __matmul__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __matmul__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __truediv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __truediv__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __floordiv__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __floordiv__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __mod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __mod__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __pow__(slf: PyRef<'_, Self>, _other: u8, _modulo: Option) -> PyRef<'_, Self> { + fn __pow__( + slf: PyClassGuard<'_, Self>, + _other: u8, + _modulo: Option, + ) -> PyClassGuard<'_, Self> { slf } - fn __lshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __lshift__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __rshift__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __rshift__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __divmod__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __divmod__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __and__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __and__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __or__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __or__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, Self> { slf } - fn __xor__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> { + fn __xor__<'p>( + slf: PyClassGuard<'p, Self>, + _other: PyClassGuard<'p, Self>, + ) -> PyClassGuard<'p, 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: PyClassGuard<'_, Self>) {} + fn __isub__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __imul__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __imatmul__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __itruediv__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __ifloordiv__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __imod__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __ilshift__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __irshift__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __iand__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __ior__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __ixor__(&mut self, _other: PyClassGuard<'_, Self>) {} + fn __ipow__(&mut self, _other: PyClassGuard<'_, Self>, _modulo: Option) {} } fn _test_binary_dunder(dunder: &str) { diff --git a/tests/test_buffer.rs b/tests/test_buffer.rs index 8cbfd752d95..e438e6d438b 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: Bound<'_, Self>, view: *mut ffi::Py_buffer, flags: c_int, ) -> PyResult<()> { @@ -41,7 +41,8 @@ impl TestBufferErrors { return Err(PyBufferError::new_err("Object is not writable")); } - let bytes = &slf.buf; + let borrow = slf.borrow_mut(); + let bytes = &borrow.buf; unsafe { (*view).buf = bytes.as_ptr() as *mut c_void; @@ -60,7 +61,7 @@ impl TestBufferErrors { (*view).suboffsets = ptr::null_mut(); (*view).internal = ptr::null_mut(); - if let Some(err) = &slf.error { + if let Some(err) = &borrow.error { use TestGetBufferError::*; match err { NullShape => { diff --git a/tests/test_class_conversion.rs b/tests/test_class_conversion.rs index f7ad6ea86ef..ccc638b4661 100644 --- a/tests/test_class_conversion.rs +++ b/tests/test_class_conversion.rs @@ -21,11 +21,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: PyClassGuard<'_, Cloneable> = py_c.extract(py).unwrap(); assert_eq!(&c, &*rc); - // Drops PyRef before taking PyRefMut + // Drops PyClassGuard before taking PyClassGuardMut } - let mrc: PyRefMut<'_, Cloneable> = py_c.extract(py).unwrap(); + let mrc: PyClassGuardMut<'_, Cloneable> = py_c.extract(py).unwrap(); assert_eq!(&c, &*mrc); }); } @@ -127,16 +127,16 @@ fn test_pyref_as_base() { let cell = Bound::new(py, initializer).unwrap(); // First try PyRefMut - let sub: PyRefMut<'_, SubClass> = cell.borrow_mut(); - let mut base: PyRefMut<'_, BaseClass> = sub.into_super(); + let sub = cell.borrow_mut(); + let mut base = 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 = cell.borrow(); + let base = sub.into_super(); assert_eq!(999, base.value); }); } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 822168096a5..648783ca3b2 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -469,6 +469,7 @@ fn traverse_cannot_be_hijacked() { fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>; } + #[expect(deprecated)] impl Traversable for PyRef<'_, HijackedTraverse> { fn __traverse__(&self, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { self.hijacked.store(true, Ordering::Release); diff --git a/tests/test_getter_setter.rs b/tests/test_getter_setter.rs index 582cfd89c5f..9bd231f0969 100644 --- a/tests/test_getter_setter.rs +++ b/tests/test_getter_setter.rs @@ -137,12 +137,12 @@ struct RefGetterSetter { #[pymethods] impl RefGetterSetter { #[getter] - fn get_num(slf: PyRef<'_, Self>) -> i32 { + fn get_num(slf: PyClassGuard<'_, Self>) -> i32 { slf.num } #[setter] - fn set_num(mut slf: PyRefMut<'_, Self>, value: i32) { + fn set_num(mut slf: PyClassGuardMut<'_, Self>, value: i32) { slf.num = value; } } diff --git a/tests/test_methods.rs b/tests/test_methods.rs index c0fec231f36..e0c02fe0b9e 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -796,7 +796,7 @@ impl MethodWithPyClassArg { value: self.value + other.value, } } - fn add_pyref(&self, other: PyRef<'_, MethodWithPyClassArg>) -> MethodWithPyClassArg { + fn add_pyref(&self, other: PyClassGuard<'_, MethodWithPyClassArg>) -> MethodWithPyClassArg { MethodWithPyClassArg { value: self.value + other.value, } @@ -804,7 +804,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: PyClassGuardMut<'_, MethodWithPyClassArg>) { other.value += self.value; } #[pyo3(signature=(other = None))] @@ -1269,10 +1269,10 @@ fn test_pymethods_warn() { } #[pyo3(warn(message = "this method raises warning"))] - fn method_with_warning(_slf: PyRef<'_, Self>) {} + fn method_with_warning(_slf: PyClassGuard<'_, Self>) {} #[pyo3(warn(message = "this method raises warning", category = PyFutureWarning))] - fn method_with_warning_and_custom_category(_slf: PyRef<'_, Self>) {} + fn method_with_warning_and_custom_category(_slf: PyClassGuard<'_, Self>) {} #[cfg(any(not(Py_LIMITED_API), Py_3_12))] #[pyo3(warn(message = "this method raises user-defined warning", category = UserDefinedWarning))] @@ -1308,7 +1308,7 @@ fn test_pymethods_warn() { } #[pyo3(warn(message = "the + op method raises warning"))] - fn __add__(&self, other: PyRef<'_, Self>) -> Self { + fn __add__(&self, other: PyClassGuard<'_, Self>) -> Self { Self { value: self.value + other.value, } diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index c172d954a7a..83c4a210d63 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -424,11 +424,11 @@ struct Iterator { #[pymethods] impl Iterator { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } - fn __next__(slf: PyRefMut<'_, Self>) -> Option { + fn __next__(slf: PyClassGuardMut<'_, Self>) -> Option { slf.iter.lock().unwrap().next() } } @@ -748,11 +748,11 @@ impl OnceFuture { } } - fn __await__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __await__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } fn __next__<'py>(&mut self, py: Python<'py>) -> Option<&Bound<'py, PyAny>> { @@ -809,7 +809,7 @@ impl AsyncIterator { } } - fn __aiter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __aiter__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } @@ -872,10 +872,10 @@ impl DescrCounter { } /// Each access will increase the count fn __get__<'a>( - mut slf: PyRefMut<'a, Self>, + mut slf: PyClassGuardMut<'a, Self>, _instance: &Bound<'_, PyAny>, _owner: Option<&Bound<'_, PyType>>, - ) -> PyRefMut<'a, Self> { + ) -> PyClassGuardMut<'a, Self> { slf.count += 1; slf } diff --git a/tests/test_pyself.rs b/tests/test_pyself.rs index 7a34dedbc7e..5511683e3be 100644 --- a/tests/test_pyself.rs +++ b/tests/test_pyself.rs @@ -34,7 +34,7 @@ impl Reader { } } fn get_iter_and_reset( - mut slf: PyRefMut<'_, Self>, + mut slf: PyClassGuardMut<'_, Self>, keys: Py, py: Python<'_>, ) -> PyResult { @@ -59,16 +59,15 @@ struct Iter { #[pymethods] impl Iter { #[expect(clippy::self_named_constructors)] - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + fn __iter__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> { slf } - fn __next__(mut slf: PyRefMut<'_, Self>) -> PyResult>> { - let bytes = slf.keys.bind(slf.py()).as_bytes(); + fn __next__(mut slf: PyClassGuardMut<'_, Self>, py: Python<'_>) -> PyResult>> { + let bytes = slf.keys.bind(py).as_bytes(); match bytes.get(slf.idx) { Some(&b) => { slf.idx += 1; - let py = slf.py(); let reader = slf.reader.bind(py); let reader_ref = reader.try_borrow()?; let res = reader_ref diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index 55d20353577..73eb022bb79 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -75,9 +75,12 @@ impl ByteSequence { Self { elements } } - fn __inplace_concat__(mut slf: PyRefMut<'_, Self>, other: &Self) -> Py { + fn __inplace_concat__<'a>( + mut slf: PyClassGuardMut<'a, Self>, + other: &Self, + ) -> PyClassGuardMut<'a, Self> { slf.elements.extend_from_slice(&other.elements); - slf.into() + slf } fn __repeat__(&self, count: isize) -> PyResult { @@ -92,14 +95,18 @@ impl ByteSequence { } } - fn __inplace_repeat__(mut slf: PyRefMut<'_, Self>, count: isize) -> PyResult> { + fn __inplace_repeat__( + mut slf: PyClassGuardMut<'_, Self>, + py: Python<'_>, + count: isize, + ) -> PyResult> { if count >= 0 { let mut elements = Vec::with_capacity(slf.elements.len() * count as usize); for _ in 0..count { elements.extend(&slf.elements); } slf.elements = elements; - Ok(slf.into()) + Ok(slf.into_pyobject(py)?.to_owned().unbind()) } else { Err(PyValueError::new_err("invalid repeat count")) } diff --git a/tests/test_various.rs b/tests/test_various.rs index cf2ae82b5ff..9ace7c4cd2e 100644 --- a/tests/test_various.rs +++ b/tests/test_various.rs @@ -18,7 +18,7 @@ impl MutRefArg { fn get(&self) -> i32 { self.n } - fn set_other(&self, mut other: PyRefMut<'_, MutRefArg>) { + fn set_other(&self, mut other: PyClassGuardMut<'_, MutRefArg>) { other.n = 100; } } diff --git a/tests/ui/invalid_async.rs b/tests/ui/invalid_async.rs index 915f30e35f1..6b802c6a2a0 100644 --- a/tests/ui/invalid_async.rs +++ b/tests/ui/invalid_async.rs @@ -11,7 +11,7 @@ pub(crate) struct AsyncRange { } #[pymethods] impl AsyncRange { - async fn __anext__(mut _pyself: PyRefMut<'_, Self>) -> PyResult { + async fn __anext__(mut _pyself: PyClassGuardMut<'_, Self>) -> PyResult { //~^ ERROR: async functions are only supported with the `experimental-async` feature Ok(0) } diff --git a/tests/ui/invalid_async.stderr b/tests/ui/invalid_async.stderr index 35649d07955..470f710f639 100644 --- a/tests/ui/invalid_async.stderr +++ b/tests/ui/invalid_async.stderr @@ -7,7 +7,7 @@ error: async functions are only supported with the `experimental-async` feature error: async functions are only supported with the `experimental-async` feature --> tests/ui/invalid_async.rs:14:5 | -14 | async fn __anext__(mut _pyself: PyRefMut<'_, Self>) -> PyResult { +14 | async fn __anext__(mut _pyself: PyClassGuardMut<'_, Self>) -> PyResult { | ^^^^^ error: async functions are only supported with the `experimental-async` feature diff --git a/tests/ui/traverse.rs b/tests/ui/traverse.rs index d65d46e3e71..4037784787f 100644 --- a/tests/ui/traverse.rs +++ b/tests/ui/traverse.rs @@ -7,8 +7,9 @@ struct TraverseTriesToTakePyRef {} #[pymethods] impl TraverseTriesToTakePyRef { + #[expect(deprecated)] fn __traverse__(_slf: PyRef, _visit: PyVisit) -> Result<(), PyTraverseError> { -//~^ ERROR: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. + //~^ ERROR: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. Ok(()) } } @@ -18,8 +19,9 @@ struct TraverseTriesToTakePyRefMut {} #[pymethods] impl TraverseTriesToTakePyRefMut { + #[expect(deprecated)] fn __traverse__(_slf: PyRefMut, _visit: PyVisit) -> Result<(), PyTraverseError> { -//~^ ERROR: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. + //~^ ERROR: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. Ok(()) } } @@ -30,7 +32,7 @@ struct TraverseTriesToTakeBound {} #[pymethods] impl TraverseTriesToTakeBound { fn __traverse__(_slf: Bound<'_, Self>, _visit: PyVisit) -> Result<(), PyTraverseError> { -//~^ ERROR: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. + //~^ ERROR: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. Ok(()) } } @@ -41,7 +43,7 @@ struct TraverseTriesToTakeMutSelf {} #[pymethods] impl TraverseTriesToTakeMutSelf { fn __traverse__(&mut self, _visit: PyVisit) -> Result<(), PyTraverseError> { -//~^ ERROR: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. + //~^ ERROR: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. Ok(()) } } @@ -62,7 +64,7 @@ struct Class; #[pymethods] impl Class { fn __traverse__(&self, _py: Python<'_>, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { -//~^ ERROR: __traverse__ may not take `Python`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. + //~^ ERROR: __traverse__ may not take `Python`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. Ok(()) } diff --git a/tests/ui/traverse.stderr b/tests/ui/traverse.stderr index 701ce4bccb4..43f3f6c25b7 100644 --- a/tests/ui/traverse.stderr +++ b/tests/ui/traverse.stderr @@ -1,31 +1,31 @@ error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. - --> tests/ui/traverse.rs:10:27 + --> tests/ui/traverse.rs:11:27 | -10 | fn __traverse__(_slf: PyRef, _visit: PyVisit) -> Result<(), PyTraverseError> { +11 | fn __traverse__(_slf: PyRef, _visit: PyVisit) -> Result<(), PyTraverseError> { | ^^^^^ error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. - --> tests/ui/traverse.rs:21:27 + --> tests/ui/traverse.rs:23:27 | -21 | fn __traverse__(_slf: PyRefMut, _visit: PyVisit) -> Result<(), PyTraverseError> { +23 | fn __traverse__(_slf: PyRefMut, _visit: PyVisit) -> Result<(), PyTraverseError> { | ^^^^^^^^ error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. - --> tests/ui/traverse.rs:32:27 + --> tests/ui/traverse.rs:34:27 | -32 | fn __traverse__(_slf: Bound<'_, Self>, _visit: PyVisit) -> Result<(), PyTraverseError> { +34 | fn __traverse__(_slf: Bound<'_, Self>, _visit: PyVisit) -> Result<(), PyTraverseError> { | ^^^^^ error: __traverse__ may not take a receiver other than `&self`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. - --> tests/ui/traverse.rs:43:21 + --> tests/ui/traverse.rs:45:21 | -43 | fn __traverse__(&mut self, _visit: PyVisit) -> Result<(), PyTraverseError> { +45 | fn __traverse__(&mut self, _visit: PyVisit) -> Result<(), PyTraverseError> { | ^ error: __traverse__ may not take `Python`. Usually, an implementation of `__traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError>` should do nothing but calls to `visit.call`. Most importantly, safe access to the Python interpreter is prohibited inside implementations of `__traverse__`, i.e. `Python::attach` will panic. - --> tests/ui/traverse.rs:64:33 + --> tests/ui/traverse.rs:66:33 | -64 | fn __traverse__(&self, _py: Python<'_>, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { +66 | fn __traverse__(&self, _py: Python<'_>, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { | ^^^^^^^^^^ error: aborting due to 5 previous errors From f09723b3811cc73b1c43a07d03b487dbce442950 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Fri, 5 Jun 2026 13:30:08 +0200 Subject: [PATCH 2/2] deprecate `Bound|Py::(try_)borrow(_mut)` --- guide/src/class.md | 31 +++--- guide/src/class/numeric.md | 2 +- guide/src/class/object.md | 4 +- guide/src/conversions/traits.md | 2 +- guide/src/parallelism.md | 4 +- newsfragments/6121.added.md | 2 + newsfragments/6121.changed.md | 2 + src/conversion.rs | 2 +- src/conversions/serde.rs | 8 +- src/instance.rs | 92 +++++++++++++++--- src/pycell.rs | 57 ++++++----- src/pycell/impl_.rs | 24 +++-- src/pyclass/guard.rs | 8 +- src/sync.rs | 44 ++++++--- src/sync/critical_section.rs | 44 ++++++--- src/types/iterator.rs | 8 +- src/types/weakref/anyref.rs | 16 ++-- tests/test_buffer.rs | 16 ++-- tests/test_buffer_protocol.rs | 9 +- tests/test_class_basics.rs | 6 +- tests/test_class_conversion.rs | 4 +- tests/test_class_init.rs | 6 +- tests/test_class_new.rs | 6 +- tests/test_gc.rs | 54 +++++++---- tests/test_methods.rs | 4 +- tests/test_proto_methods.rs | 10 +- tests/test_pyself.rs | 4 +- tests/test_sequence.rs | 3 +- tests/test_serde.rs | 20 ++-- tests/test_various.rs | 2 +- tests/ui/invalid_frozen_pyclass_borrow.rs | 16 ++-- tests/ui/invalid_frozen_pyclass_borrow.stderr | 96 +++++++++++++------ 32 files changed, 391 insertions(+), 215 deletions(-) create mode 100644 newsfragments/6121.added.md create mode 100644 newsfragments/6121.changed.md diff --git a/guide/src/class.md b/guide/src/class.md index 0e70a741a76..efeeb1d514b 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -314,17 +314,17 @@ struct MyClass { Python::attach(|py| { let obj = Bound::new(py, MyClass { num: 3 }).unwrap(); { - let obj_ref = obj.borrow(); // Get PyRef + let obj_ref = obj.try_borrow_guard().unwrap(); // Get PyClassGuard assert_eq!(obj_ref.num, 3); - // You cannot get PyRefMut unless all PyRefs are dropped - assert!(obj.try_borrow_mut().is_err()); + // You cannot get PyClassGuardMut unless all PyClassGuards are dropped + assert!(obj.try_borrow_guard_mut().is_err()); } { - let mut obj_mut = obj.borrow_mut(); // Get PyRefMut + let mut obj_mut = obj.try_borrow_guard_mut().unwrap(); // Get PyClassGuardMut obj_mut.num = 5; // You cannot get any other refs until the PyRefMut is dropped - assert!(obj.try_borrow().is_err()); - assert!(obj.try_borrow_mut().is_err()); + assert!(obj.try_borrow_guard().is_err()); + assert!(obj.try_borrow_guard_mut().is_err()); } // You can convert `Bound` to a Python object @@ -334,7 +334,6 @@ Python::attach(|py| { A `Bound<'py, T>` is restricted to the Python lifetime `'py`. To make the object longer lived (for example, to store it in a struct on the Rust side), use `Py`. -`Py` needs a `Python<'_>` token to allow access: ```rust # use pyo3::prelude::*; @@ -349,11 +348,8 @@ fn return_myclass() -> Py { let obj = return_myclass(); -Python::attach(move |py| { - let bound = obj.bind(py); // Py::bind returns &Bound<'py, MyClass> - let obj_ref = bound.borrow(); // Get PyRef - assert_eq!(obj_ref.num, 1); -}); +let obj_ref = obj.try_borrow_guard().unwrap(); // Get PyClassGuard +assert_eq!(obj_ref.num, 1); ``` ### frozen classes: Opting out of interior mutability @@ -525,7 +521,7 @@ You can inherit native types such as `PyDict`, if they implement [`PySizedLayout This is not supported when building for the Python limited API (aka the `abi3` feature of PyO3). To convert between the Rust type and its native base class, you can take `slf` as a Python object. -To access the Rust fields use `slf.borrow()` or `slf.borrow_mut()`, and to access the base class use `slf.cast::()`. +To access the Rust fields use `slf.try_borrow_guard()` or `slf.try_borrow_guard_mut()`, and to access the base class use `slf.cast::()`. ```rust # #[cfg(any(not(Py_LIMITED_API), Py_3_12))] { @@ -547,7 +543,7 @@ impl DictWithCounter { } fn set(slf: &Bound<'_, Self>, key: String, value: Bound<'_, PyAny>) -> PyResult<()> { - slf.borrow_mut().counter.entry(key.clone()).or_insert(0); + slf.try_borrow_guard_mut()?.counter.entry(key.clone()).or_insert(0); let dict = slf.cast::()?; dict.set_item(key, value) } @@ -967,10 +963,11 @@ fn print_field_and_return_me(my_class: PyClassGuard<'_, MyClass>) -> PyClassGuar // Take (a reference to) a Python object smart pointer when borrowing needs to be managed manually. #[pyfunction] -fn increment_then_print_field(my_class: &Bound<'_, MyClass>) { - my_class.borrow_mut().my_field += 1; +fn increment_then_print_field(my_class: &Bound<'_, MyClass>) -> PyResult<()> { + my_class.try_borrow_guard_mut()?.my_field += 1; - println!("{}", my_class.borrow().my_field); + println!("{}", my_class.try_borrow_guard()?.my_field); + Ok(()) } // When the Python object smart pointer needs to be stored elsewhere prefer `Py` over `Bound<'py, T>` diff --git a/guide/src/class/numeric.md b/guide/src/class/numeric.md index 13adebb72b2..e47b0cbf4e9 100644 --- a/guide/src/class/numeric.md +++ b/guide/src/class/numeric.md @@ -234,7 +234,7 @@ impl Number { fn __repr__(slf: &Bound<'_, Self>) -> PyResult { // Get the class name dynamically in case `Number` is subclassed let class_name: Bound<'_, PyString> = slf.get_type().qualname()?; - Ok(format!("{}({})", class_name, slf.borrow().0)) + Ok(format!("{}({})", class_name, slf.try_borrow_guard()?.0)) } fn __str__(&self) -> String { diff --git a/guide/src/class/object.md b/guide/src/class/object.md index 22eb8105357..e159d2627da 100644 --- a/guide/src/class/object.md +++ b/guide/src/class/object.md @@ -134,7 +134,7 @@ impl Number { // This is the equivalent of `self.__class__.__name__` in Python. let class_name: Bound<'_, PyString> = slf.get_type().qualname()?; // To access fields of the Rust struct, we need to borrow from the Bound object. - Ok(format!("{}({})", class_name, slf.borrow().0)) + Ok(format!("{}({})", class_name, slf.try_borrow_guard()?.0)) } } ``` @@ -351,7 +351,7 @@ impl Number { fn __repr__(slf: &Bound<'_, Self>) -> PyResult { let class_name: Bound<'_, PyString> = slf.get_type().qualname()?; - Ok(format!("{}({})", class_name, slf.borrow().0)) + Ok(format!("{}({})", class_name, slf.try_borrow_guard()?.0)) } fn __str__(&self) -> String { diff --git a/guide/src/conversions/traits.md b/guide/src/conversions/traits.md index e0bee7720bf..f82fb82aecc 100644 --- a/guide/src/conversions/traits.md +++ b/guide/src/conversions/traits.md @@ -537,7 +537,7 @@ impl<'py> FromPyObject<'_, 'py> for Number { fn extract(obj: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result { if let Ok(obj) = obj.cast::() { // first try extraction via class object - Ok(obj.borrow().clone()) + Ok(obj.try_borrow_guard()?.clone()) } else { obj.extract::().map(Self) // otherwise try integer directly } diff --git a/guide/src/parallelism.md b/guide/src/parallelism.md index 06746a9d857..633b115354f 100644 --- a/guide/src/parallelism.md +++ b/guide/src/parallelism.md @@ -162,9 +162,7 @@ let allowed_ids: Vec = Python::attach(|outer_py| { let instances: Vec> = (0..10).map(|x| Py::new(outer_py, UserID { id: x }).unwrap()).collect(); outer_py.detach(|| { instances.par_iter().map(|instance| { - Python::attach(|inner_py| { - instance.borrow(inner_py).id > 5 - }) + instance.try_borrow_guard().unwrap().id > 5 }).collect() }) }); diff --git a/newsfragments/6121.added.md b/newsfragments/6121.added.md new file mode 100644 index 00000000000..48ec3d8d6ef --- /dev/null +++ b/newsfragments/6121.added.md @@ -0,0 +1,2 @@ +added `Bound::try_borrow_guard(_mut)` +added `Py::try_borrow_guard(_mut)` diff --git a/newsfragments/6121.changed.md b/newsfragments/6121.changed.md new file mode 100644 index 00000000000..f6f88118cd9 --- /dev/null +++ b/newsfragments/6121.changed.md @@ -0,0 +1,2 @@ +deprecated `Bound::(try_)borrow(_mut)` +deprecated `Py::(try_)borrow(_mut)` diff --git a/src/conversion.rs b/src/conversion.rs index 463975df40e..0c2a3fc4d4b 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -608,7 +608,7 @@ mod tests { fn extract(obj: crate::Borrowed<'_, 'py, crate::PyAny>) -> Result { if let Ok(obj) = obj.cast::() { - Ok(obj.borrow().clone()) + Ok(obj.try_borrow_guard()?.clone()) } else { obj.extract::().map(Self) } diff --git a/src/conversions/serde.rs b/src/conversions/serde.rs index bb3f4f1c58f..6d06ec86524 100644 --- a/src/conversions/serde.rs +++ b/src/conversions/serde.rs @@ -23,11 +23,9 @@ where where S: Serializer, { - Python::attach(|py| { - self.try_borrow(py) - .map_err(|e| ser::Error::custom(e.to_string()))? - .serialize(serializer) - }) + self.try_borrow_guard() + .map_err(|e| ser::Error::custom(e.to_string()))? + .serialize(serializer) } } diff --git a/src/instance.rs b/src/instance.rs index beaa22f675b..61a85200914 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -12,8 +12,8 @@ use crate::pyclass::boolean_struct::{False, True}; use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods}; use crate::types::{DerefToPyAny, PyDict, PyString}; use crate::{ - ffi, CastError, CastIntoError, FromPyObject, PyAny, PyClass, PyClassInitializer, PyTypeInfo, - Python, + ffi, CastError, CastIntoError, FromPyObject, PyAny, PyClass, PyClassGuard, PyClassGuardMut, + PyClassInitializer, PyTypeInfo, Python, }; use crate::{internal::state, PyTypeCheck}; #[expect(deprecated)] @@ -150,7 +150,7 @@ impl<'py, T> Bound<'py, T> { /// /// let class_bound: &Bound<'_, Class> = class.cast()?; /// - /// class_bound.borrow_mut().i += 1; + /// class_bound.try_borrow_guard_mut()?.i += 1; /// /// // Alternatively you can get a `PyClassGuardMut` directly /// let class_ref: PyClassGuardMut<'_, Class> = class.extract()?; @@ -535,7 +535,7 @@ where /// # fn main() -> PyResult<()> { /// Python::attach(|py| -> PyResult<()> { /// let foo: Bound<'_, Foo> = Bound::new(py, Foo { inner: 73 })?; - /// let inner: &u8 = &foo.borrow().inner; + /// let inner: &u8 = &foo.try_borrow_guard()?.inner; /// /// assert_eq!(*inner, 73); /// Ok(()) @@ -548,6 +548,7 @@ where /// /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use /// [`try_borrow`](#method.try_borrow). + #[deprecated(since = "0.30.0", note = "use `try_borrow_guard` instead")] #[expect(deprecated)] #[inline] #[track_caller] @@ -572,9 +573,9 @@ where /// # fn main() -> PyResult<()> { /// Python::attach(|py| -> PyResult<()> { /// let foo: Bound<'_, Foo> = Bound::new(py, Foo { inner: 73 })?; - /// foo.borrow_mut().inner = 35; + /// foo.try_borrow_guard_mut()?.inner = 35; /// - /// assert_eq!(foo.borrow().inner, 35); + /// assert_eq!(foo.try_borrow_guard()?.inner, 35); /// Ok(()) /// })?; /// # Ok(()) @@ -584,6 +585,7 @@ where /// # Panics /// Panics if the value is currently borrowed. For a non-panicking variant, use /// [`try_borrow_mut`](#method.try_borrow_mut). + #[deprecated(since = "0.30.0", note = "use `try_borrow_guard_mut` instead")] #[expect(deprecated)] #[inline] #[track_caller] @@ -602,17 +604,28 @@ where /// /// For frozen classes, the simpler [`get`][Self::get] is available. #[expect(deprecated)] + #[deprecated(since = "0.30.0", note = "use `try_borrow_guard` instead")] #[inline] pub fn try_borrow(&self) -> Result, PyBorrowError> { PyRef::try_borrow(self) } + /// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed. + /// + /// The borrow lasts while the returned [`PyClassGuard`] exists. + /// + /// For frozen classes, the simpler [`get`][Self::get] is available. + pub fn try_borrow_guard(&self) -> Result, PyBorrowError> { + PyClassGuard::try_borrow(self.as_unbound()) + } + /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed. /// /// The borrow lasts while the returned [`PyRefMut`] exists. /// /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). #[expect(deprecated)] + #[deprecated(since = "0.30.0", note = "use `try_borrow_guard_mut` instead")] #[inline] pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> where @@ -621,6 +634,16 @@ where PyRefMut::try_borrow(self) } + /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed. + /// + /// The borrow lasts while the returned [`PyClassGuardMut`] exists. + pub fn try_borrow_guard_mut(&self) -> Result, PyBorrowMutError> + where + T: PyClass, + { + PyClassGuardMut::try_borrow_mut(self.as_unbound()) + } + /// Provide an immutable borrow of the value `T`. /// /// This is available if the class is [`frozen`][macro@crate::pyclass] and [`Sync`]. @@ -1369,7 +1392,7 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { /// # m.add_class::()?; /// # /// # let foo: Bound<'_, Foo> = m.getattr("Foo")?.call0()?.cast_into()?; -/// # let dict = &foo.borrow().inner; +/// # let dict = &foo.try_borrow_guard()?.inner; /// # let dict: &Bound<'_, PyDict> = dict.bind(py); /// # /// # Ok(()) @@ -1406,8 +1429,8 @@ impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> { /// # m.add_class::()?; /// # /// # let foo: Bound<'_, Foo> = m.getattr("Foo")?.call0()?.cast_into()?; -/// # let bar = &foo.borrow().inner; -/// # let bar: &Bar = &*bar.borrow(py); +/// # let bar = &foo.try_borrow_guard()?.inner; +/// # let bar: &Bar = &*bar.try_borrow_guard()?; /// # /// # Ok(()) /// # }) @@ -1606,7 +1629,7 @@ where /// # fn main() -> PyResult<()> { /// Python::attach(|py| -> PyResult<()> { /// let foo: Py = Py::new(py, Foo { inner: 73 })?; - /// let inner: &u8 = &foo.borrow(py).inner; + /// let inner: &u8 = &foo.try_borrow_guard()?.inner; /// /// assert_eq!(*inner, 73); /// Ok(()) @@ -1619,6 +1642,7 @@ where /// /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use /// [`try_borrow`](#method.try_borrow). + #[deprecated(since = "0.30.0", note = "use `try_borrow_guard` instead")] #[expect(deprecated)] #[inline] #[track_caller] @@ -1645,9 +1669,9 @@ where /// # fn main() -> PyResult<()> { /// Python::attach(|py| -> PyResult<()> { /// let foo: Py = Py::new(py, Foo { inner: 73 })?; - /// foo.borrow_mut(py).inner = 35; + /// foo.try_borrow_guard_mut()?.inner = 35; /// - /// assert_eq!(foo.borrow(py).inner, 35); + /// assert_eq!(foo.try_borrow_guard()?.inner, 35); /// Ok(()) /// })?; /// # Ok(()) @@ -1658,6 +1682,7 @@ where /// Panics if the value is currently borrowed. For a non-panicking variant, use /// [`try_borrow_mut`](#method.try_borrow_mut). #[expect(deprecated)] + #[deprecated(since = "0.30.0", note = "use `try_borrow_guard_mut` instead")] #[inline] #[track_caller] pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T> @@ -1677,11 +1702,24 @@ where /// /// Equivalent to `self.bind(py).try_borrow()` - see [`Bound::try_borrow`]. #[expect(deprecated)] + #[deprecated(since = "0.30.0", note = "use `try_borrow_guard` instead")] #[inline] pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result, PyBorrowError> { self.bind(py).try_borrow() } + /// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed. + /// + /// The borrow lasts while the returned [`PyClassGuard`] exists. + /// + /// For frozen classes, the simpler [`get`][Self::get] is available. + /// + /// Equivalent to [`Bound::try_borrow`]. + #[inline] + pub fn try_borrow_guard<'a>(&'a self) -> Result, PyBorrowError> { + PyClassGuard::try_borrow(self) + } + /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed. /// /// The borrow lasts while the returned [`PyRefMut`] exists. @@ -1690,6 +1728,7 @@ where /// /// Equivalent to `self.bind(py).try_borrow_mut()` - see [`Bound::try_borrow_mut`]. #[expect(deprecated)] + #[deprecated(since = "0.30.0", note = "use `try_borrow_guard_mut` instead")] #[inline] pub fn try_borrow_mut<'py>( &'py self, @@ -1701,6 +1740,19 @@ where self.bind(py).try_borrow_mut() } + /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed. + /// + /// The borrow lasts while the returned [`PyClassGuardMut`] exists. + /// + /// Equivalent to [`Bound::try_borrow_mut`]. + #[inline] + pub fn try_borrow_guard_mut<'a>(&'a self) -> Result, PyBorrowMutError> + where + T: PyClass, + { + PyClassGuardMut::try_borrow_mut(self) + } + /// Provide an immutable borrow of the value `T`. /// /// This is available if the class is [`frozen`][macro@crate::pyclass] and [`Sync`], and @@ -2404,7 +2456,7 @@ impl Py { /// /// let class_bound = class.cast_bound::(py)?; /// - /// class_bound.borrow_mut().i += 1; + /// class_bound.try_borrow_guard_mut()?.i += 1; /// /// // Alternatively you can get a `PyClassGuardMut` directly /// let class_ref: PyClassGuardMut<'_, Class> = class.extract(py)?; @@ -2869,40 +2921,50 @@ a = A() struct SomeClass(i32); #[test] + #[expect(deprecated)] fn py_borrow_methods() { // More detailed tests of the underlying semantics in pycell.rs Python::attach(|py| { let instance = Py::new(py, SomeClass(0)).unwrap(); assert_eq!(instance.borrow(py).0, 0); assert_eq!(instance.try_borrow(py).unwrap().0, 0); + assert_eq!(instance.try_borrow_guard().unwrap().0, 0); assert_eq!(instance.borrow_mut(py).0, 0); assert_eq!(instance.try_borrow_mut(py).unwrap().0, 0); + assert_eq!(instance.try_borrow_guard_mut().unwrap().0, 0); - instance.borrow_mut(py).0 = 123; + instance.try_borrow_guard_mut().unwrap().0 = 123; assert_eq!(instance.borrow(py).0, 123); assert_eq!(instance.try_borrow(py).unwrap().0, 123); + assert_eq!(instance.try_borrow_guard().unwrap().0, 123); assert_eq!(instance.borrow_mut(py).0, 123); assert_eq!(instance.try_borrow_mut(py).unwrap().0, 123); + assert_eq!(instance.try_borrow_guard_mut().unwrap().0, 123); }) } #[test] + #[expect(deprecated)] fn bound_borrow_methods() { // More detailed tests of the underlying semantics in pycell.rs Python::attach(|py| { let instance = Bound::new(py, SomeClass(0)).unwrap(); assert_eq!(instance.borrow().0, 0); assert_eq!(instance.try_borrow().unwrap().0, 0); + assert_eq!(instance.try_borrow_guard().unwrap().0, 0); assert_eq!(instance.borrow_mut().0, 0); assert_eq!(instance.try_borrow_mut().unwrap().0, 0); + assert_eq!(instance.try_borrow_guard_mut().unwrap().0, 0); - instance.borrow_mut().0 = 123; + instance.try_borrow_guard_mut().unwrap().0 = 123; assert_eq!(instance.borrow().0, 123); assert_eq!(instance.try_borrow().unwrap().0, 123); + assert_eq!(instance.try_borrow_guard().unwrap().0, 123); assert_eq!(instance.borrow_mut().0, 123); assert_eq!(instance.try_borrow_mut().unwrap().0, 123); + assert_eq!(instance.try_borrow_guard_mut().unwrap().0, 123); }) } diff --git a/src/pycell.rs b/src/pycell.rs index a2370bfd50d..8d737a9c122 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -81,6 +81,7 @@ //! //! However, we *do* need `PyCell` if we want to call its methods from Rust: //! ```rust +//! # #![allow(deprecated)] //! # use pyo3::prelude::*; //! # //! # #[pyclass] @@ -800,6 +801,8 @@ impl From for PyErr { #[cfg(feature = "macros")] mod tests { + use crate::{PyClassGuard, PyClassGuardMut}; + use super::*; #[crate::pyclass(skip_from_py_object)] @@ -808,6 +811,7 @@ mod tests { struct SomeClass(i32); #[test] + #[expect(deprecated)] fn test_as_ptr() { Python::attach(|py| { let cell = Bound::new(py, SomeClass(0)).unwrap(); @@ -819,6 +823,7 @@ mod tests { } #[test] + #[expect(deprecated)] fn test_into_ptr() { Python::attach(|py| { let cell = Bound::new(py, SomeClass(0)).unwrap(); @@ -861,15 +866,13 @@ mod tests { crate::Py::new(py, init).expect("allocation error") } - #[expect(deprecated)] - fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) { + fn get_values(self_: PyClassGuard<'_, Self>) -> (usize, usize, usize) { let val1 = self_.as_super().as_super().val1; let val2 = self_.as_super().val2; (val1, val2, self_.val3) } - #[expect(deprecated)] - fn double_values(mut self_: PyRefMut<'_, Self>) { + fn double_values(mut self_: PyClassGuardMut<'_, Self>) { self_.as_super().as_super().val1 *= 2; self_.as_super().val2 *= 2; self_.val3 *= 2; @@ -880,12 +883,11 @@ mod tests { fn test_pyref_as_super() { Python::attach(|py| { let obj = SubSubClass::new(py).into_bound(py); - let pyref = obj.borrow(); - assert_eq!(pyref.as_super().as_super().val1, 10); - assert_eq!(pyref.as_super().val2, 15); - assert_eq!(pyref.as_ref().val2, 15); // `as_ref` also works - assert_eq!(pyref.val3, 20); - assert_eq!(SubSubClass::get_values(pyref), (10, 15, 20)); + let guard = obj.try_borrow_guard().unwrap(); + assert_eq!(guard.as_super().as_super().val1, 10); + assert_eq!(guard.as_super().val2, 15); + assert_eq!(guard.val3, 20); + assert_eq!(SubSubClass::get_values(guard), (10, 15, 20)); }); } @@ -893,18 +895,25 @@ mod tests { fn test_pyrefmut_as_super() { Python::attach(|py| { let obj = SubSubClass::new(py).into_bound(py); - assert_eq!(SubSubClass::get_values(obj.borrow()), (10, 15, 20)); + assert_eq!( + SubSubClass::get_values(obj.try_borrow_guard().unwrap()), + (10, 15, 20) + ); { - let mut pyrefmut = obj.borrow_mut(); - assert_eq!(pyrefmut.as_super().as_ref().val1, 10); - pyrefmut.as_super().as_super().val1 -= 5; - pyrefmut.as_super().val2 -= 3; - pyrefmut.as_mut().val2 -= 2; // `as_mut` also works - pyrefmut.val3 -= 5; + let mut guard = obj.try_borrow_guard_mut().unwrap(); + guard.as_super().as_super().val1 -= 5; + guard.as_super().val2 -= 5; + guard.val3 -= 5; } - assert_eq!(SubSubClass::get_values(obj.borrow()), (5, 10, 15)); - SubSubClass::double_values(obj.borrow_mut()); - assert_eq!(SubSubClass::get_values(obj.borrow()), (10, 20, 30)); + assert_eq!( + SubSubClass::get_values(obj.try_borrow_guard().unwrap()), + (5, 10, 15) + ); + SubSubClass::double_values(obj.try_borrow_guard_mut().unwrap()); + assert_eq!( + SubSubClass::get_values(obj.try_borrow_guard().unwrap()), + (10, 20, 30) + ); }); } @@ -940,8 +949,8 @@ mod tests { Python::attach(|py| { let obj = SubClass::new(py); - drop(obj.borrow().into_super()); - assert!(obj.try_borrow_mut().is_ok()); + drop(obj.try_borrow_guard().unwrap().into_super()); + assert!(obj.try_borrow_guard_mut().is_ok()); }) } @@ -973,10 +982,10 @@ mod tests { Python::attach(|py| { let obj = SubSubClass::new(py); - let _super_borrow = obj.borrow().into_super(); + let _super_borrow = obj.try_borrow_guard().unwrap().into_super(); // the whole object still has an immutable borrow, so we cannot // borrow any part mutably (the borrowflag is shared) - assert!(obj.try_borrow_mut().is_err()); + assert!(obj.try_borrow_guard_mut().is_err()); }) } } diff --git a/src/pycell/impl_.rs b/src/pycell/impl_.rs index 6bd405b0271..02f2c484892 100644 --- a/src/pycell/impl_.rs +++ b/src/pycell/impl_.rs @@ -732,7 +732,7 @@ mod tests { let mmm_bound: &Bound<'_, MutableChildOfMutableChildOfMutableBase> = mmm.bind(py); - let mmm_refmut = mmm_bound.borrow_mut(); + let mmm_refmut = mmm_bound.try_borrow_guard_mut().unwrap(); // Cannot take any other mutable or immutable borrows whilst the object is borrowed mutably assert!(mmm_bound @@ -789,7 +789,7 @@ mod tests { let mmm_bound: &Bound<'_, MutableChildOfMutableChildOfMutableBase> = mmm.bind(py); - let mmm_refmut = mmm_bound.borrow(); + let mmm_refmut = mmm_bound.try_borrow_guard().unwrap(); // Further immutable borrows are ok assert!(mmm_bound @@ -844,17 +844,15 @@ mod tests { let threads = (0..10) .map(|_| { s.spawn(|| { - Python::attach(|py| { - // Each thread records its own view of how many writes it made - let mut local_modifications = 0; - for _ in 0..100 { - if let Ok(mut i) = inst.try_borrow_mut(py) { - i.x += 1; - local_modifications += 1; - } + // Each thread records its own view of how many writes it made + let mut local_modifications = 0; + for _ in 0..100 { + if let Ok(mut i) = inst.try_borrow_guard_mut() { + i.x += 1; + local_modifications += 1; } - local_modifications - }) + } + local_modifications }) }) .collect::>(); @@ -866,7 +864,7 @@ mod tests { // If the implementation is free of data races, the total number of writes // should match the final value of `x`. - assert_eq!(total_modifications, inst.borrow(py).x); + assert_eq!(total_modifications, inst.try_borrow_guard().unwrap().x); }); } diff --git a/src/pyclass/guard.rs b/src/pyclass/guard.rs index 7566e56df52..d5b625d7796 100644 --- a/src/pyclass/guard.rs +++ b/src/pyclass/guard.rs @@ -1172,9 +1172,9 @@ mod tests { let obj = Bound::new(py, MyClass { data: [0; 100] })?; let data = PyClassGuard::try_borrow(obj.as_unbound())?.map(|c| &c.data); assert_eq!(data[0], 0); - assert!(obj.try_borrow_mut().is_err()); // obj is still protected + assert!(obj.try_borrow_guard_mut().is_err()); // obj is still protected drop(data); - assert!(obj.try_borrow_mut().is_ok()); // drop released shared borrow + assert!(obj.try_borrow_guard_mut().is_ok()); // drop released shared borrow Ok::<_, PyErr>(()) }) .unwrap() @@ -1189,9 +1189,9 @@ mod tests { assert_eq!(data[0], 0); data[0] = 5; assert_eq!(data[0], 5); - assert!(obj.try_borrow_mut().is_err()); // obj is still protected + assert!(obj.try_borrow_guard_mut().is_err()); // obj is still protected drop(data); - assert!(obj.try_borrow_mut().is_ok()); // drop released mutable borrow + assert!(obj.try_borrow_guard_mut().is_ok()); // drop released mutable borrow Ok::<_, PyErr>(()) }) .unwrap() diff --git a/src/sync.rs b/src/sync.rs index f3babca27e7..279a8805dc8 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -882,7 +882,10 @@ mod tests { barrier.wait(); // sleep to ensure the other thread actually blocks std::thread::sleep(core::time::Duration::from_millis(10)); - (*b).bind(py).borrow().0.store(true, Ordering::Release); + (*b).try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); drop(b); }); }); @@ -891,7 +894,7 @@ mod tests { Python::attach(|py| { // blocks until the other thread releases the lock let b = mutex.lock_py_attached(py).unwrap(); - assert!((*b).bind(py).borrow().0.load(Ordering::Acquire)); + assert!((*b).try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); }); @@ -917,7 +920,10 @@ mod tests { barrier.wait(); // sleep to ensure the other thread actually blocks std::thread::sleep(core::time::Duration::from_millis(10)); - (*b).bind(py).borrow().0.store(true, Ordering::Release); + (*b).try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); drop(b); }); }); @@ -926,7 +932,7 @@ mod tests { Python::attach(|py| { // blocks until the other thread releases the lock let b: $guard = mutex.lock_py_attached(py); - assert!((*b).bind(py).borrow().0.load(Ordering::Acquire)); + assert!((*b).try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); }); @@ -1002,7 +1008,10 @@ mod tests { barrier.wait(); // sleep to ensure the other thread actually blocks std::thread::sleep(core::time::Duration::from_millis(10)); - (*b).bind(py).borrow().0.store(true, Ordering::Release); + (*b).try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); drop(b); }); }); @@ -1011,7 +1020,7 @@ mod tests { Python::attach(|py| { // blocks until the other thread releases the lock let b = rwlock.read_py_attached(py).unwrap(); - assert!((*b).bind(py).borrow().0.load(Ordering::Acquire)); + assert!((*b).try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); }); @@ -1040,7 +1049,7 @@ mod tests { // The bool must still be false (i.e., the writer did not actually write the // value yet). - assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire)); + assert!(!(*b).try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); s.spawn(|| { @@ -1048,7 +1057,10 @@ mod tests { Python::attach(|py| { // blocks until the other thread releases the lock let b = rwlock.write_py_attached(py).unwrap(); - (*b).bind(py).borrow().0.store(true, Ordering::Release); + (*b).try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); drop(b); }); }); @@ -1057,7 +1069,7 @@ mod tests { // Confirm that the writer did in fact run and write the expected `true` value. Python::attach(|py| { let b = rwlock.read_py_attached(py).unwrap(); - assert!((*b).bind(py).borrow().0.load(Ordering::Acquire)); + assert!((*b).try_borrow_guard().unwrap().0.load(Ordering::Acquire)); drop(b); }); } @@ -1082,7 +1094,10 @@ mod tests { barrier.wait(); // sleep to ensure the other thread actually blocks std::thread::sleep(core::time::Duration::from_millis(10)); - (*b).bind(py).borrow().0.store(true, Ordering::Release); + (*b).try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); drop(b); }); }); @@ -1091,7 +1106,7 @@ mod tests { Python::attach(|py| { // blocks until the other thread releases the lock let b: $read_guard = rwlock.read_py_attached(py); - assert!((*b).bind(py).borrow().0.load(Ordering::Acquire)); + assert!((*b).try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); }); @@ -1143,7 +1158,8 @@ mod tests { // The bool must still be false (i.e., the writer did not actually write the // value yet). - assert!(!(*b).bind(py).borrow().0.load(Ordering::Acquire)); (*b).bind(py).borrow().0.store(true, Ordering::Release); + assert!(!(*b).try_borrow_guard().unwrap().0.load(Ordering::Acquire)); + (*b).try_borrow_guard().unwrap().0.store(true, Ordering::Release); drop(b); }); @@ -1153,7 +1169,7 @@ mod tests { Python::attach(|py| { // blocks until the other thread releases the lock let b: $write_guard = rwlock.write_py_attached(py); - (*b).bind(py).borrow().0.store(true, Ordering::Release); + (*b).try_borrow_guard().unwrap().0.store(true, Ordering::Release); }); }); }); @@ -1161,7 +1177,7 @@ mod tests { // Confirm that the writer did in fact run and write the expected `true` value. Python::attach(|py| { let b: $read_guard = rwlock.read_py_attached(py); - assert!((*b).bind(py).borrow().0.load(Ordering::Acquire)); + assert!((*b).try_borrow_guard().unwrap().0.load(Ordering::Acquire)); drop(b); }); }}; diff --git a/src/sync/critical_section.rs b/src/sync/critical_section.rs index f07c4af769d..20db0e43499 100644 --- a/src/sync/critical_section.rs +++ b/src/sync/critical_section.rs @@ -306,7 +306,10 @@ mod tests { with_critical_section(b, || { barrier.wait(); std::thread::sleep(core::time::Duration::from_millis(10)); - b.borrow().0.store(true, Ordering::Release); + b.try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); }) }); }); @@ -316,7 +319,7 @@ mod tests { let b = bool_wrapper.bind(py); // this blocks until the other thread's critical section finishes with_critical_section(b, || { - assert!(b.borrow().0.load(Ordering::Acquire)); + assert!(b.try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); }); @@ -374,8 +377,14 @@ mod tests { with_critical_section2(b1, b2, || { barrier.wait(); std::thread::sleep(core::time::Duration::from_millis(10)); - b1.borrow().0.store(true, Ordering::Release); - b2.borrow().0.store(true, Ordering::Release); + b1.try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); + b2.try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); }) }); }); @@ -385,7 +394,7 @@ mod tests { let b1 = bool_wrapper1.bind(py); // this blocks until the other thread's critical section finishes with_critical_section(b1, || { - assert!(b1.borrow().0.load(Ordering::Acquire)); + assert!(b1.try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); }); @@ -395,7 +404,7 @@ mod tests { let b2 = bool_wrapper2.bind(py); // this blocks until the other thread's critical section finishes with_critical_section(b2, || { - assert!(b2.borrow().0.load(Ordering::Acquire)); + assert!(b2.try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); }); @@ -452,7 +461,10 @@ mod tests { with_critical_section2(b, b, || { barrier.wait(); std::thread::sleep(core::time::Duration::from_millis(10)); - b.borrow().0.store(true, Ordering::Release); + b.try_borrow_guard() + .unwrap() + .0 + .store(true, Ordering::Release); }) }); }); @@ -462,7 +474,7 @@ mod tests { let b = bool_wrapper.bind(py); // this blocks until the other thread's critical section finishes with_critical_section(b, || { - assert!(b.borrow().0.load(Ordering::Acquire)); + assert!(b.try_borrow_guard().unwrap().0.load(Ordering::Acquire)); }); }); }); @@ -518,7 +530,10 @@ mod tests { let v2 = vec2.bind(py); with_critical_section2(v1, v2, || { // v2.extend(v1) - v2.borrow_mut().0.extend(v1.borrow().0.iter()); + v2.try_borrow_guard_mut() + .unwrap() + .0 + .extend(v1.try_borrow_guard().unwrap().0.iter()); }) }); }); @@ -528,7 +543,10 @@ mod tests { let v2 = vec2.bind(py); with_critical_section2(v1, v2, || { // v1.extend(v2) - v1.borrow_mut().0.extend(v2.borrow().0.iter()); + v1.try_borrow_guard_mut() + .unwrap() + .0 + .extend(v2.try_borrow_guard().unwrap().0.iter()); }) }); }); @@ -549,8 +567,10 @@ mod tests { let expected2_vec2 = vec![4, 5, 1, 2, 3]; assert!( - (v1.borrow().0.eq(&expected1_vec1) && v2.borrow().0.eq(&expected1_vec2)) - || (v1.borrow().0.eq(&expected2_vec1) && v2.borrow().0.eq(&expected2_vec2)) + (v1.try_borrow_guard().unwrap().0.eq(&expected1_vec1) + && v2.try_borrow_guard().unwrap().0.eq(&expected1_vec2)) + || (v1.try_borrow_guard().unwrap().0.eq(&expected2_vec1) + && v2.try_borrow_guard().unwrap().0.eq(&expected2_vec2)) ); }); } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 7408c43568a..8a42a2cc420 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -373,7 +373,13 @@ def fibonacci(target): ); assert_eq!( - downcaster.borrow_mut(py).failed.take().unwrap().to_string(), + downcaster + .try_borrow_guard_mut() + .unwrap() + .failed + .take() + .unwrap() + .to_string(), "TypeError: 'MySequence' object is not an instance of 'Iterator'" ); }); diff --git a/src/types/weakref/anyref.rs b/src/types/weakref/anyref.rs index 938f63830bb..18de3ecfeba 100644 --- a/src/types/weakref/anyref.rs +++ b/src/types/weakref/anyref.rs @@ -89,7 +89,7 @@ pub trait PyWeakrefMethods<'py>: crate::sealed::Sealed { /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { /// if let Some(data_src) = reference.upgrade_as::()? { - /// let data = data_src.borrow(); + /// let data = data_src.try_borrow_guard()?; /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) /// } else { @@ -167,13 +167,13 @@ pub trait PyWeakrefMethods<'py>: crate::sealed::Sealed { /// } /// } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> String { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { - /// let data = data_src.borrow(); + /// let data = data_src.try_borrow_guard()?; /// let (name, score) = data.get_data(); - /// format!("Processing '{}': score = {}", name, score) + /// Ok(format!("Processing '{}': score = {}", name, score)) /// } else { - /// "The supplied data reference is no longer relevant.".to_owned() + /// Ok("The supplied data reference is no longer relevant.".to_owned()) /// } /// } /// @@ -183,14 +183,14 @@ pub trait PyWeakrefMethods<'py>: crate::sealed::Sealed { /// let reference = PyWeakrefReference::new(&data)?; /// /// assert_eq!( - /// parse_data(reference.as_borrowed()), + /// parse_data(reference.as_borrowed())?, /// "Processing 'Dave': score = 10" /// ); /// /// drop(data); /// /// assert_eq!( - /// parse_data(reference.as_borrowed()), + /// parse_data(reference.as_borrowed())?, /// "The supplied data reference is no longer relevant." /// ); /// @@ -239,7 +239,7 @@ pub trait PyWeakrefMethods<'py>: crate::sealed::Sealed { /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { /// if let Some(data_src) = reference.upgrade_as_exact::()? { - /// let data = data_src.borrow(); + /// let data = data_src.try_borrow_guard()?; /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) /// } else { diff --git a/tests/test_buffer.rs b/tests/test_buffer.rs index e438e6d438b..2202d694f77 100644 --- a/tests/test_buffer.rs +++ b/tests/test_buffer.rs @@ -41,7 +41,7 @@ impl TestBufferErrors { return Err(PyBufferError::new_err("Object is not writable")); } - let borrow = slf.borrow_mut(); + let borrow = slf.try_borrow_guard_mut()?; let bytes = &borrow.buf; unsafe { @@ -80,7 +80,7 @@ impl TestBufferErrors { } } - (*view).obj = slf.into_ptr(); + (*view).obj = slf.clone().into_ptr(); } Ok(()) @@ -101,7 +101,7 @@ fn test_get_buffer_errors() { assert!(PyBuffer::::get(instance.bind(py)).is_ok()); - instance.borrow_mut(py).error = Some(TestGetBufferError::NullShape); + instance.try_borrow_guard_mut().unwrap().error = Some(TestGetBufferError::NullShape); assert_eq!( PyBuffer::::get(instance.bind(py)) .unwrap_err() @@ -109,7 +109,7 @@ fn test_get_buffer_errors() { "BufferError: shape is null" ); - instance.borrow_mut(py).error = Some(TestGetBufferError::NullStrides); + instance.try_borrow_guard_mut().unwrap().error = Some(TestGetBufferError::NullStrides); assert_eq!( PyBuffer::::get(instance.bind(py)) .unwrap_err() @@ -117,7 +117,8 @@ fn test_get_buffer_errors() { "BufferError: strides is null" ); - instance.borrow_mut(py).error = Some(TestGetBufferError::IncorrectItemSize); + instance.try_borrow_guard_mut().unwrap().error = + Some(TestGetBufferError::IncorrectItemSize); assert_eq!( PyBuffer::::get(instance.bind(py)) .unwrap_err() @@ -125,7 +126,7 @@ fn test_get_buffer_errors() { "BufferError: buffer contents are not compatible with u32" ); - instance.borrow_mut(py).error = Some(TestGetBufferError::IncorrectFormat); + instance.try_borrow_guard_mut().unwrap().error = Some(TestGetBufferError::IncorrectFormat); assert_eq!( PyBuffer::::get(instance.bind(py)) .unwrap_err() @@ -133,7 +134,8 @@ fn test_get_buffer_errors() { "BufferError: buffer contents are not compatible with u32" ); - instance.borrow_mut(py).error = Some(TestGetBufferError::IncorrectAlignment); + instance.try_borrow_guard_mut().unwrap().error = + Some(TestGetBufferError::IncorrectAlignment); assert_eq!( PyBuffer::::get(instance.bind(py)) .unwrap_err() diff --git a/tests/test_buffer_protocol.rs b/tests/test_buffer_protocol.rs index 857eb63a4ce..5ccdb1d3ebf 100644 --- a/tests/test_buffer_protocol.rs +++ b/tests/test_buffer_protocol.rs @@ -29,7 +29,14 @@ 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.try_borrow_guard().unwrap().vec, + slf.clone().into_any(), + ) + } } unsafe fn __releasebuffer__(&self, view: *mut ffi::Py_buffer) { diff --git a/tests/test_class_basics.rs b/tests/test_class_basics.rs index 90c9c1706fa..64953918071 100644 --- a/tests/test_class_basics.rs +++ b/tests/test_class_basics.rs @@ -307,9 +307,7 @@ fn test_unsendable() -> PyResult<()> { let caught_panic = std::thread::spawn(move || { // This access must panic - Python::attach(move |py| { - obj.borrow(py); - }); + obj.try_borrow_guard().unwrap(); }) .join(); @@ -423,7 +421,7 @@ fn test_tuple_struct_class() { "# ); - assert_eq!(instance.borrow(py).0, 1234); + assert_eq!(instance.try_borrow_guard().unwrap().0, 1234); }); } diff --git a/tests/test_class_conversion.rs b/tests/test_class_conversion.rs index ccc638b4661..0003895ef19 100644 --- a/tests/test_class_conversion.rs +++ b/tests/test_class_conversion.rs @@ -127,7 +127,7 @@ fn test_pyref_as_base() { let cell = Bound::new(py, initializer).unwrap(); // First try PyRefMut - let sub = cell.borrow_mut(); + let sub = cell.try_borrow_guard_mut().unwrap(); let mut base = sub.into_super(); assert_eq!(120, base.value); base.value = 999; @@ -135,7 +135,7 @@ fn test_pyref_as_base() { drop(base); // Repeat for PyRef - let sub = cell.borrow(); + let sub = cell.try_borrow_guard().unwrap(); let base = sub.into_super(); assert_eq!(999, base.value); }); diff --git a/tests/test_class_init.rs b/tests/test_class_init.rs index 0d06fb50ccf..4677db64a5e 100644 --- a/tests/test_class_init.rs +++ b/tests/test_class_init.rs @@ -25,7 +25,7 @@ fn test_base_init() { let typeobj = py.get_type::(); let obj = typeobj.call((), None).unwrap().cast_into::().unwrap(); // check __init__ was called - assert_eq!(obj.borrow().num, 42); + assert_eq!(obj.try_borrow_guard().unwrap().num, 42); }); } @@ -50,7 +50,7 @@ fn test_subclass_without_init_calls_base_init() { .cast_into::() .unwrap(); // check Base.__init__ was called - assert_eq!(obj.as_super().borrow().num, 42); + assert_eq!(obj.as_super().try_borrow_guard().unwrap().num, 42); }); } @@ -81,7 +81,7 @@ fn test_subclass_with_init() { .unwrap(); // check SubWithInit.__init__ was called, and Base.__init__ was only called once (through // SubWithInit.__init__) - assert_eq!(obj.as_super().borrow().num, 43); + assert_eq!(obj.as_super().try_borrow_guard().unwrap().num, 43); }); } diff --git a/tests/test_class_new.rs b/tests/test_class_new.rs index 945df02b6a6..cf73dd129d6 100644 --- a/tests/test_class_new.rs +++ b/tests/test_class_new.rs @@ -74,7 +74,7 @@ fn tuple_class_with_new() { let typeobj = py.get_type::(); let wrp = typeobj.call((42,), None).unwrap(); let obj = wrp.cast::().unwrap(); - let obj_ref = obj.borrow(); + let obj_ref = obj.try_borrow_guard().unwrap(); assert_eq!(obj_ref.0, 42); }); } @@ -99,7 +99,7 @@ fn new_with_one_arg() { let typeobj = py.get_type::(); let wrp = typeobj.call((42,), None).unwrap(); let obj = wrp.cast::().unwrap(); - let obj_ref = obj.borrow(); + let obj_ref = obj.try_borrow_guard().unwrap(); assert_eq!(obj_ref.data, 42); }); } @@ -130,7 +130,7 @@ fn new_with_two_args() { .map_err(|e| e.display(py)) .unwrap(); let obj = wrp.cast::().unwrap(); - let obj_ref = obj.borrow(); + let obj_ref = obj.try_borrow_guard().unwrap(); assert_eq!(obj_ref.data1, 10); assert_eq!(obj_ref.data2, 20); }); diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 648783ca3b2..a2a9ca8c0aa 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -49,8 +49,8 @@ fn spin_freelist(py: Python<'_>, data: usize) { for _ in 0..500 { let inst1 = Py::new(py, ClassWithFreelistAndData { data: Some(data) }).unwrap(); let inst2 = Py::new(py, ClassWithFreelistAndData { data: Some(data) }).unwrap(); - assert_eq!(inst1.borrow(py).data, Some(data)); - assert_eq!(inst2.borrow(py).data, Some(data)); + assert_eq!(inst1.try_borrow_guard().unwrap().data, Some(data)); + assert_eq!(inst2.try_borrow_guard().unwrap().data, Some(data)); } } @@ -161,7 +161,7 @@ impl CycleWithClear { } fn __clear__(slf: &Bound<'_, Self>) { - slf.borrow_mut().cycle = None; + slf.try_borrow_guard_mut().unwrap().cycle = None; } } @@ -179,7 +179,7 @@ fn test_cycle_clear() { ) .unwrap(); - inst.borrow_mut().cycle = Some(inst.clone().into_any().unbind()); + inst.try_borrow_guard_mut().unwrap().cycle = Some(inst.clone().into_any().unbind()); // gc.get_objects can create references to partially initialized objects, // leading to races on the free-threaded build. @@ -225,7 +225,7 @@ fn gc_null_traversal() { }, ) .unwrap(); - obj.borrow_mut(py).cycle = Some(obj.clone_ref(py)); + obj.try_borrow_guard_mut().unwrap().cycle = Some(obj.clone_ref(py)); // the object doesn't have to be cleaned up, it just needs to be traversed. py.run(c"import gc; gc.collect()", None, None).unwrap(); @@ -272,8 +272,8 @@ fn inheritance_with_new_methods_with_drop() { .cast_into::() .unwrap(); - inst.as_super().borrow_mut().guard = Some(guard_base); - inst.borrow_mut().guard = Some(guard_sub); + inst.as_super().try_borrow_guard_mut().unwrap().guard = Some(guard_base); + inst.try_borrow_guard_mut().unwrap().guard = Some(guard_sub); check_base.assert_not_dropped(); check_sub.assert_not_dropped(); @@ -317,14 +317,22 @@ fn gc_during_borrow() { // create an object and check that traversing it works normally // when it's not borrowed let cell = Bound::new(py, TraversableClass::new()).unwrap(); - assert!(!cell.borrow().traversed.load(Ordering::Relaxed)); + assert!(!cell + .try_borrow_guard() + .unwrap() + .traversed + .load(Ordering::Relaxed)); unsafe { traverse(cell.as_ptr(), novisit, std::ptr::null_mut()) }; - assert!(cell.borrow().traversed.load(Ordering::Relaxed)); + assert!(cell + .try_borrow_guard() + .unwrap() + .traversed + .load(Ordering::Relaxed)); // create an object and check that it is not traversed if the GC // is invoked while it is already borrowed mutably let cell2 = Bound::new(py, TraversableClass::new()).unwrap(); - let guard = cell2.borrow_mut(); + let guard = cell2.try_borrow_guard_mut().unwrap(); assert!(!guard.traversed.load(Ordering::Relaxed)); unsafe { traverse(cell2.as_ptr(), novisit, std::ptr::null_mut()) }; assert!(!guard.traversed.load(Ordering::Relaxed)); @@ -483,9 +491,15 @@ fn traverse_cannot_be_hijacked() { let traverse = unsafe { get_type_traverse(ty.as_type_ptr()).unwrap() }; let cell = Bound::new(py, HijackedTraverse::new()).unwrap(); - assert_eq!(cell.borrow().traversed_and_hijacked(), (false, false)); + assert_eq!( + cell.try_borrow_guard().unwrap().traversed_and_hijacked(), + (false, false) + ); unsafe { traverse(cell.as_ptr(), novisit, std::ptr::null_mut()) }; - assert_eq!(cell.borrow().traversed_and_hijacked(), (true, false)); + assert_eq!( + cell.try_borrow_guard().unwrap().traversed_and_hijacked(), + (true, false) + ); }) } @@ -521,7 +535,7 @@ fn drop_during_traversal_with_gil() { ) .unwrap(); - *inst.borrow_mut(py).cycle.lock().unwrap() = Some(inst.clone_ref(py)); + *inst.try_borrow_guard_mut().unwrap().cycle.lock().unwrap() = Some(inst.clone_ref(py)); check.assert_not_dropped(); let ptr = inst.as_ptr(); @@ -555,7 +569,7 @@ fn drop_during_traversal_without_gil() { ) .unwrap(); - *inst.borrow_mut(py).cycle.lock().unwrap() = Some(inst.clone_ref(py)); + *inst.try_borrow_guard_mut().unwrap().cycle.lock().unwrap() = Some(inst.clone_ref(py)); check.assert_not_dropped(); inst @@ -615,7 +629,7 @@ fn unsendable_are_not_traversed_on_foreign_thread() { .join() .unwrap(); - assert!(!obj.borrow().traversed.get()); + assert!(!obj.try_borrow_guard().unwrap().traversed.get()); // traversal on home thread still works assert_eq!( @@ -623,7 +637,7 @@ fn unsendable_are_not_traversed_on_foreign_thread() { 0 ); - assert!(obj.borrow().traversed.get()); + assert!(obj.try_borrow_guard().unwrap().traversed.get()); }); } @@ -653,7 +667,8 @@ fn test_traverse_subclass() { PyClassInitializer::from(base).add_subclass(SubOverrideTraverse {}), ) .unwrap(); - obj.borrow_mut().as_super().cycle = Some(obj.clone().into_any().unbind()); + obj.try_borrow_guard_mut().unwrap().as_super().cycle = + Some(obj.clone().into_any().unbind()); check.assert_not_dropped(); let ptr = obj.as_ptr(); @@ -700,7 +715,8 @@ fn test_traverse_subclass_override_clear() { PyClassInitializer::from(base).add_subclass(SubOverrideClear {}), ) .unwrap(); - obj.borrow_mut().as_super().cycle = Some(obj.clone().into_any().unbind()); + obj.try_borrow_guard_mut().unwrap().as_super().cycle = + Some(obj.clone().into_any().unbind()); check.assert_not_dropped(); let ptr = obj.as_ptr(); @@ -780,7 +796,7 @@ fn test_drop_buffer_during_traversal_without_gil() { ) .unwrap(); - obj.borrow_mut(py).cycle = Some(obj.clone_ref(py).into_any()); + obj.try_borrow_guard_mut().unwrap().cycle = Some(obj.clone_ref(py).into_any()); let ptr = obj.as_ptr(); drop(obj); diff --git a/tests/test_methods.rs b/tests/test_methods.rs index e0c02fe0b9e..9db430fcdfa 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -36,7 +36,7 @@ impl InstanceMethod { fn instance_method() { Python::attach(|py| { let obj = Bound::new(py, InstanceMethod { member: 42 }).unwrap(); - let obj_ref = obj.borrow(); + let obj_ref = obj.try_borrow_guard().unwrap(); assert_eq!(obj_ref.method(), 42); py_assert!(py, obj, "obj.method() == 42"); py_assert!(py, obj, "obj.add_other(obj) == 84"); @@ -76,7 +76,7 @@ impl InstanceMethodWithArgs { fn instance_method_with_args() { Python::attach(|py| { let obj = Bound::new(py, InstanceMethodWithArgs { member: 7 }).unwrap(); - let obj_ref = obj.borrow(); + let obj_ref = obj.try_borrow_guard().unwrap(); assert_eq!(obj_ref.method(6), 42); py_assert!(py, obj, "obj.method(3) == 21"); py_assert!(py, obj, "obj.method(multiplier=6) == 42"); diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index 83c4a210d63..7d34cab875f 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -206,7 +206,7 @@ fn test_bool() { Python::attach(|py| { let example_py = make_example(py); assert!(example_py.is_truthy().unwrap()); - example_py.borrow_mut().value = 0; + example_py.try_borrow_guard_mut().unwrap().value = 0; assert!(!example_py.is_truthy().unwrap()); // Ensure that passing a wrong self type from Python does not cause UB @@ -501,7 +501,7 @@ fn setitem() { let c = Bound::new(py, SetItem { key: 0, val: 0 }).unwrap(); py_run!(py, c, "c[1] = 2"); { - let c = c.borrow(); + let c = c.try_borrow_guard().unwrap(); assert_eq!(c.key, 1); assert_eq!(c.val, 2); } @@ -530,7 +530,7 @@ fn delitem() { let c = Bound::new(py, DelItem { key: 0 }).unwrap(); py_run!(py, c, "del c[1]"); { - let c = c.borrow(); + let c = c.try_borrow_guard().unwrap(); assert_eq!(c.key, 1); } py_expect_exception!(py, c, "c[1] = 2", PyNotImplementedError); @@ -562,11 +562,11 @@ fn setdelitem() { let c = Bound::new(py, SetDelItem { val: None }).unwrap(); py_run!(py, c, "c[1] = 2"); { - let c = c.borrow(); + let c = c.try_borrow_guard().unwrap(); assert_eq!(c.val, Some(2)); } py_run!(py, c, "del c[1]"); - let c = c.borrow(); + let c = c.try_borrow_guard().unwrap(); assert_eq!(c.val, None); }); } diff --git a/tests/test_pyself.rs b/tests/test_pyself.rs index 5511683e3be..9828fd08ff7 100644 --- a/tests/test_pyself.rs +++ b/tests/test_pyself.rs @@ -69,7 +69,7 @@ impl Iter { Some(&b) => { slf.idx += 1; let reader = slf.reader.bind(py); - let reader_ref = reader.try_borrow()?; + let reader_ref = reader.try_borrow_guard()?; let res = reader_ref .inner .get(&b) @@ -118,7 +118,7 @@ fn test_nested_iter_reset() { reader, "list(reader.get_iter_and_reset(bytes([3, 5, 2]))) == ['c', 'e', 'b']" ); - let reader_ref = reader.borrow(); + let reader_ref = reader.try_borrow_guard().unwrap(); assert!(reader_ref.inner.is_empty()); }); } diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index 73eb022bb79..b5ad2ffc2a9 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -291,7 +291,8 @@ fn test_any_object_list_set() { py_run!(py, list, "list.items = [1, 2, 3]"); assert!(list - .borrow() + .try_borrow_guard() + .unwrap() .items .iter() .zip(&[1u32, 2, 3]) diff --git a/tests/test_serde.rs b/tests/test_serde.rs index 1c8954abe68..bc5f4b0031b 100644 --- a/tests/test_serde.rs +++ b/tests/test_serde.rs @@ -69,11 +69,17 @@ fn test_deserialize() { assert_eq!(user.friends.len(), 1usize); let friend = user.friends.first().unwrap(); - Python::attach(|py| { - assert_eq!(friend.borrow(py).username, "friend"); - assert_eq!( - friend.borrow(py).group.as_ref().unwrap().borrow(py).name, - "danya's friends" - ) - }); + assert_eq!(friend.try_borrow_guard().unwrap().username, "friend"); + assert_eq!( + friend + .try_borrow_guard() + .unwrap() + .group + .as_ref() + .unwrap() + .try_borrow_guard() + .unwrap() + .name, + "danya's friends" + ); } diff --git a/tests/test_various.rs b/tests/test_various.rs index 9ace7c4cd2e..ad427ce7882 100644 --- a/tests/test_various.rs +++ b/tests/test_various.rs @@ -30,7 +30,7 @@ fn mut_ref_arg() { let inst2 = Py::new(py, MutRefArg { n: 0 }).unwrap(); py_run!(py, inst1 inst2, "inst1.set_other(inst2)"); - let inst2 = inst2.bind(py).borrow(); + let inst2 = inst2.try_borrow_guard().unwrap(); assert_eq!(inst2.n, 100); }); } diff --git a/tests/ui/invalid_frozen_pyclass_borrow.rs b/tests/ui/invalid_frozen_pyclass_borrow.rs index a1aed23857a..a5518829433 100644 --- a/tests/ui/invalid_frozen_pyclass_borrow.rs +++ b/tests/ui/invalid_frozen_pyclass_borrow.rs @@ -13,8 +13,9 @@ impl Foo { } fn borrow_mut_fails(foo: Py, py: Python) { - let borrow = foo.bind(py).borrow_mut(); -//~^ ERROR: type mismatch resolving `::Frozen == False` + let borrow = foo.try_borrow_guard_mut().unwrap(); + //~^ ERROR: type mismatch resolving `::Frozen == False` + //~| ERROR: type mismatch resolving `::Frozen == False` } #[pyclass(subclass)] @@ -24,24 +25,25 @@ struct MutableBase; struct ImmutableChild; fn borrow_mut_of_child_fails(child: Py, py: Python) { - let borrow = child.bind(py).borrow_mut(); -//~^ ERROR: type mismatch resolving `::Frozen == False` + let borrow = child.try_borrow_guard_mut().unwrap(); + //~^ ERROR: type mismatch resolving `::Frozen == False` + //~| ERROR: type mismatch resolving `::Frozen == False` } fn py_get_of_mutable_class_fails(class: Py) { class.get(); -//~^ ERROR: type mismatch resolving `::Frozen == True` + //~^ ERROR: type mismatch resolving `::Frozen == True` } fn pyclass_get_of_mutable_class_fails(class: &Bound<'_, MutableBase>) { class.get(); -//~^ ERROR: type mismatch resolving `::Frozen == True` + //~^ ERROR: type mismatch resolving `::Frozen == True` } #[pyclass(frozen)] pub struct SetOnFrozenClass { #[pyo3(set)] -//~^ ERROR: cannot use `#[pyo3(set)]` on a `frozen` class + //~^ ERROR: cannot use `#[pyo3(set)]` on a `frozen` class field: u32, } diff --git a/tests/ui/invalid_frozen_pyclass_borrow.stderr b/tests/ui/invalid_frozen_pyclass_borrow.stderr index d32b84680f2..4fafb0547f5 100644 --- a/tests/ui/invalid_frozen_pyclass_borrow.stderr +++ b/tests/ui/invalid_frozen_pyclass_borrow.stderr @@ -1,7 +1,7 @@ error: cannot use `#[pyo3(set)]` on a `frozen` class - --> tests/ui/invalid_frozen_pyclass_borrow.rs:43:12 + --> tests/ui/invalid_frozen_pyclass_borrow.rs:45:12 | -43 | #[pyo3(set)] +45 | #[pyo3(set)] | ^^^ error[E0271]: type mismatch resolving `::Frozen == False` @@ -23,57 +23,93 @@ note: required by a bound in `pyo3::impl_::extract_argument::extract_pyclass_ref = note: this error originates in the attribute macro `pymethods` which comes from the expansion of the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving `::Frozen == False` - --> tests/ui/invalid_frozen_pyclass_borrow.rs:16:31 + --> tests/ui/invalid_frozen_pyclass_borrow.rs:16:22 + | + 16 | let borrow = foo.try_borrow_guard_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `::Frozen == False` + | +note: expected this to be `pyo3::pyclass::boolean_struct::False` + --> tests/ui/invalid_frozen_pyclass_borrow.rs:3:1 + | + 3 | #[pyclass(frozen)] + | ^^^^^^^^^^^^^^^^^^ +note: required by a bound in `pyo3::Py::::try_borrow_guard_mut` + --> src/instance.rs + | + | pub fn try_borrow_guard_mut<'a>(&'a self) -> Result, PyBorrowMutError> + | -------------------- required by a bound in this associated function + | where + | T: PyClass, + | ^^^^^^^^^^^^^^ required by this bound in `Py::::try_borrow_guard_mut` + = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0271]: type mismatch resolving `::Frozen == False` + --> tests/ui/invalid_frozen_pyclass_borrow.rs:16:18 | - 16 | let borrow = foo.bind(py).borrow_mut(); - | ^^^^^^^^^^ type mismatch resolving `::Frozen == False` + 16 | let borrow = foo.try_borrow_guard_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `::Frozen == False` | note: expected this to be `pyo3::pyclass::boolean_struct::False` --> tests/ui/invalid_frozen_pyclass_borrow.rs:3:1 | 3 | #[pyclass(frozen)] | ^^^^^^^^^^^^^^^^^^ -note: required by a bound in `pyo3::Bound::<'py, T>::borrow_mut` +note: required by a bound in `PyClassGuardMut` + --> src/pyclass/guard.rs + | + | pub struct PyClassGuardMut<'a, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyClassGuardMut` + = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0271]: type mismatch resolving `::Frozen == False` + --> tests/ui/invalid_frozen_pyclass_borrow.rs:28:24 + | + 28 | let borrow = child.try_borrow_guard_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `::Frozen == False` + | +note: expected this to be `pyo3::pyclass::boolean_struct::False` + --> tests/ui/invalid_frozen_pyclass_borrow.rs:24:1 + | + 24 | #[pyclass(frozen, extends = MutableBase)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `pyo3::Py::::try_borrow_guard_mut` --> src/instance.rs | - | pub fn borrow_mut(&self) -> PyRefMut<'py, T> - | ---------- required by a bound in this associated function + | pub fn try_borrow_guard_mut<'a>(&'a self) -> Result, PyBorrowMutError> + | -------------------- required by a bound in this associated function | where | T: PyClass, - | ^^^^^^^^^^^^^^ required by this bound in `Bound::<'py, T>::borrow_mut` + | ^^^^^^^^^^^^^^ required by this bound in `Py::::try_borrow_guard_mut` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving `::Frozen == False` - --> tests/ui/invalid_frozen_pyclass_borrow.rs:27:33 + --> tests/ui/invalid_frozen_pyclass_borrow.rs:28:18 | - 27 | let borrow = child.bind(py).borrow_mut(); - | ^^^^^^^^^^ type mismatch resolving `::Frozen == False` + 28 | let borrow = child.try_borrow_guard_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `::Frozen == False` | note: expected this to be `pyo3::pyclass::boolean_struct::False` - --> tests/ui/invalid_frozen_pyclass_borrow.rs:23:1 + --> tests/ui/invalid_frozen_pyclass_borrow.rs:24:1 | - 23 | #[pyclass(frozen, extends = MutableBase)] + 24 | #[pyclass(frozen, extends = MutableBase)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `pyo3::Bound::<'py, T>::borrow_mut` - --> src/instance.rs +note: required by a bound in `PyClassGuardMut` + --> src/pyclass/guard.rs | - | pub fn borrow_mut(&self) -> PyRefMut<'py, T> - | ---------- required by a bound in this associated function - | where - | T: PyClass, - | ^^^^^^^^^^^^^^ required by this bound in `Bound::<'py, T>::borrow_mut` + | pub struct PyClassGuardMut<'a, T: PyClass> { + | ^^^^^^^^^^^^^^ required by this bound in `PyClassGuardMut` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving `::Frozen == True` - --> tests/ui/invalid_frozen_pyclass_borrow.rs:32:11 + --> tests/ui/invalid_frozen_pyclass_borrow.rs:34:11 | - 32 | class.get(); + 34 | class.get(); | ^^^ type mismatch resolving `::Frozen == True` | note: expected this to be `pyo3::pyclass::boolean_struct::True` - --> tests/ui/invalid_frozen_pyclass_borrow.rs:20:1 + --> tests/ui/invalid_frozen_pyclass_borrow.rs:21:1 | - 20 | #[pyclass(subclass)] + 21 | #[pyclass(subclass)] | ^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `pyo3::Py::::get` --> src/instance.rs @@ -86,15 +122,15 @@ note: required by a bound in `pyo3::Py::::get` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0271]: type mismatch resolving `::Frozen == True` - --> tests/ui/invalid_frozen_pyclass_borrow.rs:37:11 + --> tests/ui/invalid_frozen_pyclass_borrow.rs:39:11 | - 37 | class.get(); + 39 | class.get(); | ^^^ type mismatch resolving `::Frozen == True` | note: expected this to be `pyo3::pyclass::boolean_struct::True` - --> tests/ui/invalid_frozen_pyclass_borrow.rs:20:1 + --> tests/ui/invalid_frozen_pyclass_borrow.rs:21:1 | - 20 | #[pyclass(subclass)] + 21 | #[pyclass(subclass)] | ^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `pyo3::Bound::<'py, T>::get` --> src/instance.rs @@ -106,6 +142,6 @@ note: required by a bound in `pyo3::Bound::<'py, T>::get` | ^^^^^^^^^^^^^ required by this bound in `Bound::<'py, T>::get` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0271`.