Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self::BaseClass>`, or by `self_.into_super()` as `PyRef<Self::BaseClass>` (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<Self::BaseClass>`, or by `self_.into_super()` as `PyClassGuard<Self::BaseClass>` (and similar for the `PyRefMut` case).

```rust
# use pyo3::prelude::*;
Expand Down Expand Up @@ -452,8 +451,8 @@ impl SubClass {
.add_subclass(SubClass { val2: 15 })
}

fn method2(self_: PyRef<'_, Self>) -> PyResult<usize> {
let super_ = self_.as_super(); // Get &PyRef<BaseClass>
fn method2(self_: PyClassGuard<'_, Self>) -> PyResult<usize> {
let super_ = self_.as_super(); // Get &PyClassGuard<BaseClass>
super_.method1().map(|x| x * self_.val2)
}
}
Expand All @@ -470,24 +469,24 @@ impl SubSubClass {
PyClassInitializer::from(SubClass::new()).add_subclass(SubSubClass { val3: 20 })
}

fn method3(self_: PyRef<'_, Self>) -> PyResult<usize> {
let base = self_.as_super().as_super(); // Get &PyRef<'_, BaseClass>
fn method3(self_: PyClassGuard<'_, Self>) -> PyResult<usize> {
let base = self_.as_super().as_super(); // Get &PyClassGuard<'_, BaseClass>
base.method1().map(|x| x * self_.val3)
}

fn method4(self_: PyRef<'_, Self>) -> PyResult<usize> {
fn method4(self_: PyClassGuard<'_, Self>) -> PyResult<usize> {
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;
Expand Down Expand Up @@ -940,7 +939,7 @@ Class objects can be used as arguments to `#[pyfunction]`s and `#[pymethods]` in

- `Py<T>` 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<T>` and `PyRefMut<T>` reference wrappers.
- `PyClassGuard<T>` and `PyClassGuardMut<T>` reference wrappers.

Examples of each of these below:

Expand All @@ -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
}
Expand Down Expand Up @@ -1575,8 +1574,8 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
[`Py<T>`]: {{#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<T>`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass_init/struct.PyClassInitializer.html

[`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
Expand Down
2 changes: 1 addition & 1 deletion guide/src/class/numeric.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ impl Number {
#
#[pymethods]
impl Number {
fn __pos__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
fn __pos__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> {
slf
}

Expand Down
14 changes: 7 additions & 7 deletions guide/src/class/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<self>`.
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
Expand Down Expand Up @@ -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<Py<PyAny>> {
fn __next__(slf: PyClassGuardMut<'_, Self>) -> Option<Py<PyAny>> {
slf.iter.lock().unwrap().next()
}
}
Expand All @@ -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<usize> {
fn __next__(mut slf: PyClassGuardMut<'_, Self>) -> Option<usize> {
slf.inner.next()
}
}
Expand All @@ -235,11 +235,11 @@ struct Container {

#[pymethods]
impl Container {
fn __iter__(slf: PyRef<'_, Self>) -> PyResult<Py<Iter>> {
fn __iter__(slf: PyClassGuard<'_, Self>, py: Python<'_>) -> PyResult<Py<Iter>> {
let iter = Iter {
inner: slf.iter.clone().into_iter(),
};
Py::new(slf.py(), iter)
Py::new(py, iter)
}
}

Expand Down
8 changes: 4 additions & 4 deletions guide/src/conversions/tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` | A Python object not connected to any lifetime of attachment to the Python interpreter. This can be sent to other threads. |
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. |
| `PyClassGuard<T>` | A `#[pyclass]` borrowed immutably. |
| `PyClassGuardMut<T>` | 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).

Expand Down Expand Up @@ -105,8 +105,8 @@ Finally, the following Rust types are also able to convert to Python as return v
| `BTreeSet<T>` | `Set[T]` |
| `Py<T>` | `T` |
| `Bound<T>` | `T` |
| `PyRef<T: PyClass>` | `T` |
| `PyRefMut<T: PyClass>` | `T` |
| `PyClassGuard<T: PyClass>` | `T` |
| `PyClassGuardMut<T: PyClass>` | `T` |

[^1]: Requires the `num-bigint` optional feature.

Expand Down
6 changes: 3 additions & 3 deletions guide/src/conversions/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ let v: Vec<i32> = 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`]
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions newsfragments/6120.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
deprecate `PyRef` and `PyRefMut` in favor of `PyClassGuard` and `PyClassGuardMut` respectively
30 changes: 15 additions & 15 deletions pytests/src/awaitable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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<PyRefMut<'_, Self>> {
fn __next__(mut pyself: PyClassGuardMut<'_, Self>) -> PyResult<PyClassGuardMut<'_, Self>> {
match pyself.result {
Some(_) => match pyself.result.take().unwrap() {
Ok(v) => Err(PyStopIteration::new_err(v)),
Expand All @@ -97,20 +97,20 @@ pub mod awaitable {
}
}

fn send<'py>(
pyself: PyRefMut<'py, Self>,
_value: Bound<'py, PyAny>,
) -> PyResult<PyRefMut<'py, Self>> {
fn send<'a>(
pyself: PyClassGuardMut<'a, Self>,
_value: Bound<'_, PyAny>,
) -> PyResult<PyClassGuardMut<'a, Self>> {
Self::__next__(pyself)
}

#[pyo3(signature = (_value, _a = None, _b = None))]
fn throw<'py>(
pyself: PyRefMut<'py, Self>,
_value: Bound<'py, PyAny>,
_a: Option<Bound<'py, PyAny>>,
_b: Option<Bound<'py, PyAny>>,
) -> PyResult<PyRefMut<'py, Self>> {
fn throw<'a>(
pyself: PyClassGuardMut<'a, Self>,
_value: Bound<'_, PyAny>,
_a: Option<Bound<'_, PyAny>>,
_b: Option<Bound<'_, PyAny>>,
) -> PyResult<PyClassGuardMut<'a, Self>> {
Self::__next__(pyself)
}

Expand Down
4 changes: 2 additions & 2 deletions pytests/src/pyclasses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -309,7 +309,7 @@ impl Number {
}
}

fn __abs__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
fn __abs__(slf: PyClassGuard<'_, Self>) -> PyClassGuard<'_, Self> {
slf
}

Expand Down
7 changes: 5 additions & 2 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -524,6 +525,7 @@ where
}
}

#[expect(deprecated)]
impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRef<'py, T>
where
T: PyClass,
Expand All @@ -541,6 +543,7 @@ where
}
}

#[expect(deprecated)]
impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRefMut<'py, T>
where
T: PyClass<Frozen = False>,
Expand Down
Loading
Loading