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}