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 |
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 | |
196 | use crate::conversion::{AsPyPointer, IntoPyObject}; |
197 | use crate::exceptions::PyRuntimeError; |
198 | use crate::ffi_ptr_ext::FfiPtrExt; |
199 | use crate::internal_tricks::{ptr_from_mut, ptr_from_ref}; |
200 | use crate::pyclass::{boolean_struct::False, PyClass}; |
201 | use crate::types::any::PyAnyMethods; |
202 | #[allow (deprecated)] |
203 | use crate::IntoPy; |
204 | use crate::{ffi, Borrowed, Bound, PyErr, PyObject, Python}; |
205 | use std::convert::Infallible; |
206 | use std::fmt; |
207 | use std::mem::ManuallyDrop; |
208 | use std::ops::{Deref, DerefMut}; |
209 | |
210 | pub(crate) mod impl_; |
211 | use impl_::{PyClassBorrowChecker, PyClassObjectLayout}; |
212 | |
213 | /// A wrapper type for an immutably borrowed value from a [`Bound<'py, T>`]. |
214 | /// |
215 | /// See the [`Bound`] documentation for more information. |
216 | /// |
217 | /// # Examples |
218 | /// |
219 | /// You can use [`PyRef`] as an alternative to a `&self` receiver when |
220 | /// - you need to access the pointer of the [`Bound`], or |
221 | /// - you want to get a super class. |
222 | /// ``` |
223 | /// # use pyo3::prelude::*; |
224 | /// #[pyclass(subclass)] |
225 | /// struct Parent { |
226 | /// basename: &'static str, |
227 | /// } |
228 | /// |
229 | /// #[pyclass(extends=Parent)] |
230 | /// struct Child { |
231 | /// name: &'static str, |
232 | /// } |
233 | /// |
234 | /// #[pymethods] |
235 | /// impl Child { |
236 | /// #[new] |
237 | /// fn new() -> (Self, Parent) { |
238 | /// (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" }) |
239 | /// } |
240 | /// |
241 | /// fn format(slf: PyRef<'_, Self>) -> String { |
242 | /// // We can get *mut ffi::PyObject from PyRef |
243 | /// let refcnt = unsafe { pyo3::ffi::Py_REFCNT(slf.as_ptr()) }; |
244 | /// // We can get &Self::BaseType by as_ref |
245 | /// let basename = slf.as_ref().basename; |
246 | /// format!("{}(base: {}, cnt: {})" , slf.name, basename, refcnt) |
247 | /// } |
248 | /// } |
249 | /// # Python::with_gil(|py| { |
250 | /// # let sub = Py::new(py, Child::new()).unwrap(); |
251 | /// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 4)', sub.format()" ); |
252 | /// # }); |
253 | /// ``` |
254 | /// |
255 | /// See the [module-level documentation](self) for more information. |
256 | #[repr (transparent)] |
257 | pub struct PyRef<'p, T: PyClass> { |
258 | // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to |
259 | // store `Borrowed` here instead, avoiding reference counting overhead. |
260 | inner: Bound<'p, T>, |
261 | } |
262 | |
263 | impl<'p, T: PyClass> PyRef<'p, T> { |
264 | /// Returns a `Python` token that is bound to the lifetime of the `PyRef`. |
265 | pub fn py(&self) -> Python<'p> { |
266 | self.inner.py() |
267 | } |
268 | } |
269 | |
270 | impl<T, U> AsRef<U> for PyRef<'_, T> |
271 | where |
272 | T: PyClass<BaseType = U>, |
273 | U: PyClass, |
274 | { |
275 | fn as_ref(&self) -> &T::BaseType { |
276 | self.as_super() |
277 | } |
278 | } |
279 | |
280 | impl<'py, T: PyClass> PyRef<'py, T> { |
281 | /// Returns the raw FFI pointer represented by self. |
282 | /// |
283 | /// # Safety |
284 | /// |
285 | /// Callers are responsible for ensuring that the pointer does not outlive self. |
286 | /// |
287 | /// The reference is borrowed; callers should not decrease the reference count |
288 | /// when they are finished with the pointer. |
289 | #[inline ] |
290 | pub fn as_ptr(&self) -> *mut ffi::PyObject { |
291 | self.inner.as_ptr() |
292 | } |
293 | |
294 | /// Returns an owned raw FFI pointer represented by self. |
295 | /// |
296 | /// # Safety |
297 | /// |
298 | /// The reference is owned; when finished the caller should either transfer ownership |
299 | /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). |
300 | #[inline ] |
301 | pub fn into_ptr(self) -> *mut ffi::PyObject { |
302 | self.inner.clone().into_ptr() |
303 | } |
304 | |
305 | #[track_caller ] |
306 | pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self { |
307 | Self::try_borrow(obj).expect("Already mutably borrowed" ) |
308 | } |
309 | |
310 | pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result<Self, PyBorrowError> { |
311 | let cell = obj.get_class_object(); |
312 | cell.ensure_threadsafe(); |
313 | cell.borrow_checker() |
314 | .try_borrow() |
315 | .map(|_| Self { inner: obj.clone() }) |
316 | } |
317 | } |
318 | |
319 | impl<'p, T, U> PyRef<'p, T> |
320 | where |
321 | T: PyClass<BaseType = U>, |
322 | U: PyClass, |
323 | { |
324 | /// Gets a `PyRef<T::BaseType>`. |
325 | /// |
326 | /// While `as_ref()` returns a reference of type `&T::BaseType`, this cannot be |
327 | /// used to get the base of `T::BaseType`. |
328 | /// |
329 | /// But with the help of this method, you can get hold of instances of the |
330 | /// super-superclass when needed. |
331 | /// |
332 | /// # Examples |
333 | /// ``` |
334 | /// # use pyo3::prelude::*; |
335 | /// #[pyclass(subclass)] |
336 | /// struct Base1 { |
337 | /// name1: &'static str, |
338 | /// } |
339 | /// |
340 | /// #[pyclass(extends=Base1, subclass)] |
341 | /// struct Base2 { |
342 | /// name2: &'static str, |
343 | /// } |
344 | /// |
345 | /// #[pyclass(extends=Base2)] |
346 | /// struct Sub { |
347 | /// name3: &'static str, |
348 | /// } |
349 | /// |
350 | /// #[pymethods] |
351 | /// impl Sub { |
352 | /// #[new] |
353 | /// fn new() -> PyClassInitializer<Self> { |
354 | /// PyClassInitializer::from(Base1 { name1: "base1" }) |
355 | /// .add_subclass(Base2 { name2: "base2" }) |
356 | /// .add_subclass(Self { name3: "sub" }) |
357 | /// } |
358 | /// fn name(slf: PyRef<'_, Self>) -> String { |
359 | /// let subname = slf.name3; |
360 | /// let super_ = slf.into_super(); |
361 | /// format!("{} {} {}" , super_.as_ref().name1, super_.name2, subname) |
362 | /// } |
363 | /// } |
364 | /// # Python::with_gil(|py| { |
365 | /// # let sub = Py::new(py, Sub::new()).unwrap(); |
366 | /// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'" ) |
367 | /// # }); |
368 | /// ``` |
369 | pub fn into_super(self) -> PyRef<'p, U> { |
370 | let py = self.py(); |
371 | PyRef { |
372 | inner: unsafe { |
373 | ManuallyDrop::new(self) |
374 | .as_ptr() |
375 | .assume_owned_unchecked(py) |
376 | .downcast_into_unchecked() |
377 | }, |
378 | } |
379 | } |
380 | |
381 | /// Borrows a shared reference to `PyRef<T::BaseType>`. |
382 | /// |
383 | /// With the help of this method, you can access attributes and call methods |
384 | /// on the superclass without consuming the `PyRef<T>`. This method can also |
385 | /// be chained to access the super-superclass (and so on). |
386 | /// |
387 | /// # Examples |
388 | /// ``` |
389 | /// # use pyo3::prelude::*; |
390 | /// #[pyclass(subclass)] |
391 | /// struct Base { |
392 | /// base_name: &'static str, |
393 | /// } |
394 | /// #[pymethods] |
395 | /// impl Base { |
396 | /// fn base_name_len(&self) -> usize { |
397 | /// self.base_name.len() |
398 | /// } |
399 | /// } |
400 | /// |
401 | /// #[pyclass(extends=Base)] |
402 | /// struct Sub { |
403 | /// sub_name: &'static str, |
404 | /// } |
405 | /// |
406 | /// #[pymethods] |
407 | /// impl Sub { |
408 | /// #[new] |
409 | /// fn new() -> (Self, Base) { |
410 | /// (Self { sub_name: "sub_name" }, Base { base_name: "base_name" }) |
411 | /// } |
412 | /// fn sub_name_len(&self) -> usize { |
413 | /// self.sub_name.len() |
414 | /// } |
415 | /// fn format_name_lengths(slf: PyRef<'_, Self>) -> String { |
416 | /// format!("{} {}" , slf.as_super().base_name_len(), slf.sub_name_len()) |
417 | /// } |
418 | /// } |
419 | /// # Python::with_gil(|py| { |
420 | /// # let sub = Py::new(py, Sub::new()).unwrap(); |
421 | /// # pyo3::py_run!(py, sub, "assert sub.format_name_lengths() == '9 8'" ) |
422 | /// # }); |
423 | /// ``` |
424 | pub fn as_super(&self) -> &PyRef<'p, U> { |
425 | let ptr = ptr_from_ref::<Bound<'p, T>>(&self.inner) |
426 | // `Bound<T>` has the same layout as `Bound<T::BaseType>` |
427 | .cast::<Bound<'p, T::BaseType>>() |
428 | // `Bound<T::BaseType>` has the same layout as `PyRef<T::BaseType>` |
429 | .cast::<PyRef<'p, T::BaseType>>(); |
430 | unsafe { &*ptr } |
431 | } |
432 | } |
433 | |
434 | impl<T: PyClass> Deref for PyRef<'_, T> { |
435 | type Target = T; |
436 | |
437 | #[inline ] |
438 | fn deref(&self) -> &T { |
439 | unsafe { &*self.inner.get_class_object().get_ptr() } |
440 | } |
441 | } |
442 | |
443 | impl<T: PyClass> Drop for PyRef<'_, T> { |
444 | fn drop(&mut self) { |
445 | self.inner |
446 | .get_class_object() |
447 | .borrow_checker() |
448 | .release_borrow() |
449 | } |
450 | } |
451 | |
452 | #[allow (deprecated)] |
453 | impl<T: PyClass> IntoPy<PyObject> for PyRef<'_, T> { |
454 | fn into_py(self, py: Python<'_>) -> PyObject { |
455 | unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } |
456 | } |
457 | } |
458 | |
459 | #[allow (deprecated)] |
460 | impl<T: PyClass> IntoPy<PyObject> for &'_ PyRef<'_, T> { |
461 | fn into_py(self, py: Python<'_>) -> PyObject { |
462 | unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } |
463 | } |
464 | } |
465 | |
466 | impl<'py, T: PyClass> IntoPyObject<'py> for PyRef<'py, T> { |
467 | type Target = T; |
468 | type Output = Bound<'py, T>; |
469 | type Error = Infallible; |
470 | |
471 | fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> { |
472 | Ok(self.inner.clone()) |
473 | } |
474 | } |
475 | |
476 | impl<'a, 'py, T: PyClass> IntoPyObject<'py> for &'a PyRef<'py, T> { |
477 | type Target = T; |
478 | type Output = Borrowed<'a, 'py, T>; |
479 | type Error = Infallible; |
480 | |
481 | fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> { |
482 | Ok(self.inner.as_borrowed()) |
483 | } |
484 | } |
485 | |
486 | unsafe impl<T: PyClass> AsPyPointer for PyRef<'_, T> { |
487 | fn as_ptr(&self) -> *mut ffi::PyObject { |
488 | self.inner.as_ptr() |
489 | } |
490 | } |
491 | |
492 | impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> { |
493 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
494 | fmt::Debug::fmt(&**self, f) |
495 | } |
496 | } |
497 | |
498 | /// A wrapper type for a mutably borrowed value from a [`Bound<'py, T>`]. |
499 | /// |
500 | /// See the [module-level documentation](self) for more information. |
501 | #[repr (transparent)] |
502 | pub struct PyRefMut<'p, T: PyClass<Frozen = False>> { |
503 | // TODO: once the GIL Ref API is removed, consider adding a lifetime parameter to `PyRef` to |
504 | // store `Borrowed` here instead, avoiding reference counting overhead. |
505 | inner: Bound<'p, T>, |
506 | } |
507 | |
508 | impl<'p, T: PyClass<Frozen = False>> PyRefMut<'p, T> { |
509 | /// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`. |
510 | pub fn py(&self) -> Python<'p> { |
511 | self.inner.py() |
512 | } |
513 | } |
514 | |
515 | impl<T, U> AsRef<U> for PyRefMut<'_, T> |
516 | where |
517 | T: PyClass<BaseType = U, Frozen = False>, |
518 | U: PyClass<Frozen = False>, |
519 | { |
520 | fn as_ref(&self) -> &T::BaseType { |
521 | PyRefMut::downgrade(self).as_super() |
522 | } |
523 | } |
524 | |
525 | impl<T, U> AsMut<U> for PyRefMut<'_, T> |
526 | where |
527 | T: PyClass<BaseType = U, Frozen = False>, |
528 | U: PyClass<Frozen = False>, |
529 | { |
530 | fn as_mut(&mut self) -> &mut T::BaseType { |
531 | self.as_super() |
532 | } |
533 | } |
534 | |
535 | impl<'py, T: PyClass<Frozen = False>> PyRefMut<'py, T> { |
536 | /// Returns the raw FFI pointer represented by self. |
537 | /// |
538 | /// # Safety |
539 | /// |
540 | /// Callers are responsible for ensuring that the pointer does not outlive self. |
541 | /// |
542 | /// The reference is borrowed; callers should not decrease the reference count |
543 | /// when they are finished with the pointer. |
544 | #[inline ] |
545 | pub fn as_ptr(&self) -> *mut ffi::PyObject { |
546 | self.inner.as_ptr() |
547 | } |
548 | |
549 | /// Returns an owned raw FFI pointer represented by self. |
550 | /// |
551 | /// # Safety |
552 | /// |
553 | /// The reference is owned; when finished the caller should either transfer ownership |
554 | /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). |
555 | #[inline ] |
556 | pub fn into_ptr(self) -> *mut ffi::PyObject { |
557 | self.inner.clone().into_ptr() |
558 | } |
559 | |
560 | #[inline ] |
561 | #[track_caller ] |
562 | pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self { |
563 | Self::try_borrow(obj).expect("Already borrowed" ) |
564 | } |
565 | |
566 | pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result<Self, PyBorrowMutError> { |
567 | let cell = obj.get_class_object(); |
568 | cell.ensure_threadsafe(); |
569 | cell.borrow_checker() |
570 | .try_borrow_mut() |
571 | .map(|_| Self { inner: obj.clone() }) |
572 | } |
573 | |
574 | pub(crate) fn downgrade(slf: &Self) -> &PyRef<'py, T> { |
575 | // `PyRefMut<T>` and `PyRef<T>` have the same layout |
576 | unsafe { &*ptr_from_ref(slf).cast() } |
577 | } |
578 | } |
579 | |
580 | impl<'p, T, U> PyRefMut<'p, T> |
581 | where |
582 | T: PyClass<BaseType = U, Frozen = False>, |
583 | U: PyClass<Frozen = False>, |
584 | { |
585 | /// Gets a `PyRef<T::BaseType>`. |
586 | /// |
587 | /// See [`PyRef::into_super`] for more. |
588 | pub fn into_super(self) -> PyRefMut<'p, U> { |
589 | let py = self.py(); |
590 | PyRefMut { |
591 | inner: unsafe { |
592 | ManuallyDrop::new(self) |
593 | .as_ptr() |
594 | .assume_owned_unchecked(py) |
595 | .downcast_into_unchecked() |
596 | }, |
597 | } |
598 | } |
599 | |
600 | /// Borrows a mutable reference to `PyRefMut<T::BaseType>`. |
601 | /// |
602 | /// With the help of this method, you can mutate attributes and call mutating |
603 | /// methods on the superclass without consuming the `PyRefMut<T>`. This method |
604 | /// can also be chained to access the super-superclass (and so on). |
605 | /// |
606 | /// See [`PyRef::as_super`] for more. |
607 | pub fn as_super(&mut self) -> &mut PyRefMut<'p, U> { |
608 | let ptr = ptr_from_mut::<Bound<'p, T>>(&mut self.inner) |
609 | // `Bound<T>` has the same layout as `Bound<T::BaseType>` |
610 | .cast::<Bound<'p, T::BaseType>>() |
611 | // `Bound<T::BaseType>` has the same layout as `PyRefMut<T::BaseType>`, |
612 | // and the mutable borrow on `self` prevents aliasing |
613 | .cast::<PyRefMut<'p, T::BaseType>>(); |
614 | unsafe { &mut *ptr } |
615 | } |
616 | } |
617 | |
618 | impl<T: PyClass<Frozen = False>> Deref for PyRefMut<'_, T> { |
619 | type Target = T; |
620 | |
621 | #[inline ] |
622 | fn deref(&self) -> &T { |
623 | unsafe { &*self.inner.get_class_object().get_ptr() } |
624 | } |
625 | } |
626 | |
627 | impl<T: PyClass<Frozen = False>> DerefMut for PyRefMut<'_, T> { |
628 | #[inline ] |
629 | fn deref_mut(&mut self) -> &mut T { |
630 | unsafe { &mut *self.inner.get_class_object().get_ptr() } |
631 | } |
632 | } |
633 | |
634 | impl<T: PyClass<Frozen = False>> Drop for PyRefMut<'_, T> { |
635 | fn drop(&mut self) { |
636 | self.inner |
637 | .get_class_object() |
638 | .borrow_checker() |
639 | .release_borrow_mut() |
640 | } |
641 | } |
642 | |
643 | #[allow (deprecated)] |
644 | impl<T: PyClass<Frozen = False>> IntoPy<PyObject> for PyRefMut<'_, T> { |
645 | fn into_py(self, py: Python<'_>) -> PyObject { |
646 | unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } |
647 | } |
648 | } |
649 | |
650 | #[allow (deprecated)] |
651 | impl<T: PyClass<Frozen = False>> IntoPy<PyObject> for &'_ PyRefMut<'_, T> { |
652 | fn into_py(self, py: Python<'_>) -> PyObject { |
653 | self.inner.clone().into_py(py) |
654 | } |
655 | } |
656 | |
657 | impl<'py, T: PyClass<Frozen = False>> IntoPyObject<'py> for PyRefMut<'py, T> { |
658 | type Target = T; |
659 | type Output = Bound<'py, T>; |
660 | type Error = Infallible; |
661 | |
662 | fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> { |
663 | Ok(self.inner.clone()) |
664 | } |
665 | } |
666 | |
667 | impl<'a, 'py, T: PyClass<Frozen = False>> IntoPyObject<'py> for &'a PyRefMut<'py, T> { |
668 | type Target = T; |
669 | type Output = Borrowed<'a, 'py, T>; |
670 | type Error = Infallible; |
671 | |
672 | fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> { |
673 | Ok(self.inner.as_borrowed()) |
674 | } |
675 | } |
676 | |
677 | impl<T: PyClass<Frozen = False> + fmt::Debug> fmt::Debug for PyRefMut<'_, T> { |
678 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
679 | fmt::Debug::fmt(self.deref(), f) |
680 | } |
681 | } |
682 | |
683 | /// An error type returned by [`Bound::try_borrow`]. |
684 | /// |
685 | /// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`. |
686 | pub struct PyBorrowError { |
687 | _private: (), |
688 | } |
689 | |
690 | impl fmt::Debug for PyBorrowError { |
691 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
692 | f.debug_struct(name:"PyBorrowError" ).finish() |
693 | } |
694 | } |
695 | |
696 | impl fmt::Display for PyBorrowError { |
697 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
698 | fmt::Display::fmt(self:"Already mutably borrowed" , f) |
699 | } |
700 | } |
701 | |
702 | impl From<PyBorrowError> for PyErr { |
703 | fn from(other: PyBorrowError) -> Self { |
704 | PyRuntimeError::new_err(args:other.to_string()) |
705 | } |
706 | } |
707 | |
708 | /// An error type returned by [`Bound::try_borrow_mut`]. |
709 | /// |
710 | /// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`. |
711 | pub struct PyBorrowMutError { |
712 | _private: (), |
713 | } |
714 | |
715 | impl fmt::Debug for PyBorrowMutError { |
716 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
717 | f.debug_struct(name:"PyBorrowMutError" ).finish() |
718 | } |
719 | } |
720 | |
721 | impl fmt::Display for PyBorrowMutError { |
722 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
723 | fmt::Display::fmt(self:"Already borrowed" , f) |
724 | } |
725 | } |
726 | |
727 | impl From<PyBorrowMutError> for PyErr { |
728 | fn from(other: PyBorrowMutError) -> Self { |
729 | PyRuntimeError::new_err(args:other.to_string()) |
730 | } |
731 | } |
732 | |
733 | #[cfg (test)] |
734 | #[cfg (feature = "macros" )] |
735 | mod tests { |
736 | |
737 | use super::*; |
738 | |
739 | #[crate::pyclass ] |
740 | #[pyo3(crate = "crate" )] |
741 | #[derive (Copy, Clone, PartialEq, Eq, Debug)] |
742 | struct SomeClass(i32); |
743 | |
744 | #[test ] |
745 | fn test_as_ptr() { |
746 | Python::with_gil(|py| { |
747 | let cell = Bound::new(py, SomeClass(0)).unwrap(); |
748 | let ptr = cell.as_ptr(); |
749 | |
750 | assert_eq!(cell.borrow().as_ptr(), ptr); |
751 | assert_eq!(cell.borrow_mut().as_ptr(), ptr); |
752 | }) |
753 | } |
754 | |
755 | #[test ] |
756 | fn test_into_ptr() { |
757 | Python::with_gil(|py| { |
758 | let cell = Bound::new(py, SomeClass(0)).unwrap(); |
759 | let ptr = cell.as_ptr(); |
760 | |
761 | assert_eq!(cell.borrow().into_ptr(), ptr); |
762 | unsafe { ffi::Py_DECREF(ptr) }; |
763 | |
764 | assert_eq!(cell.borrow_mut().into_ptr(), ptr); |
765 | unsafe { ffi::Py_DECREF(ptr) }; |
766 | }) |
767 | } |
768 | |
769 | #[crate::pyclass ] |
770 | #[pyo3(crate = "crate" , subclass)] |
771 | struct BaseClass { |
772 | val1: usize, |
773 | } |
774 | |
775 | #[crate::pyclass ] |
776 | #[pyo3(crate = "crate" , extends=BaseClass, subclass)] |
777 | struct SubClass { |
778 | val2: usize, |
779 | } |
780 | |
781 | #[crate::pyclass ] |
782 | #[pyo3(crate = "crate" , extends=SubClass)] |
783 | struct SubSubClass { |
784 | val3: usize, |
785 | } |
786 | |
787 | #[crate::pymethods ] |
788 | #[pyo3(crate = "crate" )] |
789 | impl SubSubClass { |
790 | #[new] |
791 | fn new(py: Python<'_>) -> crate::Py<SubSubClass> { |
792 | let init = crate::PyClassInitializer::from(BaseClass { val1: 10 }) |
793 | .add_subclass(SubClass { val2: 15 }) |
794 | .add_subclass(SubSubClass { val3: 20 }); |
795 | crate::Py::new(py, init).expect("allocation error" ) |
796 | } |
797 | |
798 | fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) { |
799 | let val1 = self_.as_super().as_super().val1; |
800 | let val2 = self_.as_super().val2; |
801 | (val1, val2, self_.val3) |
802 | } |
803 | |
804 | fn double_values(mut self_: PyRefMut<'_, Self>) { |
805 | self_.as_super().as_super().val1 *= 2; |
806 | self_.as_super().val2 *= 2; |
807 | self_.val3 *= 2; |
808 | } |
809 | } |
810 | |
811 | #[test ] |
812 | fn test_pyref_as_super() { |
813 | Python::with_gil(|py| { |
814 | let obj = SubSubClass::new(py).into_bound(py); |
815 | let pyref = obj.borrow(); |
816 | assert_eq!(pyref.as_super().as_super().val1, 10); |
817 | assert_eq!(pyref.as_super().val2, 15); |
818 | assert_eq!(pyref.as_ref().val2, 15); // `as_ref` also works |
819 | assert_eq!(pyref.val3, 20); |
820 | assert_eq!(SubSubClass::get_values(pyref), (10, 15, 20)); |
821 | }); |
822 | } |
823 | |
824 | #[test ] |
825 | fn test_pyrefmut_as_super() { |
826 | Python::with_gil(|py| { |
827 | let obj = SubSubClass::new(py).into_bound(py); |
828 | assert_eq!(SubSubClass::get_values(obj.borrow()), (10, 15, 20)); |
829 | { |
830 | let mut pyrefmut = obj.borrow_mut(); |
831 | assert_eq!(pyrefmut.as_super().as_ref().val1, 10); |
832 | pyrefmut.as_super().as_super().val1 -= 5; |
833 | pyrefmut.as_super().val2 -= 3; |
834 | pyrefmut.as_mut().val2 -= 2; // `as_mut` also works |
835 | pyrefmut.val3 -= 5; |
836 | } |
837 | assert_eq!(SubSubClass::get_values(obj.borrow()), (5, 10, 15)); |
838 | SubSubClass::double_values(obj.borrow_mut()); |
839 | assert_eq!(SubSubClass::get_values(obj.borrow()), (10, 20, 30)); |
840 | }); |
841 | } |
842 | |
843 | #[test ] |
844 | fn test_pyrefs_in_python() { |
845 | Python::with_gil(|py| { |
846 | let obj = SubSubClass::new(py); |
847 | crate::py_run!(py, obj, "assert obj.get_values() == (10, 15, 20)" ); |
848 | crate::py_run!(py, obj, "assert obj.double_values() is None" ); |
849 | crate::py_run!(py, obj, "assert obj.get_values() == (20, 30, 40)" ); |
850 | }); |
851 | } |
852 | } |
853 | |