1use crate::err::PyResult;
2use crate::ffi_ptr_ext::FfiPtrExt;
3use crate::type_object::{PyTypeCheck, PyTypeInfo};
4use crate::types::{
5 any::{PyAny, PyAnyMethods},
6 PyNone,
7};
8use crate::{ffi, Bound, Python};
9
10/// Represents any Python `weakref` reference.
11///
12/// In Python this is created by calling `weakref.ref` or `weakref.proxy`.
13#[repr(transparent)]
14pub struct PyWeakref(PyAny);
15
16pyobject_native_type_named!(PyWeakref);
17
18// TODO: We known the layout but this cannot be implemented, due to the lack of public typeobject pointers
19// #[cfg(not(Py_LIMITED_API))]
20// pyobject_native_type_sized!(PyWeakref, ffi::PyWeakReference);
21
22impl PyTypeCheck for PyWeakref {
23 const NAME: &'static str = "weakref";
24
25 fn type_check(object: &Bound<'_, PyAny>) -> bool {
26 unsafe { ffi::PyWeakref_Check(op:object.as_ptr()) > 0 }
27 }
28}
29
30/// Implementation of functionality for [`PyWeakref`].
31///
32/// These methods are defined for the `Bound<'py, PyWeakref>` smart pointer, so to use method call
33/// syntax these methods are separated into a trait, because stable Rust does not yet support
34/// `arbitrary_self_types`.
35#[doc(alias = "PyWeakref")]
36pub trait PyWeakrefMethods<'py>: crate::sealed::Sealed {
37 /// Upgrade the weakref to a direct Bound object reference.
38 ///
39 /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
40 /// In Python it would be equivalent to [`PyWeakref_GetRef`].
41 ///
42 /// # Example
43 #[cfg_attr(
44 not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
45 doc = "```rust,ignore"
46 )]
47 #[cfg_attr(
48 all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
49 doc = "```rust"
50 )]
51 /// use pyo3::prelude::*;
52 /// use pyo3::types::PyWeakrefReference;
53 ///
54 /// #[pyclass(weakref)]
55 /// struct Foo { /* fields omitted */ }
56 ///
57 /// #[pymethods]
58 /// impl Foo {
59 /// fn get_data(&self) -> (&str, u32) {
60 /// ("Dave", 10)
61 /// }
62 /// }
63 ///
64 /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult<String> {
65 /// if let Some(data_src) = reference.upgrade_as::<Foo>()? {
66 /// let data = data_src.borrow();
67 /// let (name, score) = data.get_data();
68 /// Ok(format!("Processing '{}': score = {}", name, score))
69 /// } else {
70 /// Ok("The supplied data reference is nolonger relavent.".to_owned())
71 /// }
72 /// }
73 ///
74 /// # fn main() -> PyResult<()> {
75 /// Python::with_gil(|py| {
76 /// let data = Bound::new(py, Foo{})?;
77 /// let reference = PyWeakrefReference::new(&data)?;
78 ///
79 /// assert_eq!(
80 /// parse_data(reference.as_borrowed())?,
81 /// "Processing 'Dave': score = 10"
82 /// );
83 ///
84 /// drop(data);
85 ///
86 /// assert_eq!(
87 /// parse_data(reference.as_borrowed())?,
88 /// "The supplied data reference is nolonger relavent."
89 /// );
90 ///
91 /// Ok(())
92 /// })
93 /// # }
94 /// ```
95 ///
96 /// # Panics
97 /// This function panics is the current object is invalid.
98 /// If used propperly this is never the case. (NonNull and actually a weakref type)
99 ///
100 /// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef
101 /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
102 /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
103 fn upgrade_as<T>(&self) -> PyResult<Option<Bound<'py, T>>>
104 where
105 T: PyTypeCheck,
106 {
107 self.upgrade()
108 .map(Bound::downcast_into::<T>)
109 .transpose()
110 .map_err(Into::into)
111 }
112
113 /// Upgrade the weakref to a direct Bound object reference unchecked. The type of the recovered object is not checked before downcasting, this could lead to unexpected behavior. Use only when absolutely certain the type can be guaranteed. The `weakref` may still return `None`.
114 ///
115 /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
116 /// In Python it would be equivalent to [`PyWeakref_GetRef`].
117 ///
118 /// # Safety
119 /// Callers must ensure that the type is valid or risk type confusion.
120 /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up.
121 ///
122 /// # Example
123 #[cfg_attr(
124 not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
125 doc = "```rust,ignore"
126 )]
127 #[cfg_attr(
128 all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
129 doc = "```rust"
130 )]
131 /// use pyo3::prelude::*;
132 /// use pyo3::types::PyWeakrefReference;
133 ///
134 /// #[pyclass(weakref)]
135 /// struct Foo { /* fields omitted */ }
136 ///
137 /// #[pymethods]
138 /// impl Foo {
139 /// fn get_data(&self) -> (&str, u32) {
140 /// ("Dave", 10)
141 /// }
142 /// }
143 ///
144 /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> String {
145 /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::<Foo>() } {
146 /// let data = data_src.borrow();
147 /// let (name, score) = data.get_data();
148 /// format!("Processing '{}': score = {}", name, score)
149 /// } else {
150 /// "The supplied data reference is nolonger relavent.".to_owned()
151 /// }
152 /// }
153 ///
154 /// # fn main() -> PyResult<()> {
155 /// Python::with_gil(|py| {
156 /// let data = Bound::new(py, Foo{})?;
157 /// let reference = PyWeakrefReference::new(&data)?;
158 ///
159 /// assert_eq!(
160 /// parse_data(reference.as_borrowed()),
161 /// "Processing 'Dave': score = 10"
162 /// );
163 ///
164 /// drop(data);
165 ///
166 /// assert_eq!(
167 /// parse_data(reference.as_borrowed()),
168 /// "The supplied data reference is nolonger relavent."
169 /// );
170 ///
171 /// Ok(())
172 /// })
173 /// # }
174 /// ```
175 ///
176 /// # Panics
177 /// This function panics is the current object is invalid.
178 /// If used propperly this is never the case. (NonNull and actually a weakref type)
179 ///
180 /// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef
181 /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
182 /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
183 unsafe fn upgrade_as_unchecked<T>(&self) -> Option<Bound<'py, T>> {
184 Some(unsafe { self.upgrade()?.downcast_into_unchecked() })
185 }
186
187 /// Upgrade the weakref to a exact direct Bound object reference.
188 ///
189 /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
190 /// In Python it would be equivalent to [`PyWeakref_GetRef`].
191 ///
192 /// # Example
193 #[cfg_attr(
194 not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
195 doc = "```rust,ignore"
196 )]
197 #[cfg_attr(
198 all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
199 doc = "```rust"
200 )]
201 /// use pyo3::prelude::*;
202 /// use pyo3::types::PyWeakrefReference;
203 ///
204 /// #[pyclass(weakref)]
205 /// struct Foo { /* fields omitted */ }
206 ///
207 /// #[pymethods]
208 /// impl Foo {
209 /// fn get_data(&self) -> (&str, u32) {
210 /// ("Dave", 10)
211 /// }
212 /// }
213 ///
214 /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult<String> {
215 /// if let Some(data_src) = reference.upgrade_as_exact::<Foo>()? {
216 /// let data = data_src.borrow();
217 /// let (name, score) = data.get_data();
218 /// Ok(format!("Processing '{}': score = {}", name, score))
219 /// } else {
220 /// Ok("The supplied data reference is nolonger relavent.".to_owned())
221 /// }
222 /// }
223 ///
224 /// # fn main() -> PyResult<()> {
225 /// Python::with_gil(|py| {
226 /// let data = Bound::new(py, Foo{})?;
227 /// let reference = PyWeakrefReference::new(&data)?;
228 ///
229 /// assert_eq!(
230 /// parse_data(reference.as_borrowed())?,
231 /// "Processing 'Dave': score = 10"
232 /// );
233 ///
234 /// drop(data);
235 ///
236 /// assert_eq!(
237 /// parse_data(reference.as_borrowed())?,
238 /// "The supplied data reference is nolonger relavent."
239 /// );
240 ///
241 /// Ok(())
242 /// })
243 /// # }
244 /// ```
245 ///
246 /// # Panics
247 /// This function panics is the current object is invalid.
248 /// If used propperly this is never the case. (NonNull and actually a weakref type)
249 ///
250 /// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef
251 /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
252 /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
253 fn upgrade_as_exact<T>(&self) -> PyResult<Option<Bound<'py, T>>>
254 where
255 T: PyTypeInfo,
256 {
257 self.upgrade()
258 .map(Bound::downcast_into_exact)
259 .transpose()
260 .map_err(Into::into)
261 }
262
263 /// Upgrade the weakref to a Bound [`PyAny`] reference to the target object if possible.
264 ///
265 /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
266 /// This function returns `Some(Bound<'py, PyAny>)` if the reference still exists, otherwise `None` will be returned.
267 ///
268 /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]).
269 /// It produces similar results to using [`PyWeakref_GetRef`] in the C api.
270 ///
271 /// # Example
272 #[cfg_attr(
273 not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
274 doc = "```rust,ignore"
275 )]
276 #[cfg_attr(
277 all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
278 doc = "```rust"
279 )]
280 /// use pyo3::prelude::*;
281 /// use pyo3::types::PyWeakrefReference;
282 ///
283 /// #[pyclass(weakref)]
284 /// struct Foo { /* fields omitted */ }
285 ///
286 /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult<String> {
287 /// if let Some(object) = reference.upgrade() {
288 /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?))
289 /// } else {
290 /// Ok("The object, which this reference refered to, no longer exists".to_owned())
291 /// }
292 /// }
293 ///
294 /// # fn main() -> PyResult<()> {
295 /// Python::with_gil(|py| {
296 /// let data = Bound::new(py, Foo{})?;
297 /// let reference = PyWeakrefReference::new(&data)?;
298 ///
299 /// assert_eq!(
300 /// parse_data(reference.as_borrowed())?,
301 /// "The object 'Foo' refered by this reference still exists."
302 /// );
303 ///
304 /// drop(data);
305 ///
306 /// assert_eq!(
307 /// parse_data(reference.as_borrowed())?,
308 /// "The object, which this reference refered to, no longer exists"
309 /// );
310 ///
311 /// Ok(())
312 /// })
313 /// # }
314 /// ```
315 ///
316 /// # Panics
317 /// This function panics is the current object is invalid.
318 /// If used properly this is never the case. (NonNull and actually a weakref type)
319 ///
320 /// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef
321 /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
322 /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
323 fn upgrade(&self) -> Option<Bound<'py, PyAny>>;
324
325 /// Retrieve to a Bound object pointed to by the weakref.
326 ///
327 /// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone).
328 ///
329 /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]).
330 /// It produces similar results to using [`PyWeakref_GetRef`] in the C api.
331 ///
332 /// # Example
333 #[cfg_attr(
334 not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
335 doc = "```rust,ignore"
336 )]
337 #[cfg_attr(
338 all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
339 doc = "```rust"
340 )]
341 /// #![allow(deprecated)]
342 /// use pyo3::prelude::*;
343 /// use pyo3::types::PyWeakrefReference;
344 ///
345 /// #[pyclass(weakref)]
346 /// struct Foo { /* fields omitted */ }
347 ///
348 /// fn get_class(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult<String> {
349 /// reference
350 /// .get_object()
351 /// .getattr("__class__")?
352 /// .repr()
353 /// .map(|repr| repr.to_string())
354 /// }
355 ///
356 /// # fn main() -> PyResult<()> {
357 /// Python::with_gil(|py| {
358 /// let object = Bound::new(py, Foo{})?;
359 /// let reference = PyWeakrefReference::new(&object)?;
360 ///
361 /// assert_eq!(
362 /// get_class(reference.as_borrowed())?,
363 /// "<class 'builtins.Foo'>"
364 /// );
365 ///
366 /// drop(object);
367 ///
368 /// assert_eq!(get_class(reference.as_borrowed())?, "<class 'NoneType'>");
369 ///
370 /// Ok(())
371 /// })
372 /// # }
373 /// ```
374 ///
375 /// # Panics
376 /// This function panics is the current object is invalid.
377 /// If used propperly this is never the case. (NonNull and actually a weakref type)
378 ///
379 /// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef
380 /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
381 /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
382 #[deprecated(since = "0.23.0", note = "Use `upgrade` instead")]
383 fn get_object(&self) -> Bound<'py, PyAny> {
384 self.upgrade().unwrap_or_else(|| {
385 // Safety: upgrade() returns `Bound<'py, PyAny>` with a lifetime `'py` if it exists, we
386 // can safely assume the same lifetime here.
387 PyNone::get(unsafe { Python::assume_gil_acquired() })
388 .to_owned()
389 .into_any()
390 })
391 }
392}
393
394impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakref> {
395 fn upgrade(&self) -> Option<Bound<'py, PyAny>> {
396 let mut obj: *mut ffi::PyObject = std::ptr::null_mut();
397 match unsafe { ffi::compat::PyWeakref_GetRef(self.as_ptr(), &mut obj) } {
398 std::os::raw::c_int::MIN..=-1 => panic!("The 'weakref' weak reference instance should be valid (non-null and actually a weakref reference)"),
399 0 => None,
400 1..=std::os::raw::c_int::MAX => Some(unsafe { obj.assume_owned_unchecked(self.py()) }),
401 }
402 }
403}
404
405#[cfg(test)]
406mod tests {
407 use crate::types::any::{PyAny, PyAnyMethods};
408 use crate::types::weakref::{PyWeakref, PyWeakrefMethods, PyWeakrefProxy, PyWeakrefReference};
409 use crate::{Bound, PyResult, Python};
410
411 fn new_reference<'py>(object: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyWeakref>> {
412 let reference = PyWeakrefReference::new(object)?;
413 reference.into_any().downcast_into().map_err(Into::into)
414 }
415
416 fn new_proxy<'py>(object: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyWeakref>> {
417 let reference = PyWeakrefProxy::new(object)?;
418 reference.into_any().downcast_into().map_err(Into::into)
419 }
420
421 mod python_class {
422 use super::*;
423 use crate::ffi;
424 use crate::{py_result_ext::PyResultExt, types::PyType};
425 use std::ptr;
426
427 fn get_type(py: Python<'_>) -> PyResult<Bound<'_, PyType>> {
428 py.run(ffi::c_str!("class A:\n pass\n"), None, None)?;
429 py.eval(ffi::c_str!("A"), None, None)
430 .downcast_into::<PyType>()
431 }
432
433 #[test]
434 fn test_weakref_upgrade_as() -> PyResult<()> {
435 fn inner(
436 create_reference: impl for<'py> FnOnce(
437 &Bound<'py, PyAny>,
438 )
439 -> PyResult<Bound<'py, PyWeakref>>,
440 ) -> PyResult<()> {
441 Python::with_gil(|py| {
442 let class = get_type(py)?;
443 let object = class.call0()?;
444 let reference = create_reference(&object)?;
445
446 {
447 // This test is a bit weird but ok.
448 let obj = reference.upgrade_as::<PyAny>();
449
450 assert!(obj.is_ok());
451 let obj = obj.unwrap();
452
453 assert!(obj.is_some());
454 assert!(
455 obj.map_or(false, |obj| ptr::eq(obj.as_ptr(), object.as_ptr())
456 && obj.is_exact_instance(&class))
457 );
458 }
459
460 drop(object);
461
462 {
463 // This test is a bit weird but ok.
464 let obj = reference.upgrade_as::<PyAny>();
465
466 assert!(obj.is_ok());
467 let obj = obj.unwrap();
468
469 assert!(obj.is_none());
470 }
471
472 Ok(())
473 })
474 }
475
476 inner(new_reference)?;
477 inner(new_proxy)
478 }
479
480 #[test]
481 fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
482 fn inner(
483 create_reference: impl for<'py> FnOnce(
484 &Bound<'py, PyAny>,
485 )
486 -> PyResult<Bound<'py, PyWeakref>>,
487 ) -> PyResult<()> {
488 Python::with_gil(|py| {
489 let class = get_type(py)?;
490 let object = class.call0()?;
491 let reference = create_reference(&object)?;
492
493 {
494 // This test is a bit weird but ok.
495 let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
496
497 assert!(obj.is_some());
498 assert!(
499 obj.map_or(false, |obj| ptr::eq(obj.as_ptr(), object.as_ptr())
500 && obj.is_exact_instance(&class))
501 );
502 }
503
504 drop(object);
505
506 {
507 // This test is a bit weird but ok.
508 let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
509
510 assert!(obj.is_none());
511 }
512
513 Ok(())
514 })
515 }
516
517 inner(new_reference)?;
518 inner(new_proxy)
519 }
520
521 #[test]
522 fn test_weakref_upgrade() -> PyResult<()> {
523 fn inner(
524 create_reference: impl for<'py> FnOnce(
525 &Bound<'py, PyAny>,
526 )
527 -> PyResult<Bound<'py, PyWeakref>>,
528 call_retrievable: bool,
529 ) -> PyResult<()> {
530 let not_call_retrievable = !call_retrievable;
531
532 Python::with_gil(|py| {
533 let class = get_type(py)?;
534 let object = class.call0()?;
535 let reference = create_reference(&object)?;
536
537 assert!(not_call_retrievable || reference.call0()?.is(&object));
538 assert!(reference.upgrade().is_some());
539 assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
540
541 drop(object);
542
543 assert!(not_call_retrievable || reference.call0()?.is_none());
544 assert!(reference.upgrade().is_none());
545
546 Ok(())
547 })
548 }
549
550 inner(new_reference, true)?;
551 inner(new_proxy, false)
552 }
553
554 #[test]
555 #[allow(deprecated)]
556 fn test_weakref_get_object() -> PyResult<()> {
557 fn inner(
558 create_reference: impl for<'py> FnOnce(
559 &Bound<'py, PyAny>,
560 )
561 -> PyResult<Bound<'py, PyWeakref>>,
562 call_retrievable: bool,
563 ) -> PyResult<()> {
564 let not_call_retrievable = !call_retrievable;
565
566 Python::with_gil(|py| {
567 let class = get_type(py)?;
568 let object = class.call0()?;
569 let reference = create_reference(&object)?;
570
571 assert!(not_call_retrievable || reference.call0()?.is(&object));
572 assert!(reference.get_object().is(&object));
573
574 drop(object);
575
576 assert!(not_call_retrievable || reference.call0()?.is(&reference.get_object()));
577 assert!(not_call_retrievable || reference.call0()?.is_none());
578 assert!(reference.get_object().is_none());
579
580 Ok(())
581 })
582 }
583
584 inner(new_reference, true)?;
585 inner(new_proxy, false)
586 }
587 }
588
589 // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable.
590 #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))]
591 mod pyo3_pyclass {
592 use super::*;
593 use crate::{pyclass, Py};
594 use std::ptr;
595
596 #[pyclass(weakref, crate = "crate")]
597 struct WeakrefablePyClass {}
598
599 #[test]
600 fn test_weakref_upgrade_as() -> PyResult<()> {
601 fn inner(
602 create_reference: impl for<'py> FnOnce(
603 &Bound<'py, PyAny>,
604 )
605 -> PyResult<Bound<'py, PyWeakref>>,
606 ) -> PyResult<()> {
607 Python::with_gil(|py| {
608 let object = Py::new(py, WeakrefablePyClass {})?;
609 let reference = create_reference(object.bind(py))?;
610
611 {
612 let obj = reference.upgrade_as::<WeakrefablePyClass>();
613
614 assert!(obj.is_ok());
615 let obj = obj.unwrap();
616
617 assert!(obj.is_some());
618 assert!(obj.map_or(false, |obj| ptr::eq(obj.as_ptr(), object.as_ptr())));
619 }
620
621 drop(object);
622
623 {
624 let obj = reference.upgrade_as::<WeakrefablePyClass>();
625
626 assert!(obj.is_ok());
627 let obj = obj.unwrap();
628
629 assert!(obj.is_none());
630 }
631
632 Ok(())
633 })
634 }
635
636 inner(new_reference)?;
637 inner(new_proxy)
638 }
639
640 #[test]
641 fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
642 fn inner(
643 create_reference: impl for<'py> FnOnce(
644 &Bound<'py, PyAny>,
645 )
646 -> PyResult<Bound<'py, PyWeakref>>,
647 ) -> PyResult<()> {
648 Python::with_gil(|py| {
649 let object = Py::new(py, WeakrefablePyClass {})?;
650 let reference = create_reference(object.bind(py))?;
651
652 {
653 let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
654
655 assert!(obj.is_some());
656 assert!(obj.map_or(false, |obj| ptr::eq(obj.as_ptr(), object.as_ptr())));
657 }
658
659 drop(object);
660
661 {
662 let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
663
664 assert!(obj.is_none());
665 }
666
667 Ok(())
668 })
669 }
670
671 inner(new_reference)?;
672 inner(new_proxy)
673 }
674
675 #[test]
676 fn test_weakref_upgrade() -> PyResult<()> {
677 fn inner(
678 create_reference: impl for<'py> FnOnce(
679 &Bound<'py, PyAny>,
680 )
681 -> PyResult<Bound<'py, PyWeakref>>,
682 call_retrievable: bool,
683 ) -> PyResult<()> {
684 let not_call_retrievable = !call_retrievable;
685
686 Python::with_gil(|py| {
687 let object = Py::new(py, WeakrefablePyClass {})?;
688 let reference = create_reference(object.bind(py))?;
689
690 assert!(not_call_retrievable || reference.call0()?.is(&object));
691 assert!(reference.upgrade().is_some());
692 assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
693
694 drop(object);
695
696 assert!(not_call_retrievable || reference.call0()?.is_none());
697 assert!(reference.upgrade().is_none());
698
699 Ok(())
700 })
701 }
702
703 inner(new_reference, true)?;
704 inner(new_proxy, false)
705 }
706
707 #[test]
708 #[allow(deprecated)]
709 fn test_weakref_get_object() -> PyResult<()> {
710 fn inner(
711 create_reference: impl for<'py> FnOnce(
712 &Bound<'py, PyAny>,
713 )
714 -> PyResult<Bound<'py, PyWeakref>>,
715 call_retrievable: bool,
716 ) -> PyResult<()> {
717 let not_call_retrievable = !call_retrievable;
718
719 Python::with_gil(|py| {
720 let object = Py::new(py, WeakrefablePyClass {})?;
721 let reference = create_reference(object.bind(py))?;
722
723 assert!(not_call_retrievable || reference.call0()?.is(&object));
724 assert!(reference.get_object().is(&object));
725
726 drop(object);
727
728 assert!(not_call_retrievable || reference.call0()?.is(&reference.get_object()));
729 assert!(not_call_retrievable || reference.call0()?.is_none());
730 assert!(reference.get_object().is_none());
731
732 Ok(())
733 })
734 }
735
736 inner(new_reference, true)?;
737 inner(new_proxy, false)
738 }
739 }
740}
741