pyo3/pyclass/
guard.rs

1use crate::conversion::FromPyObjectBound;
2use crate::impl_::pycell::{PyClassObject, PyClassObjectLayout as _};
3use crate::pycell::PyBorrowMutError;
4use crate::pycell::{impl_::PyClassBorrowChecker, PyBorrowError};
5use crate::pyclass::boolean_struct::False;
6use crate::{ffi, Borrowed, IntoPyObject, Py, PyClass};
7use std::convert::Infallible;
8use std::marker::PhantomData;
9use std::ops::{Deref, DerefMut};
10use std::ptr::NonNull;
11
12/// A wrapper type for an immutably borrowed value from a `PyClass`.
13///
14/// Rust has strict aliasing rules - you can either have any number of immutable
15/// (shared) references or one mutable reference. Python's ownership model is
16/// the complete opposite of that - any Python object can be referenced any
17/// number of times, and mutation is allowed from any reference.
18///
19/// PyO3 deals with these differences by employing the [Interior Mutability]
20/// pattern. This requires that PyO3 enforces the borrowing rules and it has two
21/// mechanisms for doing so:
22/// - Statically it can enforce thread-safe access with the
23///   [`Python<'py>`](crate::Python) token. All Rust code holding that token, or
24///   anything derived from it, can assume that they have safe access to the
25///   Python interpreter's state. For this reason all the native Python objects
26///   can be mutated through shared references.
27/// - However, methods and functions in Rust usually *do* need `&mut`
28///   references. While PyO3 can use the [`Python<'py>`](crate::Python) token to
29///   guarantee thread-safe access to them, it cannot statically guarantee
30///   uniqueness of `&mut` references. As such those references have to be
31///   tracked dynamically at runtime, using [`PyClassGuard`] and
32///   [`PyClassGuardMut`] defined in this module. This works similar to std's
33///   [`RefCell`](std::cell::RefCell) type. Especially when building for
34///   free-threaded Python it gets harder to track which thread borrows which
35///   object at any time. This can lead to method calls failing with
36///   [`PyBorrowError`]. In these cases consider using `frozen` classes together
37///   with Rust interior mutability primitives like [`Mutex`](std::sync::Mutex)
38///   instead of using [`PyClassGuardMut`] to get mutable access.
39///
40/// # Examples
41///
42/// You can use [`PyClassGuard`] as an alternative to a `&self` receiver when
43/// - you need to access the pointer of the `PyClass`, or
44/// - you want to get a super class.
45/// ```
46/// # use pyo3::prelude::*;
47/// # use pyo3::PyClassGuard;
48/// #[pyclass(subclass)]
49/// struct Parent {
50///     basename: &'static str,
51/// }
52///
53/// #[pyclass(extends=Parent)]
54/// struct Child {
55///     name: &'static str,
56///  }
57///
58/// #[pymethods]
59/// impl Child {
60///     #[new]
61///     fn new() -> (Self, Parent) {
62///         (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" })
63///     }
64///
65///     fn format(slf: PyClassGuard<'_, Self>) -> String {
66///         // We can get &Self::BaseType by as_super
67///         let basename = slf.as_super().basename;
68///         format!("{}(base: {})", slf.name, basename)
69///     }
70/// }
71/// # Python::attach(|py| {
72/// #     let sub = Py::new(py, Child::new()).unwrap();
73/// #     pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly)', sub.format()");
74/// # });
75/// ```
76///
77/// See also [`PyClassGuardMut`] and the [guide] for more information.
78///
79/// [Interior Mutability]:
80///     https://doc.rust-lang.org/book/ch15-05-interior-mutability.html
81///     "RefCell<T> and the Interior Mutability Pattern - The Rust Programming
82///     Language"
83/// [guide]: https://pyo3.rs/latest/class.html#bound-and-interior-mutability
84///     "Bound and interior mutability"
85#[repr(transparent)]
86pub struct PyClassGuard<'a, T: PyClass> {
87    ptr: NonNull<ffi::PyObject>,
88    marker: PhantomData<&'a Py<T>>,
89}
90
91impl<'a, T: PyClass> PyClassGuard<'a, T> {
92    pub(crate) fn try_borrow(obj: &'a Py<T>) -> Result<Self, PyBorrowError> {
93        Self::try_from_class_object(obj.get_class_object())
94    }
95
96    fn try_from_class_object(obj: &'a PyClassObject<T>) -> Result<Self, PyBorrowError> {
97        obj.ensure_threadsafe();
98        obj.borrow_checker().try_borrow().map(|_| Self {
99            ptr: NonNull::from(obj).cast(),
100            marker: PhantomData,
101        })
102    }
103
104    pub(crate) fn as_class_object(&self) -> &'a PyClassObject<T> {
105        // SAFETY: `ptr` by construction points to a `PyClassObject<T>` and is
106        // valid for at least 'a
107        unsafe { self.ptr.cast().as_ref() }
108    }
109
110    /// Consumes the [`PyClassGuard`] and returns a [`PyClassGuardMap`] for a component of the
111    /// borrowed data
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// # use pyo3::prelude::*;
117    /// # use pyo3::PyClassGuard;
118    ///
119    /// #[pyclass]
120    /// pub struct MyClass {
121    ///     msg: String,
122    /// }
123    ///
124    /// # Python::attach(|py| {
125    /// let obj = Bound::new(py, MyClass { msg: String::from("hello") })?;
126    /// let msg = obj.extract::<PyClassGuard<'_, MyClass>>()?.map(|c| &c.msg);
127    /// assert_eq!(&*msg, "hello");
128    /// # Ok::<_, PyErr>(())
129    /// # }).unwrap();
130    /// ```
131    pub fn map<F, U: ?Sized>(self, f: F) -> PyClassGuardMap<'a, U, false>
132    where
133        F: FnOnce(&T) -> &U,
134    {
135        let slf = std::mem::ManuallyDrop::new(self); // the borrow is released when dropping the `PyClassGuardMap`
136        PyClassGuardMap {
137            ptr: NonNull::from(f(&slf)),
138            checker: slf.as_class_object().borrow_checker(),
139        }
140    }
141}
142
143impl<'a, T, U> PyClassGuard<'a, T>
144where
145    T: PyClass<BaseType = U>,
146    U: PyClass,
147{
148    /// Borrows a shared reference to `PyClassGuard<T::BaseType>`.
149    ///
150    /// With the help of this method, you can access attributes and call methods
151    /// on the superclass without consuming the `PyClassGuard<T>`. This method
152    /// can also be chained to access the super-superclass (and so on).
153    ///
154    /// # Examples
155    /// ```
156    /// # use pyo3::prelude::*;
157    /// # use pyo3::PyClassGuard;
158    /// #[pyclass(subclass)]
159    /// struct Base {
160    ///     base_name: &'static str,
161    /// }
162    /// #[pymethods]
163    /// impl Base {
164    ///     fn base_name_len(&self) -> usize {
165    ///         self.base_name.len()
166    ///     }
167    /// }
168    ///
169    /// #[pyclass(extends=Base)]
170    /// struct Sub {
171    ///     sub_name: &'static str,
172    /// }
173    ///
174    /// #[pymethods]
175    /// impl Sub {
176    ///     #[new]
177    ///     fn new() -> (Self, Base) {
178    ///         (Self { sub_name: "sub_name" }, Base { base_name: "base_name" })
179    ///     }
180    ///     fn sub_name_len(&self) -> usize {
181    ///         self.sub_name.len()
182    ///     }
183    ///     fn format_name_lengths(slf: PyClassGuard<'_, Self>) -> String {
184    ///         format!("{} {}", slf.as_super().base_name_len(), slf.sub_name_len())
185    ///     }
186    /// }
187    /// # Python::attach(|py| {
188    /// #     let sub = Py::new(py, Sub::new()).unwrap();
189    /// #     pyo3::py_run!(py, sub, "assert sub.format_name_lengths() == '9 8'")
190    /// # });
191    /// ```
192    pub fn as_super(&self) -> &PyClassGuard<'a, U> {
193        // SAFETY: `PyClassGuard<T>` and `PyClassGuard<U>` have the same layout
194        unsafe { NonNull::from(self).cast().as_ref() }
195    }
196
197    /// Gets a `PyClassGuard<T::BaseType>`.
198    ///
199    /// With the help of this method, you can get hold of instances of the
200    /// super-superclass when needed.
201    ///
202    /// # Examples
203    /// ```
204    /// # use pyo3::prelude::*;
205    /// # use pyo3::PyClassGuard;
206    /// #[pyclass(subclass)]
207    /// struct Base1 {
208    ///     name1: &'static str,
209    /// }
210    ///
211    /// #[pyclass(extends=Base1, subclass)]
212    /// struct Base2 {
213    ///     name2: &'static str,
214    /// }
215    ///
216    /// #[pyclass(extends=Base2)]
217    /// struct Sub {
218    ///     name3: &'static str,
219    /// }
220    ///
221    /// #[pymethods]
222    /// impl Sub {
223    ///     #[new]
224    ///     fn new() -> PyClassInitializer<Self> {
225    ///         PyClassInitializer::from(Base1 { name1: "base1" })
226    ///             .add_subclass(Base2 { name2: "base2" })
227    ///             .add_subclass(Self { name3: "sub" })
228    ///     }
229    ///     fn name(slf: PyClassGuard<'_, Self>) -> String {
230    ///         let subname = slf.name3;
231    ///         let super_ = slf.into_super();
232    ///         format!("{} {} {}", super_.as_super().name1, super_.name2, subname)
233    ///     }
234    /// }
235    /// # Python::attach(|py| {
236    /// #     let sub = Py::new(py, Sub::new()).unwrap();
237    /// #     pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'")
238    /// # });
239    /// ```
240    pub fn into_super(self) -> PyClassGuard<'a, U> {
241        let t_not_frozen = !<T::Frozen as crate::pyclass::boolean_struct::private::Boolean>::VALUE;
242        let u_frozen = <U::Frozen as crate::pyclass::boolean_struct::private::Boolean>::VALUE;
243        if t_not_frozen && u_frozen {
244            // If `T` is a mutable subclass of a frozen `U` base, then it is possible that we need
245            // to release the borrow count now. (e.g. `U` may have a noop borrow checker so dropping
246            // the `PyRef<U>` later would noop and leak the borrow we currently hold.)
247            //
248            // However it's nontrivial, if `U` is frozen but itself has a mutable base class `V`,
249            // then the borrow checker of both `T` and `U` is the shared borrow checker of `V`.
250            //
251            // But it's really hard to prove that in the type system, the soundest thing we can do
252            // is just add a borrow to `U` now and then release the borrow of `T`.
253
254            self.as_super()
255                .as_class_object()
256                .borrow_checker()
257                .try_borrow()
258                .expect("this object is already borrowed");
259
260            self.as_class_object().borrow_checker().release_borrow()
261        };
262        PyClassGuard {
263            ptr: std::mem::ManuallyDrop::new(self).ptr,
264            marker: PhantomData,
265        }
266    }
267}
268
269impl<T: PyClass> Deref for PyClassGuard<'_, T> {
270    type Target = T;
271
272    #[inline]
273    fn deref(&self) -> &T {
274        // SAFETY: `PyClassObject<T>` constains a valid `T`, by construction no
275        // mutable alias is enforced
276        unsafe { &*self.as_class_object().get_ptr().cast_const() }
277    }
278}
279
280impl<'a, 'py, T: PyClass> FromPyObjectBound<'a, 'py> for PyClassGuard<'a, T> {
281    fn from_py_object_bound(obj: Borrowed<'a, 'py, crate::PyAny>) -> crate::PyResult<Self> {
282        Self::try_from_class_object(obj.cast()?.get_class_object()).map_err(Into::into)
283    }
284}
285
286impl<'a, 'py, T: PyClass> IntoPyObject<'py> for PyClassGuard<'a, T> {
287    type Target = T;
288    type Output = Borrowed<'a, 'py, T>;
289    type Error = Infallible;
290
291    #[inline]
292    fn into_pyobject(self, py: crate::Python<'py>) -> Result<Self::Output, Self::Error> {
293        (&self).into_pyobject(py)
294    }
295}
296
297impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &PyClassGuard<'a, T> {
298    type Target = T;
299    type Output = Borrowed<'a, 'py, T>;
300    type Error = Infallible;
301
302    #[cfg(feature = "experimental-inspect")]
303    const OUTPUT_TYPE: &'static str = T::PYTHON_TYPE;
304
305    #[inline]
306    fn into_pyobject(self, py: crate::Python<'py>) -> Result<Self::Output, Self::Error> {
307        // SAFETY: `ptr` is guaranteed to be valid for 'a and points to an
308        // object of type T
309        unsafe { Ok(Borrowed::from_non_null(py, self.ptr).cast_unchecked()) }
310    }
311}
312
313impl<T: PyClass> Drop for PyClassGuard<'_, T> {
314    /// Releases the shared borrow
315    fn drop(&mut self) {
316        self.as_class_object().borrow_checker().release_borrow()
317    }
318}
319
320// SAFETY: `PyClassGuard` only provides access to the inner `T` (and no other
321// Python APIs) which does not require a Python thread state
322#[cfg(feature = "nightly")]
323unsafe impl<T: PyClass> crate::marker::Ungil for PyClassGuard<'_, T> {}
324// SAFETY: we provide access to
325// - `&T`, which requires `T: Sync` to be Send and `T: Sync` to be Sync
326unsafe impl<T: PyClass + Sync> Send for PyClassGuard<'_, T> {}
327unsafe impl<T: PyClass + Sync> Sync for PyClassGuard<'_, T> {}
328
329/// A wrapper type for a mutably borrowed value from a `PyClass`
330///
331/// # When *not* to use [`PyClassGuardMut`]
332///
333/// Usually you can use `&mut` references as method and function receivers and
334/// arguments, and you won't need to use [`PyClassGuardMut`] directly:
335///
336/// ```rust,no_run
337/// use pyo3::prelude::*;
338///
339/// #[pyclass]
340/// struct Number {
341///     inner: u32,
342/// }
343///
344/// #[pymethods]
345/// impl Number {
346///     fn increment(&mut self) {
347///         self.inner += 1;
348///     }
349/// }
350/// ```
351///
352/// The [`#[pymethods]`](crate::pymethods) proc macro will generate this wrapper
353/// function (and more), using [`PyClassGuardMut`] under the hood:
354///
355/// ```rust,no_run
356/// # use pyo3::prelude::*;
357/// # #[pyclass]
358/// # struct Number {
359/// #    inner: u32,
360/// # }
361/// #
362/// # #[pymethods]
363/// # impl Number {
364/// #    fn increment(&mut self) {
365/// #        self.inner += 1;
366/// #    }
367/// # }
368/// #
369/// // The function which is exported to Python looks roughly like the following
370/// unsafe extern "C" fn __pymethod_increment__(
371///     _slf: *mut ::pyo3::ffi::PyObject,
372///     _args: *mut ::pyo3::ffi::PyObject,
373/// ) -> *mut ::pyo3::ffi::PyObject {
374///     unsafe fn inner<'py>(
375///         py: ::pyo3::Python<'py>,
376///         _slf: *mut ::pyo3::ffi::PyObject,
377///     ) -> ::pyo3::PyResult<*mut ::pyo3::ffi::PyObject> {
378///         let function = Number::increment;
379/// #       #[allow(clippy::let_unit_value)]
380///         let mut holder_0 = ::pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT;
381///         let result = {
382///             let ret = function(::pyo3::impl_::extract_argument::extract_pyclass_ref_mut::<Number>(
383///                 unsafe { ::pyo3::impl_::pymethods::BoundRef::ref_from_ptr(py, &_slf) }.0,
384///                 &mut holder_0,
385///             )?);
386///             {
387///                 let result = {
388///                     let obj = ret;
389/// #                   #[allow(clippy::useless_conversion)]
390///                     ::pyo3::impl_::wrap::converter(&obj)
391///                         .wrap(obj)
392///                         .map_err(::core::convert::Into::<::pyo3::PyErr>::into)
393///                 };
394///                 ::pyo3::impl_::wrap::converter(&result).map_into_ptr(py, result)
395///             }
396///         };
397///         result
398///     }
399///
400///     unsafe {
401///         ::pyo3::impl_::trampoline::noargs(
402///             _slf,
403///             _args,
404///             inner,
405///         )
406///     }
407/// }
408/// ```
409///
410/// # When to use [`PyClassGuardMut`]
411/// ## Using PyClasses from Rust
412///
413/// However, we *do* need [`PyClassGuardMut`] if we want to call its methods
414/// from Rust:
415/// ```rust
416/// # use pyo3::prelude::*;
417/// # use pyo3::{PyClassGuard, PyClassGuardMut};
418/// #
419/// # #[pyclass]
420/// # struct Number {
421/// #     inner: u32,
422/// # }
423/// #
424/// # #[pymethods]
425/// # impl Number {
426/// #     fn increment(&mut self) {
427/// #         self.inner += 1;
428/// #     }
429/// # }
430/// # fn main() -> PyResult<()> {
431/// Python::attach(|py| {
432///     let n = Py::new(py, Number { inner: 0 })?;
433///
434///     // We borrow the guard and then dereference
435///     // it to get a mutable reference to Number
436///     let mut guard: PyClassGuardMut<'_, Number> = n.extract(py)?;
437///     let n_mutable: &mut Number = &mut *guard;
438///
439///     n_mutable.increment();
440///
441///     // To avoid panics we must dispose of the
442///     // `PyClassGuardMut` before borrowing again.
443///     drop(guard);
444///
445///     let n_immutable: &Number = &*n.extract::<PyClassGuard<'_, Number>>(py)?;
446///     assert_eq!(n_immutable.inner, 1);
447///
448///     Ok(())
449/// })
450/// # }
451/// ```
452/// ## Dealing with possibly overlapping mutable references
453///
454/// It is also necessary to use [`PyClassGuardMut`] if you can receive mutable
455/// arguments that may overlap. Suppose the following function that swaps the
456/// values of two `Number`s:
457/// ```
458/// # use pyo3::prelude::*;
459/// # #[pyclass]
460/// # pub struct Number {
461/// #     inner: u32,
462/// # }
463/// #[pyfunction]
464/// fn swap_numbers(a: &mut Number, b: &mut Number) {
465///     std::mem::swap(&mut a.inner, &mut b.inner);
466/// }
467/// # fn main() {
468/// #     Python::attach(|py| {
469/// #         let n = Py::new(py, Number{inner: 35}).unwrap();
470/// #         let n2 = n.clone_ref(py);
471/// #         assert!(n.is(&n2));
472/// #         let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
473/// #         fun.call1((n, n2)).expect_err("Managed to create overlapping mutable references. Note: this is undefined behaviour.");
474/// #     });
475/// # }
476/// ```
477/// When users pass in the same `Number` as both arguments, one of the mutable
478/// borrows will fail and raise a `RuntimeError`:
479/// ```text
480/// >>> a = Number()
481/// >>> swap_numbers(a, a)
482/// Traceback (most recent call last):
483///   File "<stdin>", line 1, in <module>
484///   RuntimeError: Already borrowed
485/// ```
486///
487/// It is better to write that function like this:
488/// ```rust
489/// # use pyo3::prelude::*;
490/// # use pyo3::{PyClassGuard, PyClassGuardMut};
491/// # #[pyclass]
492/// # pub struct Number {
493/// #     inner: u32,
494/// # }
495/// #[pyfunction]
496/// fn swap_numbers(a: &Bound<'_, Number>, b: &Bound<'_, Number>) -> PyResult<()> {
497///     // Check that the pointers are unequal
498///     if !a.is(b) {
499///         let mut a: PyClassGuardMut<'_, Number> = a.extract()?;
500///         let mut b: PyClassGuardMut<'_, Number> = b.extract()?;
501///         std::mem::swap(&mut a.inner, &mut b.inner);
502///     } else {
503///         // Do nothing - they are the same object, so don't need swapping.
504///     }
505///     Ok(())
506/// }
507/// # fn main() {
508/// #     // With duplicate numbers
509/// #     Python::attach(|py| {
510/// #         let n = Py::new(py, Number{inner: 35}).unwrap();
511/// #         let n2 = n.clone_ref(py);
512/// #         assert!(n.is(&n2));
513/// #         let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
514/// #         fun.call1((n, n2)).unwrap();
515/// #     });
516/// #
517/// #     // With two different numbers
518/// #     Python::attach(|py| {
519/// #         let n = Py::new(py, Number{inner: 35}).unwrap();
520/// #         let n2 = Py::new(py, Number{inner: 42}).unwrap();
521/// #         assert!(!n.is(&n2));
522/// #         let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
523/// #         fun.call1((&n, &n2)).unwrap();
524/// #         let n: u32 = n.extract::<PyClassGuard<'_, Number>>(py).unwrap().inner;
525/// #         let n2: u32 = n2.extract::<PyClassGuard<'_, Number>>(py).unwrap().inner;
526/// #         assert_eq!(n, 42);
527/// #         assert_eq!(n2, 35);
528/// #     });
529/// # }
530/// ```
531/// See [`PyClassGuard`] and the [guide] for more information.
532///
533/// [guide]: https://pyo3.rs/latest/class.html#bound-and-interior-mutability
534///     "Bound and interior mutability"
535#[repr(transparent)]
536pub struct PyClassGuardMut<'a, T: PyClass<Frozen = False>> {
537    ptr: NonNull<ffi::PyObject>,
538    marker: PhantomData<&'a Py<T>>,
539}
540
541impl<'a, T: PyClass<Frozen = False>> PyClassGuardMut<'a, T> {
542    pub(crate) fn try_borrow_mut(obj: &'a Py<T>) -> Result<Self, PyBorrowMutError> {
543        Self::try_from_class_object(obj.get_class_object())
544    }
545
546    fn try_from_class_object(obj: &'a PyClassObject<T>) -> Result<Self, PyBorrowMutError> {
547        obj.ensure_threadsafe();
548        obj.borrow_checker().try_borrow_mut().map(|_| Self {
549            ptr: NonNull::from(obj).cast(),
550            marker: PhantomData,
551        })
552    }
553
554    pub(crate) fn as_class_object(&self) -> &'a PyClassObject<T> {
555        // SAFETY: `ptr` by construction points to a `PyClassObject<T>` and is
556        // valid for at least 'a
557        unsafe { self.ptr.cast().as_ref() }
558    }
559
560    /// Consumes the [`PyClassGuardMut`] and returns a [`PyClassGuardMap`] for a component of the
561    /// borrowed data
562    ///
563    /// # Examples
564    ///
565    /// ```
566    /// # use pyo3::prelude::*;
567    /// # use pyo3::PyClassGuardMut;
568    ///
569    /// #[pyclass]
570    /// pub struct MyClass {
571    ///     data: [i32; 100],
572    /// }
573    ///
574    /// # Python::attach(|py| {
575    /// let obj = Bound::new(py, MyClass { data: [0; 100] })?;
576    /// let mut data = obj.extract::<PyClassGuardMut<'_, MyClass>>()?.map(|c| c.data.as_mut_slice());
577    /// data[0] = 42;
578    /// # Ok::<_, PyErr>(())
579    /// # }).unwrap();
580    /// ```
581    pub fn map<F, U: ?Sized>(self, f: F) -> PyClassGuardMap<'a, U, true>
582    where
583        F: FnOnce(&mut T) -> &mut U,
584    {
585        let mut slf = std::mem::ManuallyDrop::new(self); // the borrow is released when dropping the `PyClassGuardMap`
586        PyClassGuardMap {
587            ptr: NonNull::from(f(&mut slf)),
588            checker: slf.as_class_object().borrow_checker(),
589        }
590    }
591}
592
593impl<'a, T, U> PyClassGuardMut<'a, T>
594where
595    T: PyClass<BaseType = U, Frozen = False>,
596    U: PyClass<Frozen = False>,
597{
598    /// Borrows a mutable reference to `PyClassGuardMut<T::BaseType>`.
599    ///
600    /// With the help of this method, you can mutate attributes and call
601    /// mutating methods on the superclass without consuming the
602    /// `PyClassGuardMut<T>`. This method can also be chained to access the
603    /// super-superclass (and so on).
604    ///
605    /// See [`PyClassGuard::as_super`] for more.
606    pub fn as_super(&mut self) -> &mut PyClassGuardMut<'a, U> {
607        // SAFETY: `PyClassGuardMut<T>` and `PyClassGuardMut<U>` have the same layout
608        unsafe { NonNull::from(self).cast().as_mut() }
609    }
610
611    /// Gets a `PyClassGuardMut<T::BaseType>`.
612    ///
613    /// See [`PyClassGuard::into_super`] for more.
614    pub fn into_super(self) -> PyClassGuardMut<'a, U> {
615        // `PyClassGuardMut` is only available for non-frozen classes, so there
616        // is no possibility of leaking borrows like `PyClassGuard`
617        PyClassGuardMut {
618            ptr: std::mem::ManuallyDrop::new(self).ptr,
619            marker: PhantomData,
620        }
621    }
622}
623
624impl<T: PyClass<Frozen = False>> Deref for PyClassGuardMut<'_, T> {
625    type Target = T;
626
627    #[inline]
628    fn deref(&self) -> &T {
629        // SAFETY: `PyClassObject<T>` constains a valid `T`, by construction no
630        // alias is enforced
631        unsafe { &*self.as_class_object().get_ptr().cast_const() }
632    }
633}
634impl<T: PyClass<Frozen = False>> DerefMut for PyClassGuardMut<'_, T> {
635    #[inline]
636    fn deref_mut(&mut self) -> &mut T {
637        // SAFETY: `PyClassObject<T>` constains a valid `T`, by construction no
638        // alias is enforced
639        unsafe { &mut *self.as_class_object().get_ptr() }
640    }
641}
642
643impl<'a, 'py, T: PyClass<Frozen = False>> FromPyObjectBound<'a, 'py> for PyClassGuardMut<'a, T> {
644    fn from_py_object_bound(obj: Borrowed<'a, 'py, crate::PyAny>) -> crate::PyResult<Self> {
645        Self::try_from_class_object(obj.cast()?.get_class_object()).map_err(Into::into)
646    }
647}
648
649impl<'a, 'py, T: PyClass<Frozen = False>> IntoPyObject<'py> for PyClassGuardMut<'a, T> {
650    type Target = T;
651    type Output = Borrowed<'a, 'py, T>;
652    type Error = Infallible;
653
654    #[inline]
655    fn into_pyobject(self, py: crate::Python<'py>) -> Result<Self::Output, Self::Error> {
656        (&self).into_pyobject(py)
657    }
658}
659
660impl<'a, 'py, T: PyClass<Frozen = False>> IntoPyObject<'py> for &PyClassGuardMut<'a, T> {
661    type Target = T;
662    type Output = Borrowed<'a, 'py, T>;
663    type Error = Infallible;
664
665    #[inline]
666    fn into_pyobject(self, py: crate::Python<'py>) -> Result<Self::Output, Self::Error> {
667        // SAFETY: `ptr` is guaranteed to be valid for 'a and points to an
668        // object of type T
669        unsafe { Ok(Borrowed::from_non_null(py, self.ptr).cast_unchecked()) }
670    }
671}
672
673impl<T: PyClass<Frozen = False>> Drop for PyClassGuardMut<'_, T> {
674    /// Releases the mutable borrow
675    fn drop(&mut self) {
676        self.as_class_object().borrow_checker().release_borrow_mut()
677    }
678}
679
680// SAFETY: `PyClassGuardMut` only provides access to the inner `T` (and no other
681// Python APIs) which does not require a Python thread state
682#[cfg(feature = "nightly")]
683unsafe impl<T: PyClass<Frozen = False>> crate::marker::Ungil for PyClassGuardMut<'_, T> {}
684// SAFETY: we provide access to
685// - `&T`, which requires `T: Sync` to be Send and `T: Sync` to be Sync
686// - `&mut T`, which requires `T: Send` to be Send and `T: Sync` to be Sync
687unsafe impl<T: PyClass<Frozen = False> + Send + Sync> Send for PyClassGuardMut<'_, T> {}
688unsafe impl<T: PyClass<Frozen = False> + Sync> Sync for PyClassGuardMut<'_, T> {}
689
690/// Wraps a borrowed reference `U` to a value stored inside of a pyclass `T`
691///
692/// See [`PyClassGuard::map`] and [`PyClassGuardMut::map`]
693pub struct PyClassGuardMap<'a, U: ?Sized, const MUT: bool> {
694    ptr: NonNull<U>,
695    checker: &'a dyn PyClassBorrowChecker,
696}
697
698impl<U: ?Sized, const MUT: bool> Deref for PyClassGuardMap<'_, U, MUT> {
699    type Target = U;
700
701    fn deref(&self) -> &U {
702        // SAFETY: `checker` guards our access to the `T` that `U` points into
703        unsafe { self.ptr.as_ref() }
704    }
705}
706
707impl<U: ?Sized> DerefMut for PyClassGuardMap<'_, U, true> {
708    fn deref_mut(&mut self) -> &mut Self::Target {
709        // SAFETY: `checker` guards our access to the `T` that `U` points into
710        unsafe { self.ptr.as_mut() }
711    }
712}
713
714impl<U: ?Sized, const MUT: bool> Drop for PyClassGuardMap<'_, U, MUT> {
715    fn drop(&mut self) {
716        if MUT {
717            self.checker.release_borrow_mut();
718        } else {
719            self.checker.release_borrow();
720        }
721    }
722}
723
724#[cfg(test)]
725#[cfg(feature = "macros")]
726mod tests {
727    use super::{PyClassGuard, PyClassGuardMut};
728    use crate::{types::PyAnyMethods as _, Bound, IntoPyObject as _, Py, PyErr, Python};
729
730    #[test]
731    fn test_into_frozen_super_released_borrow() {
732        #[crate::pyclass]
733        #[pyo3(crate = "crate", subclass, frozen)]
734        struct BaseClass {}
735
736        #[crate::pyclass]
737        #[pyo3(crate = "crate", extends=BaseClass, subclass)]
738        struct SubClass {}
739
740        #[crate::pymethods]
741        #[pyo3(crate = "crate")]
742        impl SubClass {
743            #[new]
744            fn new(py: Python<'_>) -> Py<SubClass> {
745                let init = crate::PyClassInitializer::from(BaseClass {}).add_subclass(SubClass {});
746                Py::new(py, init).expect("allocation error")
747            }
748        }
749
750        Python::attach(|py| {
751            let obj = SubClass::new(py);
752            drop(PyClassGuard::try_borrow(&obj).unwrap().into_super());
753            assert!(PyClassGuardMut::try_borrow_mut(&obj).is_ok());
754        })
755    }
756
757    #[test]
758    fn test_into_frozen_super_mutable_base_holds_borrow() {
759        #[crate::pyclass]
760        #[pyo3(crate = "crate", subclass)]
761        struct BaseClass {}
762
763        #[crate::pyclass]
764        #[pyo3(crate = "crate", extends=BaseClass, subclass, frozen)]
765        struct SubClass {}
766
767        #[crate::pyclass]
768        #[pyo3(crate = "crate", extends=SubClass, subclass)]
769        struct SubSubClass {}
770
771        #[crate::pymethods]
772        #[pyo3(crate = "crate")]
773        impl SubSubClass {
774            #[new]
775            fn new(py: Python<'_>) -> Py<SubSubClass> {
776                let init = crate::PyClassInitializer::from(BaseClass {})
777                    .add_subclass(SubClass {})
778                    .add_subclass(SubSubClass {});
779                Py::new(py, init).expect("allocation error")
780            }
781        }
782
783        Python::attach(|py| {
784            let obj = SubSubClass::new(py);
785            let _super_borrow = PyClassGuard::try_borrow(&obj).unwrap().into_super();
786            // the whole object still has an immutable borrow, so we cannot
787            // borrow any part mutably (the borrowflag is shared)
788            assert!(PyClassGuardMut::try_borrow_mut(&obj).is_err());
789        })
790    }
791
792    #[crate::pyclass]
793    #[pyo3(crate = "crate", subclass)]
794    struct BaseClass {
795        val1: usize,
796    }
797
798    #[crate::pyclass]
799    #[pyo3(crate = "crate", extends=BaseClass, subclass)]
800    struct SubClass {
801        val2: usize,
802    }
803
804    #[crate::pyclass]
805    #[pyo3(crate = "crate", extends=SubClass)]
806    struct SubSubClass {
807        #[pyo3(get)]
808        val3: usize,
809    }
810
811    #[crate::pymethods]
812    #[pyo3(crate = "crate")]
813    impl SubSubClass {
814        #[new]
815        fn new(py: Python<'_>) -> Py<SubSubClass> {
816            let init = crate::PyClassInitializer::from(BaseClass { val1: 10 })
817                .add_subclass(SubClass { val2: 15 })
818                .add_subclass(SubSubClass { val3: 20 });
819            Py::new(py, init).expect("allocation error")
820        }
821
822        fn get_values(self_: PyClassGuard<'_, Self>) -> (usize, usize, usize) {
823            let val1 = self_.as_super().as_super().val1;
824            let val2 = self_.as_super().val2;
825            (val1, val2, self_.val3)
826        }
827
828        fn double_values(mut self_: PyClassGuardMut<'_, Self>) {
829            self_.as_super().as_super().val1 *= 2;
830            self_.as_super().val2 *= 2;
831            self_.val3 *= 2;
832        }
833
834        fn __add__<'a>(
835            mut slf: PyClassGuardMut<'a, Self>,
836            other: PyClassGuard<'a, Self>,
837        ) -> PyClassGuardMut<'a, Self> {
838            slf.val3 += other.val3;
839            slf
840        }
841
842        fn __rsub__<'a>(
843            slf: PyClassGuard<'a, Self>,
844            mut other: PyClassGuardMut<'a, Self>,
845        ) -> PyClassGuardMut<'a, Self> {
846            other.val3 -= slf.val3;
847            other
848        }
849    }
850
851    #[test]
852    fn test_pyclassguard_into_pyobject() {
853        Python::attach(|py| {
854            let class = Py::new(py, BaseClass { val1: 42 })?;
855            let guard = PyClassGuard::try_borrow(&class).unwrap();
856            let new_ref = (&guard).into_pyobject(py)?;
857            assert!(new_ref.is(&class));
858            let new = guard.into_pyobject(py)?;
859            assert!(new.is(&class));
860            Ok::<_, PyErr>(())
861        })
862        .unwrap();
863    }
864
865    #[test]
866    fn test_pyclassguardmut_into_pyobject() {
867        Python::attach(|py| {
868            let class = Py::new(py, BaseClass { val1: 42 })?;
869            let guard = PyClassGuardMut::try_borrow_mut(&class).unwrap();
870            let new_ref = (&guard).into_pyobject(py)?;
871            assert!(new_ref.is(&class));
872            let new = guard.into_pyobject(py)?;
873            assert!(new.is(&class));
874            Ok::<_, PyErr>(())
875        })
876        .unwrap();
877    }
878    #[test]
879    fn test_pyclassguard_as_super() {
880        Python::attach(|py| {
881            let obj = SubSubClass::new(py).into_bound(py);
882            let pyref = PyClassGuard::try_borrow(obj.as_unbound()).unwrap();
883            assert_eq!(pyref.as_super().as_super().val1, 10);
884            assert_eq!(pyref.as_super().val2, 15);
885            assert_eq!(pyref.val3, 20);
886            assert_eq!(SubSubClass::get_values(pyref), (10, 15, 20));
887        });
888    }
889
890    #[test]
891    fn test_pyclassguardmut_as_super() {
892        Python::attach(|py| {
893            let obj = SubSubClass::new(py).into_bound(py);
894            assert_eq!(
895                SubSubClass::get_values(PyClassGuard::try_borrow(obj.as_unbound()).unwrap()),
896                (10, 15, 20)
897            );
898            {
899                let mut pyrefmut = PyClassGuardMut::try_borrow_mut(obj.as_unbound()).unwrap();
900                assert_eq!(pyrefmut.as_super().as_super().val1, 10);
901                pyrefmut.as_super().as_super().val1 -= 5;
902                pyrefmut.as_super().val2 -= 5;
903                pyrefmut.val3 -= 5;
904            }
905            assert_eq!(
906                SubSubClass::get_values(PyClassGuard::try_borrow(obj.as_unbound()).unwrap()),
907                (5, 10, 15)
908            );
909            SubSubClass::double_values(PyClassGuardMut::try_borrow_mut(obj.as_unbound()).unwrap());
910            assert_eq!(
911                SubSubClass::get_values(PyClassGuard::try_borrow(obj.as_unbound()).unwrap()),
912                (10, 20, 30)
913            );
914        });
915    }
916
917    #[test]
918    fn test_extract_guard() {
919        Python::attach(|py| {
920            let obj1 = SubSubClass::new(py);
921            let obj2 = SubSubClass::new(py);
922            crate::py_run!(py, obj1 obj2, "assert ((obj1 + obj2) - obj2).val3 == obj1.val3");
923        });
924    }
925
926    #[test]
927    fn test_pyclassguards_in_python() {
928        Python::attach(|py| {
929            let obj = SubSubClass::new(py);
930            crate::py_run!(py, obj, "assert obj.get_values() == (10, 15, 20)");
931            crate::py_run!(py, obj, "assert obj.double_values() is None");
932            crate::py_run!(py, obj, "assert obj.get_values() == (20, 30, 40)");
933        });
934    }
935
936    #[crate::pyclass]
937    #[pyo3(crate = "crate")]
938    pub struct MyClass {
939        data: [i32; 100],
940    }
941
942    #[test]
943    fn test_pyclassguard_map() {
944        Python::attach(|py| {
945            let obj = Bound::new(py, MyClass { data: [0; 100] })?;
946            let data = PyClassGuard::try_borrow(obj.as_unbound())?.map(|c| &c.data);
947            assert_eq!(data[0], 0);
948            assert!(obj.try_borrow_mut().is_err()); // obj is still protected
949            drop(data);
950            assert!(obj.try_borrow_mut().is_ok()); // drop released shared borrow
951            Ok::<_, PyErr>(())
952        })
953        .unwrap()
954    }
955
956    #[test]
957    fn test_pyclassguardmut_map() {
958        Python::attach(|py| {
959            let obj = Bound::new(py, MyClass { data: [0; 100] })?;
960            let mut data =
961                PyClassGuardMut::try_borrow_mut(obj.as_unbound())?.map(|c| c.data.as_mut_slice());
962            assert_eq!(data[0], 0);
963            data[0] = 5;
964            assert_eq!(data[0], 5);
965            assert!(obj.try_borrow_mut().is_err()); // obj is still protected
966            drop(data);
967            assert!(obj.try_borrow_mut().is_ok()); // drop released mutable borrow
968            Ok::<_, PyErr>(())
969        })
970        .unwrap()
971    }
972
973    #[test]
974    fn test_pyclassguard_map_unrelated() {
975        use crate::types::{PyString, PyStringMethods};
976        Python::attach(|py| {
977            let obj = Bound::new(py, MyClass { data: [0; 100] })?;
978            let string = PyString::new(py, "pyo3");
979            // It is possible to return something not borrowing from the guard, but that shouldn't
980            // matter. `RefCell` has the same behaviour
981            let refmap = PyClassGuard::try_borrow(obj.as_unbound())?.map(|_| &string);
982            assert_eq!(refmap.to_cow()?, "pyo3");
983            Ok::<_, PyErr>(())
984        })
985        .unwrap()
986    }
987}