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