pyo3/pycell.rs
1//! PyO3's interior mutability primitive.
2//!
3//! Rust has strict aliasing rules - you can either have any number of immutable (shared) references or one mutable
4//! reference. Python's ownership model is the complete opposite of that - any Python object
5//! can be referenced any number of times, and mutation is allowed from any reference.
6//!
7//! PyO3 deals with these differences by employing the [Interior Mutability]
8//! pattern. This requires that PyO3 enforces the borrowing rules and it has two mechanisms for
9//! doing so:
10//! - Statically it can enforce thread-safe access with the [`Python<'py>`](crate::Python) token.
11//! All Rust code holding that token, or anything derived from it, can assume that they have
12//! safe access to the Python interpreter's state. For this reason all the native Python objects
13//! can be mutated through shared references.
14//! - However, methods and functions in Rust usually *do* need `&mut` references. While PyO3 can
15//! use the [`Python<'py>`](crate::Python) token to guarantee thread-safe access to them, it cannot
16//! statically guarantee uniqueness of `&mut` references. As such those references have to be tracked
17//! dynamically at runtime, using `PyCell` and the other types defined in this module. This works
18//! similar to std's [`RefCell`](std::cell::RefCell) type.
19//!
20//! # When *not* to use PyCell
21//!
22//! Usually you can use `&mut` references as method and function receivers and arguments, and you
23//! won't need to use `PyCell` directly:
24//!
25//! ```rust,no_run
26//! use pyo3::prelude::*;
27//!
28//! #[pyclass]
29//! struct Number {
30//! inner: u32,
31//! }
32//!
33//! #[pymethods]
34//! impl Number {
35//! fn increment(&mut self) {
36//! self.inner += 1;
37//! }
38//! }
39//! ```
40//!
41//! The [`#[pymethods]`](crate::pymethods) proc macro will generate this wrapper function (and more),
42//! using `PyCell` under the hood:
43//!
44//! ```rust,ignore
45//! # use pyo3::prelude::*;
46//! # #[pyclass]
47//! # struct Number {
48//! # inner: u32,
49//! # }
50//! #
51//! # #[pymethods]
52//! # impl Number {
53//! # fn increment(&mut self) {
54//! # self.inner += 1;
55//! # }
56//! # }
57//! #
58//! // The function which is exported to Python looks roughly like the following
59//! unsafe extern "C" fn __pymethod_increment__(
60//! _slf: *mut pyo3::ffi::PyObject,
61//! _args: *mut pyo3::ffi::PyObject,
62//! ) -> *mut pyo3::ffi::PyObject {
63//! use :: pyo3 as _pyo3;
64//! _pyo3::impl_::trampoline::noargs(_slf, _args, |py, _slf| {
65//! # #[allow(deprecated)]
66//! let _cell = py
67//! .from_borrowed_ptr::<_pyo3::PyAny>(_slf)
68//! .downcast::<_pyo3::PyCell<Number>>()?;
69//! let mut _ref = _cell.try_borrow_mut()?;
70//! let _slf: &mut Number = &mut *_ref;
71//! _pyo3::impl_::callback::convert(py, Number::increment(_slf))
72//! })
73//! }
74//! ```
75//!
76//! # When to use PyCell
77//! ## Using pyclasses from Rust
78//!
79//! However, we *do* need `PyCell` if we want to call its methods from Rust:
80//! ```rust
81//! # use pyo3::prelude::*;
82//! #
83//! # #[pyclass]
84//! # struct Number {
85//! # inner: u32,
86//! # }
87//! #
88//! # #[pymethods]
89//! # impl Number {
90//! # fn increment(&mut self) {
91//! # self.inner += 1;
92//! # }
93//! # }
94//! # fn main() -> PyResult<()> {
95//! Python::with_gil(|py| {
96//! let n = Py::new(py, Number { inner: 0 })?;
97//!
98//! // We borrow the guard and then dereference
99//! // it to get a mutable reference to Number
100//! let mut guard: PyRefMut<'_, Number> = n.bind(py).borrow_mut();
101//! let n_mutable: &mut Number = &mut *guard;
102//!
103//! n_mutable.increment();
104//!
105//! // To avoid panics we must dispose of the
106//! // `PyRefMut` before borrowing again.
107//! drop(guard);
108//!
109//! let n_immutable: &Number = &n.bind(py).borrow();
110//! assert_eq!(n_immutable.inner, 1);
111//!
112//! Ok(())
113//! })
114//! # }
115//! ```
116//! ## Dealing with possibly overlapping mutable references
117//!
118//! It is also necessary to use `PyCell` if you can receive mutable arguments that may overlap.
119//! Suppose the following function that swaps the values of two `Number`s:
120//! ```
121//! # use pyo3::prelude::*;
122//! # #[pyclass]
123//! # pub struct Number {
124//! # inner: u32,
125//! # }
126//! #[pyfunction]
127//! fn swap_numbers(a: &mut Number, b: &mut Number) {
128//! std::mem::swap(&mut a.inner, &mut b.inner);
129//! }
130//! # fn main() {
131//! # Python::with_gil(|py| {
132//! # let n = Py::new(py, Number{inner: 35}).unwrap();
133//! # let n2 = n.clone_ref(py);
134//! # assert!(n.is(&n2));
135//! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
136//! # fun.call1((n, n2)).expect_err("Managed to create overlapping mutable references. Note: this is undefined behaviour.");
137//! # });
138//! # }
139//! ```
140//! When users pass in the same `Number` as both arguments, one of the mutable borrows will
141//! fail and raise a `RuntimeError`:
142//! ```text
143//! >>> a = Number()
144//! >>> swap_numbers(a, a)
145//! Traceback (most recent call last):
146//! File "<stdin>", line 1, in <module>
147//! RuntimeError: Already borrowed
148//! ```
149//!
150//! It is better to write that function like this:
151//! ```rust,ignore
152//! # #![allow(deprecated)]
153//! # use pyo3::prelude::*;
154//! # #[pyclass]
155//! # pub struct Number {
156//! # inner: u32,
157//! # }
158//! #[pyfunction]
159//! fn swap_numbers(a: &PyCell<Number>, b: &PyCell<Number>) {
160//! // Check that the pointers are unequal
161//! if !a.is(b) {
162//! std::mem::swap(&mut a.borrow_mut().inner, &mut b.borrow_mut().inner);
163//! } else {
164//! // Do nothing - they are the same object, so don't need swapping.
165//! }
166//! }
167//! # fn main() {
168//! # // With duplicate numbers
169//! # Python::with_gil(|py| {
170//! # let n = Py::new(py, Number{inner: 35}).unwrap();
171//! # let n2 = n.clone_ref(py);
172//! # assert!(n.is(&n2));
173//! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
174//! # fun.call1((n, n2)).unwrap();
175//! # });
176//! #
177//! # // With two different numbers
178//! # Python::with_gil(|py| {
179//! # let n = Py::new(py, Number{inner: 35}).unwrap();
180//! # let n2 = Py::new(py, Number{inner: 42}).unwrap();
181//! # assert!(!n.is(&n2));
182//! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap();
183//! # fun.call1((&n, &n2)).unwrap();
184//! # let n: u32 = n.borrow(py).inner;
185//! # let n2: u32 = n2.borrow(py).inner;
186//! # assert_eq!(n, 42);
187//! # assert_eq!(n2, 35);
188//! # });
189//! # }
190//! ```
191//! See the [guide] for more information.
192//!
193//! [guide]: https://pyo3.rs/latest/class.html#pycell-and-interior-mutability "PyCell and interior mutability"
194//! [Interior Mutability]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html "RefCell<T> and the Interior Mutability Pattern - The Rust Programming Language"
195
196use crate::conversion::IntoPyObject;
197use crate::exceptions::PyRuntimeError;
198use crate::ffi_ptr_ext::FfiPtrExt;
199use crate::internal_tricks::{ptr_from_mut, ptr_from_ref};
200use crate::pyclass::{boolean_struct::False, PyClass};
201use crate::types::any::PyAnyMethods;
202use crate::{ffi, Borrowed, Bound, PyErr, Python};
203use std::convert::Infallible;
204use std::fmt;
205use std::mem::ManuallyDrop;
206use std::ops::{Deref, DerefMut};
207
208pub(crate) mod impl_;
209use impl_::{PyClassBorrowChecker, PyClassObjectLayout};
210
211/// A wrapper type for an immutably borrowed value from a [`Bound<'py, T>`].
212///
213/// See the [`Bound`] documentation for more information.
214///
215/// # Examples
216///
217/// You can use [`PyRef`] as an alternative to a `&self` receiver when
218/// - you need to access the pointer of the [`Bound`], or
219/// - you want to get a super class.
220/// ```
221/// # use pyo3::prelude::*;
222/// #[pyclass(subclass)]
223/// struct Parent {
224/// basename: &'static str,
225/// }
226///
227/// #[pyclass(extends=Parent)]
228/// struct Child {
229/// name: &'static str,
230/// }
231///
232/// #[pymethods]
233/// impl Child {
234/// #[new]
235/// fn new() -> (Self, Parent) {
236/// (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" })
237/// }
238///
239/// fn format(slf: PyRef<'_, Self>) -> String {
240/// // We can get *mut ffi::PyObject from PyRef
241/// let refcnt = unsafe { pyo3::ffi::Py_REFCNT(slf.as_ptr()) };
242/// // We can get &Self::BaseType by as_ref
243/// let basename = slf.as_ref().basename;
244/// format!("{}(base: {}, cnt: {})", slf.name, basename, refcnt)
245/// }
246/// }
247/// # Python::with_gil(|py| {
248/// # let sub = Py::new(py, Child::new()).unwrap();
249/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 4)', sub.format()");
250/// # });
251/// ```
252///
253/// See the [module-level documentation](self) for more information.
254#[repr(transparent)]
255pub struct PyRef<'p, T: PyClass> {
256 // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to
257 // store `Borrowed` here instead, avoiding reference counting overhead.
258 inner: Bound<'p, T>,
259}
260
261impl<'p, T: PyClass> PyRef<'p, T> {
262 /// Returns a `Python` token that is bound to the lifetime of the `PyRef`.
263 pub fn py(&self) -> Python<'p> {
264 self.inner.py()
265 }
266}
267
268impl<T, U> AsRef<U> for PyRef<'_, T>
269where
270 T: PyClass<BaseType = U>,
271 U: PyClass,
272{
273 fn as_ref(&self) -> &T::BaseType {
274 self.as_super()
275 }
276}
277
278impl<'py, T: PyClass> PyRef<'py, T> {
279 /// Returns the raw FFI pointer represented by self.
280 ///
281 /// # Safety
282 ///
283 /// Callers are responsible for ensuring that the pointer does not outlive self.
284 ///
285 /// The reference is borrowed; callers should not decrease the reference count
286 /// when they are finished with the pointer.
287 #[inline]
288 pub fn as_ptr(&self) -> *mut ffi::PyObject {
289 self.inner.as_ptr()
290 }
291
292 /// Returns an owned raw FFI pointer represented by self.
293 ///
294 /// # Safety
295 ///
296 /// The reference is owned; when finished the caller should either transfer ownership
297 /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)).
298 #[inline]
299 pub fn into_ptr(self) -> *mut ffi::PyObject {
300 self.inner.clone().into_ptr()
301 }
302
303 #[track_caller]
304 pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self {
305 Self::try_borrow(obj).expect("Already mutably borrowed")
306 }
307
308 pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result<Self, PyBorrowError> {
309 let cell = obj.get_class_object();
310 cell.ensure_threadsafe();
311 cell.borrow_checker()
312 .try_borrow()
313 .map(|_| Self { inner: obj.clone() })
314 }
315}
316
317impl<'p, T, U> PyRef<'p, T>
318where
319 T: PyClass<BaseType = U>,
320 U: PyClass,
321{
322 /// Gets a `PyRef<T::BaseType>`.
323 ///
324 /// While `as_ref()` returns a reference of type `&T::BaseType`, this cannot be
325 /// used to get the base of `T::BaseType`.
326 ///
327 /// But with the help of this method, you can get hold of instances of the
328 /// super-superclass when needed.
329 ///
330 /// # Examples
331 /// ```
332 /// # use pyo3::prelude::*;
333 /// #[pyclass(subclass)]
334 /// struct Base1 {
335 /// name1: &'static str,
336 /// }
337 ///
338 /// #[pyclass(extends=Base1, subclass)]
339 /// struct Base2 {
340 /// name2: &'static str,
341 /// }
342 ///
343 /// #[pyclass(extends=Base2)]
344 /// struct Sub {
345 /// name3: &'static str,
346 /// }
347 ///
348 /// #[pymethods]
349 /// impl Sub {
350 /// #[new]
351 /// fn new() -> PyClassInitializer<Self> {
352 /// PyClassInitializer::from(Base1 { name1: "base1" })
353 /// .add_subclass(Base2 { name2: "base2" })
354 /// .add_subclass(Self { name3: "sub" })
355 /// }
356 /// fn name(slf: PyRef<'_, Self>) -> String {
357 /// let subname = slf.name3;
358 /// let super_ = slf.into_super();
359 /// format!("{} {} {}", super_.as_ref().name1, super_.name2, subname)
360 /// }
361 /// }
362 /// # Python::with_gil(|py| {
363 /// # let sub = Py::new(py, Sub::new()).unwrap();
364 /// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'")
365 /// # });
366 /// ```
367 pub fn into_super(self) -> PyRef<'p, U> {
368 let py = self.py();
369 PyRef {
370 inner: unsafe {
371 ManuallyDrop::new(self)
372 .as_ptr()
373 .assume_owned_unchecked(py)
374 .downcast_into_unchecked()
375 },
376 }
377 }
378
379 /// Borrows a shared reference to `PyRef<T::BaseType>`.
380 ///
381 /// With the help of this method, you can access attributes and call methods
382 /// on the superclass without consuming the `PyRef<T>`. This method can also
383 /// be chained to access the super-superclass (and so on).
384 ///
385 /// # Examples
386 /// ```
387 /// # use pyo3::prelude::*;
388 /// #[pyclass(subclass)]
389 /// struct Base {
390 /// base_name: &'static str,
391 /// }
392 /// #[pymethods]
393 /// impl Base {
394 /// fn base_name_len(&self) -> usize {
395 /// self.base_name.len()
396 /// }
397 /// }
398 ///
399 /// #[pyclass(extends=Base)]
400 /// struct Sub {
401 /// sub_name: &'static str,
402 /// }
403 ///
404 /// #[pymethods]
405 /// impl Sub {
406 /// #[new]
407 /// fn new() -> (Self, Base) {
408 /// (Self { sub_name: "sub_name" }, Base { base_name: "base_name" })
409 /// }
410 /// fn sub_name_len(&self) -> usize {
411 /// self.sub_name.len()
412 /// }
413 /// fn format_name_lengths(slf: PyRef<'_, Self>) -> String {
414 /// format!("{} {}", slf.as_super().base_name_len(), slf.sub_name_len())
415 /// }
416 /// }
417 /// # Python::with_gil(|py| {
418 /// # let sub = Py::new(py, Sub::new()).unwrap();
419 /// # pyo3::py_run!(py, sub, "assert sub.format_name_lengths() == '9 8'")
420 /// # });
421 /// ```
422 pub fn as_super(&self) -> &PyRef<'p, U> {
423 let ptr = ptr_from_ref::<Bound<'p, T>>(&self.inner)
424 // `Bound<T>` has the same layout as `Bound<T::BaseType>`
425 .cast::<Bound<'p, T::BaseType>>()
426 // `Bound<T::BaseType>` has the same layout as `PyRef<T::BaseType>`
427 .cast::<PyRef<'p, T::BaseType>>();
428 unsafe { &*ptr }
429 }
430}
431
432impl<T: PyClass> Deref for PyRef<'_, T> {
433 type Target = T;
434
435 #[inline]
436 fn deref(&self) -> &T {
437 unsafe { &*self.inner.get_class_object().get_ptr() }
438 }
439}
440
441impl<T: PyClass> Drop for PyRef<'_, T> {
442 fn drop(&mut self) {
443 self.inner
444 .get_class_object()
445 .borrow_checker()
446 .release_borrow()
447 }
448}
449
450impl<'py, T: PyClass> IntoPyObject<'py> for PyRef<'py, T> {
451 type Target = T;
452 type Output = Bound<'py, T>;
453 type Error = Infallible;
454
455 fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
456 Ok(self.inner.clone())
457 }
458}
459
460impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRef<'py, T> {
461 type Target = T;
462 type Output = Borrowed<'a, 'py, T>;
463 type Error = Infallible;
464
465 fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
466 Ok(self.inner.as_borrowed())
467 }
468}
469
470impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> {
471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472 fmt::Debug::fmt(&**self, f)
473 }
474}
475
476/// A wrapper type for a mutably borrowed value from a [`Bound<'py, T>`].
477///
478/// See the [module-level documentation](self) for more information.
479#[repr(transparent)]
480pub struct PyRefMut<'p, T: PyClass<Frozen = False>> {
481 // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to
482 // store `Borrowed` here instead, avoiding reference counting overhead.
483 inner: Bound<'p, T>,
484}
485
486impl<'p, T: PyClass<Frozen = False>> PyRefMut<'p, T> {
487 /// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`.
488 pub fn py(&self) -> Python<'p> {
489 self.inner.py()
490 }
491}
492
493impl<T, U> AsRef<U> for PyRefMut<'_, T>
494where
495 T: PyClass<BaseType = U, Frozen = False>,
496 U: PyClass<Frozen = False>,
497{
498 fn as_ref(&self) -> &T::BaseType {
499 PyRefMut::downgrade(self).as_super()
500 }
501}
502
503impl<T, U> AsMut<U> for PyRefMut<'_, T>
504where
505 T: PyClass<BaseType = U, Frozen = False>,
506 U: PyClass<Frozen = False>,
507{
508 fn as_mut(&mut self) -> &mut T::BaseType {
509 self.as_super()
510 }
511}
512
513impl<'py, T: PyClass<Frozen = False>> PyRefMut<'py, T> {
514 /// Returns the raw FFI pointer represented by self.
515 ///
516 /// # Safety
517 ///
518 /// Callers are responsible for ensuring that the pointer does not outlive self.
519 ///
520 /// The reference is borrowed; callers should not decrease the reference count
521 /// when they are finished with the pointer.
522 #[inline]
523 pub fn as_ptr(&self) -> *mut ffi::PyObject {
524 self.inner.as_ptr()
525 }
526
527 /// Returns an owned raw FFI pointer represented by self.
528 ///
529 /// # Safety
530 ///
531 /// The reference is owned; when finished the caller should either transfer ownership
532 /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)).
533 #[inline]
534 pub fn into_ptr(self) -> *mut ffi::PyObject {
535 self.inner.clone().into_ptr()
536 }
537
538 #[inline]
539 #[track_caller]
540 pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self {
541 Self::try_borrow(obj).expect("Already borrowed")
542 }
543
544 pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result<Self, PyBorrowMutError> {
545 let cell = obj.get_class_object();
546 cell.ensure_threadsafe();
547 cell.borrow_checker()
548 .try_borrow_mut()
549 .map(|_| Self { inner: obj.clone() })
550 }
551
552 pub(crate) fn downgrade(slf: &Self) -> &PyRef<'py, T> {
553 // `PyRefMut<T>` and `PyRef<T>` have the same layout
554 unsafe { &*ptr_from_ref(slf).cast() }
555 }
556}
557
558impl<'p, T, U> PyRefMut<'p, T>
559where
560 T: PyClass<BaseType = U, Frozen = False>,
561 U: PyClass<Frozen = False>,
562{
563 /// Gets a `PyRef<T::BaseType>`.
564 ///
565 /// See [`PyRef::into_super`] for more.
566 pub fn into_super(self) -> PyRefMut<'p, U> {
567 let py = self.py();
568 PyRefMut {
569 inner: unsafe {
570 ManuallyDrop::new(self)
571 .as_ptr()
572 .assume_owned_unchecked(py)
573 .downcast_into_unchecked()
574 },
575 }
576 }
577
578 /// Borrows a mutable reference to `PyRefMut<T::BaseType>`.
579 ///
580 /// With the help of this method, you can mutate attributes and call mutating
581 /// methods on the superclass without consuming the `PyRefMut<T>`. This method
582 /// can also be chained to access the super-superclass (and so on).
583 ///
584 /// See [`PyRef::as_super`] for more.
585 pub fn as_super(&mut self) -> &mut PyRefMut<'p, U> {
586 let ptr = ptr_from_mut::<Bound<'p, T>>(&mut self.inner)
587 // `Bound<T>` has the same layout as `Bound<T::BaseType>`
588 .cast::<Bound<'p, T::BaseType>>()
589 // `Bound<T::BaseType>` has the same layout as `PyRefMut<T::BaseType>`,
590 // and the mutable borrow on `self` prevents aliasing
591 .cast::<PyRefMut<'p, T::BaseType>>();
592 unsafe { &mut *ptr }
593 }
594}
595
596impl<T: PyClass<Frozen = False>> Deref for PyRefMut<'_, T> {
597 type Target = T;
598
599 #[inline]
600 fn deref(&self) -> &T {
601 unsafe { &*self.inner.get_class_object().get_ptr() }
602 }
603}
604
605impl<T: PyClass<Frozen = False>> DerefMut for PyRefMut<'_, T> {
606 #[inline]
607 fn deref_mut(&mut self) -> &mut T {
608 unsafe { &mut *self.inner.get_class_object().get_ptr() }
609 }
610}
611
612impl<T: PyClass<Frozen = False>> Drop for PyRefMut<'_, T> {
613 fn drop(&mut self) {
614 self.inner
615 .get_class_object()
616 .borrow_checker()
617 .release_borrow_mut()
618 }
619}
620
621impl<'py, T: PyClass<Frozen = False>> IntoPyObject<'py> for PyRefMut<'py, T> {
622 type Target = T;
623 type Output = Bound<'py, T>;
624 type Error = Infallible;
625
626 fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
627 Ok(self.inner.clone())
628 }
629}
630
631impl<'a, 'py, T: PyClass<Frozen = False>> IntoPyObject<'py> for &'a PyRefMut<'py, T> {
632 type Target = T;
633 type Output = Borrowed<'a, 'py, T>;
634 type Error = Infallible;
635
636 fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
637 Ok(self.inner.as_borrowed())
638 }
639}
640
641impl<T: PyClass<Frozen = False> + fmt::Debug> fmt::Debug for PyRefMut<'_, T> {
642 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
643 fmt::Debug::fmt(self.deref(), f)
644 }
645}
646
647/// An error type returned by [`Bound::try_borrow`].
648///
649/// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`.
650pub struct PyBorrowError {
651 _private: (),
652}
653
654impl fmt::Debug for PyBorrowError {
655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 f.debug_struct("PyBorrowError").finish()
657 }
658}
659
660impl fmt::Display for PyBorrowError {
661 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
662 fmt::Display::fmt("Already mutably borrowed", f)
663 }
664}
665
666impl From<PyBorrowError> for PyErr {
667 fn from(other: PyBorrowError) -> Self {
668 PyRuntimeError::new_err(other.to_string())
669 }
670}
671
672/// An error type returned by [`Bound::try_borrow_mut`].
673///
674/// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`.
675pub struct PyBorrowMutError {
676 _private: (),
677}
678
679impl fmt::Debug for PyBorrowMutError {
680 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681 f.debug_struct("PyBorrowMutError").finish()
682 }
683}
684
685impl fmt::Display for PyBorrowMutError {
686 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
687 fmt::Display::fmt("Already borrowed", f)
688 }
689}
690
691impl From<PyBorrowMutError> for PyErr {
692 fn from(other: PyBorrowMutError) -> Self {
693 PyRuntimeError::new_err(other.to_string())
694 }
695}
696
697#[cfg(test)]
698#[cfg(feature = "macros")]
699mod tests {
700
701 use super::*;
702
703 #[crate::pyclass]
704 #[pyo3(crate = "crate")]
705 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
706 struct SomeClass(i32);
707
708 #[test]
709 fn test_as_ptr() {
710 Python::with_gil(|py| {
711 let cell = Bound::new(py, SomeClass(0)).unwrap();
712 let ptr = cell.as_ptr();
713
714 assert_eq!(cell.borrow().as_ptr(), ptr);
715 assert_eq!(cell.borrow_mut().as_ptr(), ptr);
716 })
717 }
718
719 #[test]
720 fn test_into_ptr() {
721 Python::with_gil(|py| {
722 let cell = Bound::new(py, SomeClass(0)).unwrap();
723 let ptr = cell.as_ptr();
724
725 assert_eq!(cell.borrow().into_ptr(), ptr);
726 unsafe { ffi::Py_DECREF(ptr) };
727
728 assert_eq!(cell.borrow_mut().into_ptr(), ptr);
729 unsafe { ffi::Py_DECREF(ptr) };
730 })
731 }
732
733 #[crate::pyclass]
734 #[pyo3(crate = "crate", subclass)]
735 struct BaseClass {
736 val1: usize,
737 }
738
739 #[crate::pyclass]
740 #[pyo3(crate = "crate", extends=BaseClass, subclass)]
741 struct SubClass {
742 val2: usize,
743 }
744
745 #[crate::pyclass]
746 #[pyo3(crate = "crate", extends=SubClass)]
747 struct SubSubClass {
748 val3: usize,
749 }
750
751 #[crate::pymethods]
752 #[pyo3(crate = "crate")]
753 impl SubSubClass {
754 #[new]
755 fn new(py: Python<'_>) -> crate::Py<SubSubClass> {
756 let init = crate::PyClassInitializer::from(BaseClass { val1: 10 })
757 .add_subclass(SubClass { val2: 15 })
758 .add_subclass(SubSubClass { val3: 20 });
759 crate::Py::new(py, init).expect("allocation error")
760 }
761
762 fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) {
763 let val1 = self_.as_super().as_super().val1;
764 let val2 = self_.as_super().val2;
765 (val1, val2, self_.val3)
766 }
767
768 fn double_values(mut self_: PyRefMut<'_, Self>) {
769 self_.as_super().as_super().val1 *= 2;
770 self_.as_super().val2 *= 2;
771 self_.val3 *= 2;
772 }
773 }
774
775 #[test]
776 fn test_pyref_as_super() {
777 Python::with_gil(|py| {
778 let obj = SubSubClass::new(py).into_bound(py);
779 let pyref = obj.borrow();
780 assert_eq!(pyref.as_super().as_super().val1, 10);
781 assert_eq!(pyref.as_super().val2, 15);
782 assert_eq!(pyref.as_ref().val2, 15); // `as_ref` also works
783 assert_eq!(pyref.val3, 20);
784 assert_eq!(SubSubClass::get_values(pyref), (10, 15, 20));
785 });
786 }
787
788 #[test]
789 fn test_pyrefmut_as_super() {
790 Python::with_gil(|py| {
791 let obj = SubSubClass::new(py).into_bound(py);
792 assert_eq!(SubSubClass::get_values(obj.borrow()), (10, 15, 20));
793 {
794 let mut pyrefmut = obj.borrow_mut();
795 assert_eq!(pyrefmut.as_super().as_ref().val1, 10);
796 pyrefmut.as_super().as_super().val1 -= 5;
797 pyrefmut.as_super().val2 -= 3;
798 pyrefmut.as_mut().val2 -= 2; // `as_mut` also works
799 pyrefmut.val3 -= 5;
800 }
801 assert_eq!(SubSubClass::get_values(obj.borrow()), (5, 10, 15));
802 SubSubClass::double_values(obj.borrow_mut());
803 assert_eq!(SubSubClass::get_values(obj.borrow()), (10, 20, 30));
804 });
805 }
806
807 #[test]
808 fn test_pyrefs_in_python() {
809 Python::with_gil(|py| {
810 let obj = SubSubClass::new(py);
811 crate::py_run!(py, obj, "assert obj.get_values() == (10, 15, 20)");
812 crate::py_run!(py, obj, "assert obj.double_values() is None");
813 crate::py_run!(py, obj, "assert obj.get_values() == (20, 30, 40)");
814 });
815 }
816}