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 threadsafe 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 |
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 | //! let _cell = py |
66 | //! .from_borrowed_ptr::<_pyo3::PyAny>(_slf) |
67 | //! .downcast::<_pyo3::PyCell<Number>>()?; |
68 | //! let mut _ref = _cell.try_borrow_mut()?; |
69 | //! let _slf: &mut Number = &mut *_ref; |
70 | //! _pyo3::callback::convert(py, Number::increment(_slf)) |
71 | //! }) |
72 | //! } |
73 | //! ``` |
74 | //! |
75 | //! # When to use PyCell |
76 | //! ## Using pyclasses from Rust |
77 | //! |
78 | //! However, we *do* need [`PyCell`] if we want to call its methods from Rust: |
79 | //! ```rust |
80 | //! # use pyo3::prelude::*; |
81 | //! # |
82 | //! # #[pyclass] |
83 | //! # struct Number { |
84 | //! # inner: u32, |
85 | //! # } |
86 | //! # |
87 | //! # #[pymethods] |
88 | //! # impl Number { |
89 | //! # fn increment(&mut self) { |
90 | //! # self.inner += 1; |
91 | //! # } |
92 | //! # } |
93 | //! # fn main() -> PyResult<()> { |
94 | //! Python::with_gil(|py| { |
95 | //! let n = Py::new(py, Number { inner: 0 })?; |
96 | //! |
97 | //! // We borrow the guard and then dereference |
98 | //! // it to get a mutable reference to Number |
99 | //! let mut guard: PyRefMut<'_, Number> = n.as_ref(py).borrow_mut(); |
100 | //! let n_mutable: &mut Number = &mut *guard; |
101 | //! |
102 | //! n_mutable.increment(); |
103 | //! |
104 | //! // To avoid panics we must dispose of the |
105 | //! // `PyRefMut` before borrowing again. |
106 | //! drop(guard); |
107 | //! |
108 | //! let n_immutable: &Number = &n.as_ref(py).borrow(); |
109 | //! assert_eq!(n_immutable.inner, 1); |
110 | //! |
111 | //! Ok(()) |
112 | //! }) |
113 | //! # } |
114 | //! ``` |
115 | //! ## Dealing with possibly overlapping mutable references |
116 | //! |
117 | //! It is also necessary to use [`PyCell`] if you can receive mutable arguments that may overlap. |
118 | //! Suppose the following function that swaps the values of two `Number`s: |
119 | //! ``` |
120 | //! # use pyo3::prelude::*; |
121 | //! # #[pyclass] |
122 | //! # pub struct Number { |
123 | //! # inner: u32, |
124 | //! # } |
125 | //! #[pyfunction] |
126 | //! fn swap_numbers(a: &mut Number, b: &mut Number) { |
127 | //! std::mem::swap(&mut a.inner, &mut b.inner); |
128 | //! } |
129 | //! # fn main() { |
130 | //! # Python::with_gil(|py| { |
131 | //! # let n = Py::new(py, Number{inner: 35}).unwrap(); |
132 | //! # let n2 = n.clone_ref(py); |
133 | //! # assert!(n.is(&n2)); |
134 | //! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap(); |
135 | //! # fun.call1((n, n2)).expect_err("Managed to create overlapping mutable references. Note: this is undefined behaviour." ); |
136 | //! # }); |
137 | //! # } |
138 | //! ``` |
139 | //! When users pass in the same `Number` as both arguments, one of the mutable borrows will |
140 | //! fail and raise a `RuntimeError`: |
141 | //! ```text |
142 | //! >>> a = Number() |
143 | //! >>> swap_numbers(a, a) |
144 | //! Traceback (most recent call last): |
145 | //! File "<stdin>", line 1, in <module> |
146 | //! RuntimeError: Already borrowed |
147 | //! ``` |
148 | //! |
149 | //! It is better to write that function like this: |
150 | //! ```rust |
151 | //! # use pyo3::prelude::*; |
152 | //! # #[pyclass] |
153 | //! # pub struct Number { |
154 | //! # inner: u32, |
155 | //! # } |
156 | //! #[pyfunction] |
157 | //! fn swap_numbers(a: &PyCell<Number>, b: &PyCell<Number>) { |
158 | //! // Check that the pointers are unequal |
159 | //! if !a.is(b) { |
160 | //! std::mem::swap(&mut a.borrow_mut().inner, &mut b.borrow_mut().inner); |
161 | //! } else { |
162 | //! // Do nothing - they are the same object, so don't need swapping. |
163 | //! } |
164 | //! } |
165 | //! # fn main() { |
166 | //! # // With duplicate numbers |
167 | //! # Python::with_gil(|py| { |
168 | //! # let n = Py::new(py, Number{inner: 35}).unwrap(); |
169 | //! # let n2 = n.clone_ref(py); |
170 | //! # assert!(n.is(&n2)); |
171 | //! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap(); |
172 | //! # fun.call1((n, n2)).unwrap(); |
173 | //! # }); |
174 | //! # |
175 | //! # // With two different numbers |
176 | //! # Python::with_gil(|py| { |
177 | //! # let n = Py::new(py, Number{inner: 35}).unwrap(); |
178 | //! # let n2 = Py::new(py, Number{inner: 42}).unwrap(); |
179 | //! # assert!(!n.is(&n2)); |
180 | //! # let fun = pyo3::wrap_pyfunction!(swap_numbers, py).unwrap(); |
181 | //! # fun.call1((&n, &n2)).unwrap(); |
182 | //! # let n: u32 = n.borrow(py).inner; |
183 | //! # let n2: u32 = n2.borrow(py).inner; |
184 | //! # assert_eq!(n, 42); |
185 | //! # assert_eq!(n2, 35); |
186 | //! # }); |
187 | //! # } |
188 | //! ``` |
189 | //! See the [guide] for more information. |
190 | //! |
191 | //! [guide]: https://pyo3.rs/latest/class.html#pycell-and-interior-mutability "PyCell and interior mutability" |
192 | //! [Interior Mutability]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html "RefCell<T> and the Interior Mutability Pattern - The Rust Programming Language" |
193 | |
194 | use crate::exceptions::PyRuntimeError; |
195 | use crate::impl_::pyclass::{ |
196 | PyClassBaseType, PyClassDict, PyClassImpl, PyClassThreadChecker, PyClassWeakRef, |
197 | }; |
198 | use crate::pyclass::{ |
199 | boolean_struct::{False, True}, |
200 | PyClass, |
201 | }; |
202 | use crate::pyclass_init::PyClassInitializer; |
203 | use crate::type_object::{PyLayout, PySizedLayout}; |
204 | use crate::types::PyAny; |
205 | use crate::{ |
206 | conversion::{AsPyPointer, FromPyPointer, ToPyObject}, |
207 | type_object::get_tp_free, |
208 | PyTypeInfo, |
209 | }; |
210 | use crate::{ffi, IntoPy, PyErr, PyNativeType, PyObject, PyResult, Python}; |
211 | use std::cell::UnsafeCell; |
212 | use std::fmt; |
213 | use std::mem::ManuallyDrop; |
214 | use std::ops::{Deref, DerefMut}; |
215 | |
216 | pub(crate) mod impl_; |
217 | use impl_::{GetBorrowChecker, PyClassBorrowChecker, PyClassMutability}; |
218 | |
219 | /// Base layout of PyCell. |
220 | #[doc (hidden)] |
221 | #[repr (C)] |
222 | pub struct PyCellBase<T> { |
223 | ob_base: T, |
224 | } |
225 | |
226 | unsafe impl<T, U> PyLayout<T> for PyCellBase<U> where U: PySizedLayout<T> {} |
227 | |
228 | /// A container type for (mutably) accessing [`PyClass`] values |
229 | /// |
230 | /// `PyCell` autodereferences to [`PyAny`], so you can call `PyAny`'s methods on a `PyCell<T>`. |
231 | /// |
232 | /// # Examples |
233 | /// |
234 | /// This example demonstrates getting a mutable reference of the contained `PyClass`. |
235 | /// ```rust |
236 | /// use pyo3::prelude::*; |
237 | /// |
238 | /// #[pyclass] |
239 | /// struct Number { |
240 | /// inner: u32, |
241 | /// } |
242 | /// |
243 | /// #[pymethods] |
244 | /// impl Number { |
245 | /// fn increment(&mut self) { |
246 | /// self.inner += 1; |
247 | /// } |
248 | /// } |
249 | /// |
250 | /// # fn main() -> PyResult<()> { |
251 | /// Python::with_gil(|py| { |
252 | /// let n = PyCell::new(py, Number { inner: 0 })?; |
253 | /// |
254 | /// let n_mutable: &mut Number = &mut n.borrow_mut(); |
255 | /// n_mutable.increment(); |
256 | /// |
257 | /// Ok(()) |
258 | /// }) |
259 | /// # } |
260 | /// ``` |
261 | /// For more information on how, when and why (not) to use `PyCell` please see the |
262 | /// [module-level documentation](self). |
263 | #[repr (C)] |
264 | pub struct PyCell<T: PyClassImpl> { |
265 | ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase, |
266 | contents: PyCellContents<T>, |
267 | } |
268 | |
269 | #[repr (C)] |
270 | pub(crate) struct PyCellContents<T: PyClassImpl> { |
271 | pub(crate) value: ManuallyDrop<UnsafeCell<T>>, |
272 | pub(crate) borrow_checker: <T::PyClassMutability as PyClassMutability>::Storage, |
273 | pub(crate) thread_checker: T::ThreadChecker, |
274 | pub(crate) dict: T::Dict, |
275 | pub(crate) weakref: T::WeakRef, |
276 | } |
277 | |
278 | unsafe impl<T: PyClass> PyNativeType for PyCell<T> {} |
279 | |
280 | impl<T: PyClass> PyCell<T> { |
281 | /// Makes a new `PyCell` on the Python heap and return the reference to it. |
282 | /// |
283 | /// In cases where the value in the cell does not need to be accessed immediately after |
284 | /// creation, consider [`Py::new`](crate::Py::new) as a more efficient alternative. |
285 | pub fn new(py: Python<'_>, value: impl Into<PyClassInitializer<T>>) -> PyResult<&Self> { |
286 | unsafe { |
287 | let initializer = value.into(); |
288 | let self_ = initializer.create_cell(py)?; |
289 | FromPyPointer::from_owned_ptr_or_err(py, self_ as _) |
290 | } |
291 | } |
292 | |
293 | /// Immutably borrows the value `T`. This borrow lasts as long as the returned `PyRef` exists. |
294 | /// |
295 | /// For frozen classes, the simpler [`get`][Self::get] is available. |
296 | /// |
297 | /// # Panics |
298 | /// |
299 | /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use |
300 | /// [`try_borrow`](#method.try_borrow). |
301 | pub fn borrow(&self) -> PyRef<'_, T> { |
302 | self.try_borrow().expect("Already mutably borrowed" ) |
303 | } |
304 | |
305 | /// Mutably borrows the value `T`. This borrow lasts as long as the returned `PyRefMut` exists. |
306 | /// |
307 | /// # Panics |
308 | /// |
309 | /// Panics if the value is currently borrowed. For a non-panicking variant, use |
310 | /// [`try_borrow_mut`](#method.try_borrow_mut). |
311 | pub fn borrow_mut(&self) -> PyRefMut<'_, T> |
312 | where |
313 | T: PyClass<Frozen = False>, |
314 | { |
315 | self.try_borrow_mut().expect("Already borrowed" ) |
316 | } |
317 | |
318 | /// Immutably borrows the value `T`, returning an error if the value is currently |
319 | /// mutably borrowed. This borrow lasts as long as the returned `PyRef` exists. |
320 | /// |
321 | /// This is the non-panicking variant of [`borrow`](#method.borrow). |
322 | /// |
323 | /// For frozen classes, the simpler [`get`][Self::get] is available. |
324 | /// |
325 | /// # Examples |
326 | /// |
327 | /// ``` |
328 | /// # use pyo3::prelude::*; |
329 | /// #[pyclass] |
330 | /// struct Class {} |
331 | /// |
332 | /// Python::with_gil(|py| { |
333 | /// let c = PyCell::new(py, Class {}).unwrap(); |
334 | /// { |
335 | /// let m = c.borrow_mut(); |
336 | /// assert!(c.try_borrow().is_err()); |
337 | /// } |
338 | /// |
339 | /// { |
340 | /// let m = c.borrow(); |
341 | /// assert!(c.try_borrow().is_ok()); |
342 | /// } |
343 | /// }); |
344 | /// ``` |
345 | pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> { |
346 | self.ensure_threadsafe(); |
347 | self.borrow_checker() |
348 | .try_borrow() |
349 | .map(|_| PyRef { inner: self }) |
350 | } |
351 | |
352 | /// Mutably borrows the value `T`, returning an error if the value is currently borrowed. |
353 | /// This borrow lasts as long as the returned `PyRefMut` exists. |
354 | /// |
355 | /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). |
356 | /// |
357 | /// # Examples |
358 | /// |
359 | /// ``` |
360 | /// # use pyo3::prelude::*; |
361 | /// #[pyclass] |
362 | /// struct Class {} |
363 | /// Python::with_gil(|py| { |
364 | /// let c = PyCell::new(py, Class {}).unwrap(); |
365 | /// { |
366 | /// let m = c.borrow(); |
367 | /// assert!(c.try_borrow_mut().is_err()); |
368 | /// } |
369 | /// |
370 | /// assert!(c.try_borrow_mut().is_ok()); |
371 | /// }); |
372 | /// ``` |
373 | pub fn try_borrow_mut(&self) -> Result<PyRefMut<'_, T>, PyBorrowMutError> |
374 | where |
375 | T: PyClass<Frozen = False>, |
376 | { |
377 | self.ensure_threadsafe(); |
378 | self.borrow_checker() |
379 | .try_borrow_mut() |
380 | .map(|_| PyRefMut { inner: self }) |
381 | } |
382 | |
383 | /// Immutably borrows the value `T`, returning an error if the value is |
384 | /// currently mutably borrowed. |
385 | /// |
386 | /// # Safety |
387 | /// |
388 | /// This method is unsafe because it does not return a `PyRef`, |
389 | /// thus leaving the borrow flag untouched. Mutably borrowing the `PyCell` |
390 | /// while the reference returned by this method is alive is undefined behaviour. |
391 | /// |
392 | /// # Examples |
393 | /// |
394 | /// ``` |
395 | /// # use pyo3::prelude::*; |
396 | /// #[pyclass] |
397 | /// struct Class {} |
398 | /// Python::with_gil(|py| { |
399 | /// let c = PyCell::new(py, Class {}).unwrap(); |
400 | /// |
401 | /// { |
402 | /// let m = c.borrow_mut(); |
403 | /// assert!(unsafe { c.try_borrow_unguarded() }.is_err()); |
404 | /// } |
405 | /// |
406 | /// { |
407 | /// let m = c.borrow(); |
408 | /// assert!(unsafe { c.try_borrow_unguarded() }.is_ok()); |
409 | /// } |
410 | /// }); |
411 | /// ``` |
412 | pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { |
413 | self.ensure_threadsafe(); |
414 | self.borrow_checker() |
415 | .try_borrow_unguarded() |
416 | .map(|_: ()| &*self.contents.value.get()) |
417 | } |
418 | |
419 | /// Provide an immutable borrow of the value `T` without acquiring the GIL. |
420 | /// |
421 | /// This is available if the class is [`frozen`][macro@crate::pyclass] and [`Sync`]. |
422 | /// |
423 | /// While the GIL is usually required to get access to `&PyCell<T>`, |
424 | /// compared to [`borrow`][Self::borrow] or [`try_borrow`][Self::try_borrow] |
425 | /// this avoids any thread or borrow checking overhead at runtime. |
426 | /// |
427 | /// # Examples |
428 | /// |
429 | /// ``` |
430 | /// use std::sync::atomic::{AtomicUsize, Ordering}; |
431 | /// # use pyo3::prelude::*; |
432 | /// |
433 | /// #[pyclass(frozen)] |
434 | /// struct FrozenCounter { |
435 | /// value: AtomicUsize, |
436 | /// } |
437 | /// |
438 | /// Python::with_gil(|py| { |
439 | /// let counter = FrozenCounter { value: AtomicUsize::new(0) }; |
440 | /// |
441 | /// let cell = PyCell::new(py, counter).unwrap(); |
442 | /// |
443 | /// cell.get().value.fetch_add(1, Ordering::Relaxed); |
444 | /// }); |
445 | /// ``` |
446 | pub fn get(&self) -> &T |
447 | where |
448 | T: PyClass<Frozen = True> + Sync, |
449 | { |
450 | // SAFETY: The class itself is frozen and `Sync` and we do not access anything but `self.contents.value`. |
451 | unsafe { &*self.get_ptr() } |
452 | } |
453 | |
454 | /// Replaces the wrapped value with a new one, returning the old value. |
455 | /// |
456 | /// # Panics |
457 | /// |
458 | /// Panics if the value is currently borrowed. |
459 | #[inline ] |
460 | pub fn replace(&self, t: T) -> T |
461 | where |
462 | T: PyClass<Frozen = False>, |
463 | { |
464 | std::mem::replace(&mut *self.borrow_mut(), t) |
465 | } |
466 | |
467 | /// Replaces the wrapped value with a new one computed from `f`, returning the old value. |
468 | /// |
469 | /// # Panics |
470 | /// |
471 | /// Panics if the value is currently borrowed. |
472 | pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T |
473 | where |
474 | T: PyClass<Frozen = False>, |
475 | { |
476 | let mut_borrow = &mut *self.borrow_mut(); |
477 | let replacement = f(mut_borrow); |
478 | std::mem::replace(mut_borrow, replacement) |
479 | } |
480 | |
481 | /// Swaps the wrapped value of `self` with the wrapped value of `other`. |
482 | /// |
483 | /// # Panics |
484 | /// |
485 | /// Panics if the value in either `PyCell` is currently borrowed. |
486 | #[inline ] |
487 | pub fn swap(&self, other: &Self) |
488 | where |
489 | T: PyClass<Frozen = False>, |
490 | { |
491 | std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut()) |
492 | } |
493 | |
494 | pub(crate) fn get_ptr(&self) -> *mut T { |
495 | self.contents.value.get() |
496 | } |
497 | |
498 | /// Gets the offset of the dictionary from the start of the struct in bytes. |
499 | pub(crate) fn dict_offset() -> ffi::Py_ssize_t { |
500 | use memoffset::offset_of; |
501 | |
502 | let offset = offset_of!(PyCell<T>, contents) + offset_of!(PyCellContents<T>, dict); |
503 | |
504 | // Py_ssize_t may not be equal to isize on all platforms |
505 | #[allow (clippy::useless_conversion)] |
506 | offset.try_into().expect("offset should fit in Py_ssize_t" ) |
507 | } |
508 | |
509 | /// Gets the offset of the weakref list from the start of the struct in bytes. |
510 | pub(crate) fn weaklist_offset() -> ffi::Py_ssize_t { |
511 | use memoffset::offset_of; |
512 | |
513 | let offset = offset_of!(PyCell<T>, contents) + offset_of!(PyCellContents<T>, weakref); |
514 | |
515 | // Py_ssize_t may not be equal to isize on all platforms |
516 | #[allow (clippy::useless_conversion)] |
517 | offset.try_into().expect("offset should fit in Py_ssize_t" ) |
518 | } |
519 | } |
520 | |
521 | impl<T: PyClassImpl> PyCell<T> { |
522 | fn borrow_checker(&self) -> &<T::PyClassMutability as PyClassMutability>::Checker { |
523 | T::PyClassMutability::borrow_checker(self) |
524 | } |
525 | } |
526 | |
527 | unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {} |
528 | impl<T: PyClass> PySizedLayout<T> for PyCell<T> {} |
529 | |
530 | unsafe impl<T: PyClass> AsPyPointer for PyCell<T> { |
531 | fn as_ptr(&self) -> *mut ffi::PyObject { |
532 | (self as *const _) as *mut _ |
533 | } |
534 | } |
535 | |
536 | impl<T: PyClass> ToPyObject for &PyCell<T> { |
537 | fn to_object(&self, py: Python<'_>) -> PyObject { |
538 | unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } |
539 | } |
540 | } |
541 | |
542 | impl<T: PyClass> AsRef<PyAny> for PyCell<T> { |
543 | fn as_ref(&self) -> &PyAny { |
544 | unsafe { self.py().from_borrowed_ptr(self.as_ptr()) } |
545 | } |
546 | } |
547 | |
548 | impl<T: PyClass> Deref for PyCell<T> { |
549 | type Target = PyAny; |
550 | |
551 | fn deref(&self) -> &PyAny { |
552 | unsafe { self.py().from_borrowed_ptr(self.as_ptr()) } |
553 | } |
554 | } |
555 | |
556 | impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> { |
557 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
558 | match self.try_borrow() { |
559 | Ok(borrow: PyRef<'_, T>) => f.debug_struct("RefCell" ).field(name:"value" , &borrow).finish(), |
560 | Err(_) => { |
561 | struct BorrowedPlaceholder; |
562 | impl fmt::Debug for BorrowedPlaceholder { |
563 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
564 | f.write_str(data:"<borrowed>" ) |
565 | } |
566 | } |
567 | f&mut DebugStruct<'_, '_>.debug_struct("RefCell" ) |
568 | .field(name:"value" , &BorrowedPlaceholder) |
569 | .finish() |
570 | } |
571 | } |
572 | } |
573 | } |
574 | |
575 | /// A wrapper type for an immutably borrowed value from a [`PyCell`]`<T>`. |
576 | /// |
577 | /// See the [`PyCell`] documentation for more information. |
578 | /// |
579 | /// # Examples |
580 | /// |
581 | /// You can use `PyRef` as an alternative to a `&self` receiver when |
582 | /// - you need to access the pointer of the `PyCell`, or |
583 | /// - you want to get a super class. |
584 | /// ``` |
585 | /// # use pyo3::prelude::*; |
586 | /// #[pyclass(subclass)] |
587 | /// struct Parent { |
588 | /// basename: &'static str, |
589 | /// } |
590 | /// |
591 | /// #[pyclass(extends=Parent)] |
592 | /// struct Child { |
593 | /// name: &'static str, |
594 | /// } |
595 | /// |
596 | /// #[pymethods] |
597 | /// impl Child { |
598 | /// #[new] |
599 | /// fn new() -> (Self, Parent) { |
600 | /// (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" }) |
601 | /// } |
602 | /// |
603 | /// fn format(slf: PyRef<'_, Self>) -> String { |
604 | /// // We can get *mut ffi::PyObject from PyRef |
605 | /// let refcnt = unsafe { pyo3::ffi::Py_REFCNT(slf.as_ptr()) }; |
606 | /// // We can get &Self::BaseType by as_ref |
607 | /// let basename = slf.as_ref().basename; |
608 | /// format!("{}(base: {}, cnt: {})" , slf.name, basename, refcnt) |
609 | /// } |
610 | /// } |
611 | /// # Python::with_gil(|py| { |
612 | /// # let sub = PyCell::new(py, Child::new()).unwrap(); |
613 | /// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 3)'" ); |
614 | /// # }); |
615 | /// ``` |
616 | /// |
617 | /// See the [module-level documentation](self) for more information. |
618 | pub struct PyRef<'p, T: PyClass> { |
619 | inner: &'p PyCell<T>, |
620 | } |
621 | |
622 | impl<'p, T: PyClass> PyRef<'p, T> { |
623 | /// Returns a `Python` token that is bound to the lifetime of the `PyRef`. |
624 | pub fn py(&self) -> Python<'p> { |
625 | self.inner.py() |
626 | } |
627 | } |
628 | |
629 | impl<'p, T, U> AsRef<U> for PyRef<'p, T> |
630 | where |
631 | T: PyClass<BaseType = U>, |
632 | U: PyClass, |
633 | { |
634 | fn as_ref(&self) -> &T::BaseType { |
635 | unsafe { &*self.inner.ob_base.get_ptr() } |
636 | } |
637 | } |
638 | |
639 | impl<'p, T: PyClass> PyRef<'p, T> { |
640 | /// Returns the raw FFI pointer represented by self. |
641 | /// |
642 | /// # Safety |
643 | /// |
644 | /// Callers are responsible for ensuring that the pointer does not outlive self. |
645 | /// |
646 | /// The reference is borrowed; callers should not decrease the reference count |
647 | /// when they are finished with the pointer. |
648 | #[inline ] |
649 | pub fn as_ptr(&self) -> *mut ffi::PyObject { |
650 | self.inner.as_ptr() |
651 | } |
652 | |
653 | /// Returns an owned raw FFI pointer represented by self. |
654 | /// |
655 | /// # Safety |
656 | /// |
657 | /// The reference is owned; when finished the caller should either transfer ownership |
658 | /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). |
659 | #[inline ] |
660 | pub fn into_ptr(self) -> *mut ffi::PyObject { |
661 | self.inner.into_ptr() |
662 | } |
663 | } |
664 | |
665 | impl<'p, T, U> PyRef<'p, T> |
666 | where |
667 | T: PyClass<BaseType = U>, |
668 | U: PyClass, |
669 | { |
670 | /// Gets a `PyRef<T::BaseType>`. |
671 | /// |
672 | /// While `as_ref()` returns a reference of type `&T::BaseType`, this cannot be |
673 | /// used to get the base of `T::BaseType`. |
674 | /// |
675 | /// But with the help of this method, you can get hold of instances of the |
676 | /// super-superclass when needed. |
677 | /// |
678 | /// # Examples |
679 | /// ``` |
680 | /// # use pyo3::prelude::*; |
681 | /// #[pyclass(subclass)] |
682 | /// struct Base1 { |
683 | /// name1: &'static str, |
684 | /// } |
685 | /// |
686 | /// #[pyclass(extends=Base1, subclass)] |
687 | /// struct Base2 { |
688 | /// name2: &'static str, |
689 | /// } |
690 | /// |
691 | /// #[pyclass(extends=Base2)] |
692 | /// struct Sub { |
693 | /// name3: &'static str, |
694 | /// } |
695 | /// |
696 | /// #[pymethods] |
697 | /// impl Sub { |
698 | /// #[new] |
699 | /// fn new() -> PyClassInitializer<Self> { |
700 | /// PyClassInitializer::from(Base1 { name1: "base1" }) |
701 | /// .add_subclass(Base2 { name2: "base2" }) |
702 | /// .add_subclass(Self { name3: "sub" }) |
703 | /// } |
704 | /// fn name(slf: PyRef<'_, Self>) -> String { |
705 | /// let subname = slf.name3; |
706 | /// let super_ = slf.into_super(); |
707 | /// format!("{} {} {}" , super_.as_ref().name1, super_.name2, subname) |
708 | /// } |
709 | /// } |
710 | /// # Python::with_gil(|py| { |
711 | /// # let sub = PyCell::new(py, Sub::new()).unwrap(); |
712 | /// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'" ) |
713 | /// # }); |
714 | /// ``` |
715 | pub fn into_super(self) -> PyRef<'p, U> { |
716 | let PyRef { inner } = self; |
717 | std::mem::forget(self); |
718 | PyRef { |
719 | inner: &inner.ob_base, |
720 | } |
721 | } |
722 | } |
723 | |
724 | impl<'p, T: PyClass> Deref for PyRef<'p, T> { |
725 | type Target = T; |
726 | |
727 | #[inline ] |
728 | fn deref(&self) -> &T { |
729 | unsafe { &*self.inner.get_ptr() } |
730 | } |
731 | } |
732 | |
733 | impl<'p, T: PyClass> Drop for PyRef<'p, T> { |
734 | fn drop(&mut self) { |
735 | self.inner.borrow_checker().release_borrow() |
736 | } |
737 | } |
738 | |
739 | impl<T: PyClass> IntoPy<PyObject> for PyRef<'_, T> { |
740 | fn into_py(self, py: Python<'_>) -> PyObject { |
741 | unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } |
742 | } |
743 | } |
744 | |
745 | impl<T: PyClass> IntoPy<PyObject> for &'_ PyRef<'_, T> { |
746 | fn into_py(self, py: Python<'_>) -> PyObject { |
747 | self.inner.into_py(py) |
748 | } |
749 | } |
750 | |
751 | impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRef<'a, T> { |
752 | type Error = PyBorrowError; |
753 | fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> { |
754 | cell.try_borrow() |
755 | } |
756 | } |
757 | |
758 | unsafe impl<'a, T: PyClass> AsPyPointer for PyRef<'a, T> { |
759 | fn as_ptr(&self) -> *mut ffi::PyObject { |
760 | self.inner.as_ptr() |
761 | } |
762 | } |
763 | |
764 | impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> { |
765 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
766 | fmt::Debug::fmt(&**self, f) |
767 | } |
768 | } |
769 | |
770 | /// A wrapper type for a mutably borrowed value from a[`PyCell`]`<T>`. |
771 | /// |
772 | /// See the [module-level documentation](self) for more information. |
773 | pub struct PyRefMut<'p, T: PyClass<Frozen = False>> { |
774 | inner: &'p PyCell<T>, |
775 | } |
776 | |
777 | impl<'p, T: PyClass<Frozen = False>> PyRefMut<'p, T> { |
778 | /// Returns a `Python` token that is bound to the lifetime of the `PyRefMut`. |
779 | pub fn py(&self) -> Python<'p> { |
780 | self.inner.py() |
781 | } |
782 | } |
783 | |
784 | impl<'p, T, U> AsRef<U> for PyRefMut<'p, T> |
785 | where |
786 | T: PyClass<BaseType = U, Frozen = False>, |
787 | U: PyClass<Frozen = False>, |
788 | { |
789 | fn as_ref(&self) -> &T::BaseType { |
790 | unsafe { &*self.inner.ob_base.get_ptr() } |
791 | } |
792 | } |
793 | |
794 | impl<'p, T, U> AsMut<U> for PyRefMut<'p, T> |
795 | where |
796 | T: PyClass<BaseType = U, Frozen = False>, |
797 | U: PyClass<Frozen = False>, |
798 | { |
799 | fn as_mut(&mut self) -> &mut T::BaseType { |
800 | unsafe { &mut *self.inner.ob_base.get_ptr() } |
801 | } |
802 | } |
803 | |
804 | impl<'p, T: PyClass<Frozen = False>> PyRefMut<'p, T> { |
805 | /// Returns the raw FFI pointer represented by self. |
806 | /// |
807 | /// # Safety |
808 | /// |
809 | /// Callers are responsible for ensuring that the pointer does not outlive self. |
810 | /// |
811 | /// The reference is borrowed; callers should not decrease the reference count |
812 | /// when they are finished with the pointer. |
813 | #[inline ] |
814 | pub fn as_ptr(&self) -> *mut ffi::PyObject { |
815 | self.inner.as_ptr() |
816 | } |
817 | |
818 | /// Returns an owned raw FFI pointer represented by self. |
819 | /// |
820 | /// # Safety |
821 | /// |
822 | /// The reference is owned; when finished the caller should either transfer ownership |
823 | /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). |
824 | #[inline ] |
825 | pub fn into_ptr(self) -> *mut ffi::PyObject { |
826 | self.inner.into_ptr() |
827 | } |
828 | } |
829 | |
830 | impl<'p, T, U> PyRefMut<'p, T> |
831 | where |
832 | T: PyClass<BaseType = U, Frozen = False>, |
833 | U: PyClass<Frozen = False>, |
834 | { |
835 | /// Gets a `PyRef<T::BaseType>`. |
836 | /// |
837 | /// See [`PyRef::into_super`] for more. |
838 | pub fn into_super(self) -> PyRefMut<'p, U> { |
839 | let PyRefMut { inner: &PyCell } = self; |
840 | std::mem::forget(self); |
841 | PyRefMut { |
842 | inner: &inner.ob_base, |
843 | } |
844 | } |
845 | } |
846 | |
847 | impl<'p, T: PyClass<Frozen = False>> Deref for PyRefMut<'p, T> { |
848 | type Target = T; |
849 | |
850 | #[inline ] |
851 | fn deref(&self) -> &T { |
852 | unsafe { &*self.inner.get_ptr() } |
853 | } |
854 | } |
855 | |
856 | impl<'p, T: PyClass<Frozen = False>> DerefMut for PyRefMut<'p, T> { |
857 | #[inline ] |
858 | fn deref_mut(&mut self) -> &mut T { |
859 | unsafe { &mut *self.inner.get_ptr() } |
860 | } |
861 | } |
862 | |
863 | impl<'p, T: PyClass<Frozen = False>> Drop for PyRefMut<'p, T> { |
864 | fn drop(&mut self) { |
865 | self.inner.borrow_checker().release_borrow_mut() |
866 | } |
867 | } |
868 | |
869 | impl<T: PyClass<Frozen = False>> IntoPy<PyObject> for PyRefMut<'_, T> { |
870 | fn into_py(self, py: Python<'_>) -> PyObject { |
871 | unsafe { PyObject::from_borrowed_ptr(py, self.inner.as_ptr()) } |
872 | } |
873 | } |
874 | |
875 | impl<T: PyClass<Frozen = False>> IntoPy<PyObject> for &'_ PyRefMut<'_, T> { |
876 | fn into_py(self, py: Python<'_>) -> PyObject { |
877 | self.inner.into_py(py) |
878 | } |
879 | } |
880 | |
881 | unsafe impl<'a, T: PyClass<Frozen = False>> AsPyPointer for PyRefMut<'a, T> { |
882 | fn as_ptr(&self) -> *mut ffi::PyObject { |
883 | self.inner.as_ptr() |
884 | } |
885 | } |
886 | |
887 | impl<'a, T: PyClass<Frozen = False>> std::convert::TryFrom<&'a PyCell<T>> |
888 | for crate::PyRefMut<'a, T> |
889 | { |
890 | type Error = PyBorrowMutError; |
891 | fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> { |
892 | cell.try_borrow_mut() |
893 | } |
894 | } |
895 | |
896 | impl<T: PyClass<Frozen = False> + fmt::Debug> fmt::Debug for PyRefMut<'_, T> { |
897 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
898 | fmt::Debug::fmt(self.deref(), f) |
899 | } |
900 | } |
901 | |
902 | /// An error type returned by [`PyCell::try_borrow`]. |
903 | /// |
904 | /// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`. |
905 | pub struct PyBorrowError { |
906 | _private: (), |
907 | } |
908 | |
909 | impl fmt::Debug for PyBorrowError { |
910 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
911 | f.debug_struct(name:"PyBorrowError" ).finish() |
912 | } |
913 | } |
914 | |
915 | impl fmt::Display for PyBorrowError { |
916 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
917 | fmt::Display::fmt(self:"Already mutably borrowed" , f) |
918 | } |
919 | } |
920 | |
921 | impl From<PyBorrowError> for PyErr { |
922 | fn from(other: PyBorrowError) -> Self { |
923 | PyRuntimeError::new_err(args:other.to_string()) |
924 | } |
925 | } |
926 | |
927 | /// An error type returned by [`PyCell::try_borrow_mut`]. |
928 | /// |
929 | /// If this error is allowed to bubble up into Python code it will raise a `RuntimeError`. |
930 | pub struct PyBorrowMutError { |
931 | _private: (), |
932 | } |
933 | |
934 | impl fmt::Debug for PyBorrowMutError { |
935 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
936 | f.debug_struct(name:"PyBorrowMutError" ).finish() |
937 | } |
938 | } |
939 | |
940 | impl fmt::Display for PyBorrowMutError { |
941 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
942 | fmt::Display::fmt(self:"Already borrowed" , f) |
943 | } |
944 | } |
945 | |
946 | impl From<PyBorrowMutError> for PyErr { |
947 | fn from(other: PyBorrowMutError) -> Self { |
948 | PyRuntimeError::new_err(args:other.to_string()) |
949 | } |
950 | } |
951 | |
952 | #[doc (hidden)] |
953 | pub trait PyCellLayout<T>: PyLayout<T> { |
954 | fn ensure_threadsafe(&self); |
955 | /// Implementation of tp_dealloc. |
956 | /// # Safety |
957 | /// - slf must be a valid pointer to an instance of a T or a subclass. |
958 | /// - slf must not be used after this call (as it will be freed). |
959 | unsafe fn tp_dealloc(py: Python<'_>, slf: *mut ffi::PyObject); |
960 | } |
961 | |
962 | impl<T, U> PyCellLayout<T> for PyCellBase<U> |
963 | where |
964 | U: PySizedLayout<T>, |
965 | T: PyTypeInfo, |
966 | { |
967 | fn ensure_threadsafe(&self) {} |
968 | unsafe fn tp_dealloc(py: Python<'_>, slf: *mut ffi::PyObject) { |
969 | let type_obj = T::type_object_raw(py); |
970 | // For `#[pyclass]` types which inherit from PyAny, we can just call tp_free |
971 | if type_obj == std::ptr::addr_of_mut!(ffi::PyBaseObject_Type) { |
972 | return get_tp_free(ffi::Py_TYPE(slf))(slf as _); |
973 | } |
974 | |
975 | // More complex native types (e.g. `extends=PyDict`) require calling the base's dealloc. |
976 | #[cfg (not(Py_LIMITED_API))] |
977 | { |
978 | if let Some(dealloc) = (*type_obj).tp_dealloc { |
979 | // Before CPython 3.11 BaseException_dealloc would use Py_GC_UNTRACK which |
980 | // assumes the exception is currently GC tracked, so we have to re-track |
981 | // before calling the dealloc so that it can safely call Py_GC_UNTRACK. |
982 | #[cfg (not(any(Py_3_11, PyPy)))] |
983 | if ffi::PyType_FastSubclass(type_obj, ffi::Py_TPFLAGS_BASE_EXC_SUBCLASS) == 1 { |
984 | ffi::PyObject_GC_Track(slf.cast()); |
985 | } |
986 | dealloc(slf as _); |
987 | } else { |
988 | get_tp_free(ffi::Py_TYPE(slf))(slf as _); |
989 | } |
990 | } |
991 | |
992 | #[cfg (Py_LIMITED_API)] |
993 | unreachable!("subclassing native types is not possible with the `abi3` feature" ); |
994 | } |
995 | } |
996 | |
997 | impl<T: PyClassImpl> PyCellLayout<T> for PyCell<T> |
998 | where |
999 | <T::BaseType as PyClassBaseType>::LayoutAsBase: PyCellLayout<T::BaseType>, |
1000 | { |
1001 | fn ensure_threadsafe(&self) { |
1002 | self.contents.thread_checker.ensure(); |
1003 | self.ob_base.ensure_threadsafe(); |
1004 | } |
1005 | unsafe fn tp_dealloc(py: Python<'_>, slf: *mut ffi::PyObject) { |
1006 | // Safety: Python only calls tp_dealloc when no references to the object remain. |
1007 | let cell: &mut PyCell = &mut *(slf as *mut PyCell<T>); |
1008 | if cell.contents.thread_checker.can_drop(py) { |
1009 | ManuallyDrop::drop(&mut cell.contents.value); |
1010 | } |
1011 | cell.contents.dict.clear_dict(py); |
1012 | cell.contents.weakref.clear_weakrefs(_obj:slf, py); |
1013 | <T::BaseType as PyClassBaseType>::LayoutAsBase::tp_dealloc(py, slf) |
1014 | } |
1015 | } |
1016 | |
1017 | #[cfg (test)] |
1018 | #[cfg (feature = "macros" )] |
1019 | mod tests { |
1020 | |
1021 | use super::*; |
1022 | |
1023 | #[crate::pyclass ] |
1024 | #[pyo3(crate = "crate" )] |
1025 | #[derive (Copy, Clone, PartialEq, Eq, Debug)] |
1026 | struct SomeClass(i32); |
1027 | |
1028 | #[test ] |
1029 | fn pycell_replace() { |
1030 | Python::with_gil(|py| { |
1031 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1032 | assert_eq!(*cell.borrow(), SomeClass(0)); |
1033 | |
1034 | let previous = cell.replace(SomeClass(123)); |
1035 | assert_eq!(previous, SomeClass(0)); |
1036 | assert_eq!(*cell.borrow(), SomeClass(123)); |
1037 | }) |
1038 | } |
1039 | |
1040 | #[test ] |
1041 | #[should_panic (expected = "Already borrowed: PyBorrowMutError" )] |
1042 | fn pycell_replace_panic() { |
1043 | Python::with_gil(|py| { |
1044 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1045 | let _guard = cell.borrow(); |
1046 | |
1047 | cell.replace(SomeClass(123)); |
1048 | }) |
1049 | } |
1050 | |
1051 | #[test ] |
1052 | fn pycell_replace_with() { |
1053 | Python::with_gil(|py| { |
1054 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1055 | assert_eq!(*cell.borrow(), SomeClass(0)); |
1056 | |
1057 | let previous = cell.replace_with(|value| { |
1058 | *value = SomeClass(2); |
1059 | SomeClass(123) |
1060 | }); |
1061 | assert_eq!(previous, SomeClass(2)); |
1062 | assert_eq!(*cell.borrow(), SomeClass(123)); |
1063 | }) |
1064 | } |
1065 | |
1066 | #[test ] |
1067 | #[should_panic (expected = "Already borrowed: PyBorrowMutError" )] |
1068 | fn pycell_replace_with_panic() { |
1069 | Python::with_gil(|py| { |
1070 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1071 | let _guard = cell.borrow(); |
1072 | |
1073 | cell.replace_with(|_| SomeClass(123)); |
1074 | }) |
1075 | } |
1076 | |
1077 | #[test ] |
1078 | fn pycell_swap() { |
1079 | Python::with_gil(|py| { |
1080 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1081 | let cell2 = PyCell::new(py, SomeClass(123)).unwrap(); |
1082 | assert_eq!(*cell.borrow(), SomeClass(0)); |
1083 | assert_eq!(*cell2.borrow(), SomeClass(123)); |
1084 | |
1085 | cell.swap(cell2); |
1086 | assert_eq!(*cell.borrow(), SomeClass(123)); |
1087 | assert_eq!(*cell2.borrow(), SomeClass(0)); |
1088 | }) |
1089 | } |
1090 | |
1091 | #[test ] |
1092 | #[should_panic (expected = "Already borrowed: PyBorrowMutError" )] |
1093 | fn pycell_swap_panic() { |
1094 | Python::with_gil(|py| { |
1095 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1096 | let cell2 = PyCell::new(py, SomeClass(123)).unwrap(); |
1097 | |
1098 | let _guard = cell.borrow(); |
1099 | cell.swap(cell2); |
1100 | }) |
1101 | } |
1102 | |
1103 | #[test ] |
1104 | #[should_panic (expected = "Already borrowed: PyBorrowMutError" )] |
1105 | fn pycell_swap_panic_other_borrowed() { |
1106 | Python::with_gil(|py| { |
1107 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1108 | let cell2 = PyCell::new(py, SomeClass(123)).unwrap(); |
1109 | |
1110 | let _guard = cell2.borrow(); |
1111 | cell.swap(cell2); |
1112 | }) |
1113 | } |
1114 | |
1115 | #[test ] |
1116 | fn test_as_ptr() { |
1117 | Python::with_gil(|py| { |
1118 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1119 | let ptr = cell.as_ptr(); |
1120 | |
1121 | assert_eq!(cell.borrow().as_ptr(), ptr); |
1122 | assert_eq!(cell.borrow_mut().as_ptr(), ptr); |
1123 | }) |
1124 | } |
1125 | |
1126 | #[test ] |
1127 | fn test_into_ptr() { |
1128 | Python::with_gil(|py| { |
1129 | let cell = PyCell::new(py, SomeClass(0)).unwrap(); |
1130 | let ptr = cell.as_ptr(); |
1131 | |
1132 | assert_eq!(cell.borrow().into_ptr(), ptr); |
1133 | unsafe { ffi::Py_DECREF(ptr) }; |
1134 | |
1135 | assert_eq!(cell.borrow_mut().into_ptr(), ptr); |
1136 | unsafe { ffi::Py_DECREF(ptr) }; |
1137 | }) |
1138 | } |
1139 | } |
1140 | |