pyo3/types/weakref/
anyref.rs

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(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(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
426        fn get_type(py: Python<'_>) -> PyResult<Bound<'_, PyType>> {
427            py.run(ffi::c_str!("class A:\n    pass\n"), None, None)?;
428            py.eval(ffi::c_str!("A"), None, None)
429                .downcast_into::<PyType>()
430        }
431
432        #[test]
433        fn test_weakref_upgrade_as() -> PyResult<()> {
434            fn inner(
435                create_reference: impl for<'py> FnOnce(
436                    &Bound<'py, PyAny>,
437                )
438                    -> PyResult<Bound<'py, PyWeakref>>,
439            ) -> PyResult<()> {
440                Python::with_gil(|py| {
441                    let class = get_type(py)?;
442                    let object = class.call0()?;
443                    let reference = create_reference(&object)?;
444
445                    {
446                        // This test is a bit weird but ok.
447                        let obj = reference.upgrade_as::<PyAny>();
448
449                        assert!(obj.is_ok());
450                        let obj = obj.unwrap();
451
452                        assert!(obj.is_some());
453                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
454                            && obj.is_exact_instance(&class)));
455                    }
456
457                    drop(object);
458
459                    {
460                        // This test is a bit weird but ok.
461                        let obj = reference.upgrade_as::<PyAny>();
462
463                        assert!(obj.is_ok());
464                        let obj = obj.unwrap();
465
466                        assert!(obj.is_none());
467                    }
468
469                    Ok(())
470                })
471            }
472
473            inner(new_reference)?;
474            inner(new_proxy)
475        }
476
477        #[test]
478        fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
479            fn inner(
480                create_reference: impl for<'py> FnOnce(
481                    &Bound<'py, PyAny>,
482                )
483                    -> PyResult<Bound<'py, PyWeakref>>,
484            ) -> PyResult<()> {
485                Python::with_gil(|py| {
486                    let class = get_type(py)?;
487                    let object = class.call0()?;
488                    let reference = create_reference(&object)?;
489
490                    {
491                        // This test is a bit weird but ok.
492                        let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
493
494                        assert!(obj.is_some());
495                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
496                            && obj.is_exact_instance(&class)));
497                    }
498
499                    drop(object);
500
501                    {
502                        // This test is a bit weird but ok.
503                        let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
504
505                        assert!(obj.is_none());
506                    }
507
508                    Ok(())
509                })
510            }
511
512            inner(new_reference)?;
513            inner(new_proxy)
514        }
515
516        #[test]
517        fn test_weakref_upgrade() -> PyResult<()> {
518            fn inner(
519                create_reference: impl for<'py> FnOnce(
520                    &Bound<'py, PyAny>,
521                )
522                    -> PyResult<Bound<'py, PyWeakref>>,
523                call_retrievable: bool,
524            ) -> PyResult<()> {
525                let not_call_retrievable = !call_retrievable;
526
527                Python::with_gil(|py| {
528                    let class = get_type(py)?;
529                    let object = class.call0()?;
530                    let reference = create_reference(&object)?;
531
532                    assert!(not_call_retrievable || reference.call0()?.is(&object));
533                    assert!(reference.upgrade().is_some());
534                    assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
535
536                    drop(object);
537
538                    assert!(not_call_retrievable || reference.call0()?.is_none());
539                    assert!(reference.upgrade().is_none());
540
541                    Ok(())
542                })
543            }
544
545            inner(new_reference, true)?;
546            inner(new_proxy, false)
547        }
548
549        #[test]
550        #[allow(deprecated)]
551        fn test_weakref_get_object() -> PyResult<()> {
552            fn inner(
553                create_reference: impl for<'py> FnOnce(
554                    &Bound<'py, PyAny>,
555                )
556                    -> PyResult<Bound<'py, PyWeakref>>,
557                call_retrievable: bool,
558            ) -> PyResult<()> {
559                let not_call_retrievable = !call_retrievable;
560
561                Python::with_gil(|py| {
562                    let class = get_type(py)?;
563                    let object = class.call0()?;
564                    let reference = create_reference(&object)?;
565
566                    assert!(not_call_retrievable || reference.call0()?.is(&object));
567                    assert!(reference.get_object().is(&object));
568
569                    drop(object);
570
571                    assert!(not_call_retrievable || reference.call0()?.is(&reference.get_object()));
572                    assert!(not_call_retrievable || reference.call0()?.is_none());
573                    assert!(reference.get_object().is_none());
574
575                    Ok(())
576                })
577            }
578
579            inner(new_reference, true)?;
580            inner(new_proxy, false)
581        }
582    }
583
584    // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable.
585    #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))]
586    mod pyo3_pyclass {
587        use super::*;
588        use crate::{pyclass, Py};
589
590        #[pyclass(weakref, crate = "crate")]
591        struct WeakrefablePyClass {}
592
593        #[test]
594        fn test_weakref_upgrade_as() -> PyResult<()> {
595            fn inner(
596                create_reference: impl for<'py> FnOnce(
597                    &Bound<'py, PyAny>,
598                )
599                    -> PyResult<Bound<'py, PyWeakref>>,
600            ) -> PyResult<()> {
601                Python::with_gil(|py| {
602                    let object = Py::new(py, WeakrefablePyClass {})?;
603                    let reference = create_reference(object.bind(py))?;
604
605                    {
606                        let obj = reference.upgrade_as::<WeakrefablePyClass>();
607
608                        assert!(obj.is_ok());
609                        let obj = obj.unwrap();
610
611                        assert!(obj.is_some());
612                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
613                    }
614
615                    drop(object);
616
617                    {
618                        let obj = reference.upgrade_as::<WeakrefablePyClass>();
619
620                        assert!(obj.is_ok());
621                        let obj = obj.unwrap();
622
623                        assert!(obj.is_none());
624                    }
625
626                    Ok(())
627                })
628            }
629
630            inner(new_reference)?;
631            inner(new_proxy)
632        }
633
634        #[test]
635        fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
636            fn inner(
637                create_reference: impl for<'py> FnOnce(
638                    &Bound<'py, PyAny>,
639                )
640                    -> PyResult<Bound<'py, PyWeakref>>,
641            ) -> PyResult<()> {
642                Python::with_gil(|py| {
643                    let object = Py::new(py, WeakrefablePyClass {})?;
644                    let reference = create_reference(object.bind(py))?;
645
646                    {
647                        let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
648
649                        assert!(obj.is_some());
650                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
651                    }
652
653                    drop(object);
654
655                    {
656                        let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
657
658                        assert!(obj.is_none());
659                    }
660
661                    Ok(())
662                })
663            }
664
665            inner(new_reference)?;
666            inner(new_proxy)
667        }
668
669        #[test]
670        fn test_weakref_upgrade() -> PyResult<()> {
671            fn inner(
672                create_reference: impl for<'py> FnOnce(
673                    &Bound<'py, PyAny>,
674                )
675                    -> PyResult<Bound<'py, PyWeakref>>,
676                call_retrievable: bool,
677            ) -> PyResult<()> {
678                let not_call_retrievable = !call_retrievable;
679
680                Python::with_gil(|py| {
681                    let object = Py::new(py, WeakrefablePyClass {})?;
682                    let reference = create_reference(object.bind(py))?;
683
684                    assert!(not_call_retrievable || reference.call0()?.is(&object));
685                    assert!(reference.upgrade().is_some());
686                    assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
687
688                    drop(object);
689
690                    assert!(not_call_retrievable || reference.call0()?.is_none());
691                    assert!(reference.upgrade().is_none());
692
693                    Ok(())
694                })
695            }
696
697            inner(new_reference, true)?;
698            inner(new_proxy, false)
699        }
700
701        #[test]
702        #[allow(deprecated)]
703        fn test_weakref_get_object() -> PyResult<()> {
704            fn inner(
705                create_reference: impl for<'py> FnOnce(
706                    &Bound<'py, PyAny>,
707                )
708                    -> PyResult<Bound<'py, PyWeakref>>,
709                call_retrievable: bool,
710            ) -> PyResult<()> {
711                let not_call_retrievable = !call_retrievable;
712
713                Python::with_gil(|py| {
714                    let object = Py::new(py, WeakrefablePyClass {})?;
715                    let reference = create_reference(object.bind(py))?;
716
717                    assert!(not_call_retrievable || reference.call0()?.is(&object));
718                    assert!(reference.get_object().is(&object));
719
720                    drop(object);
721
722                    assert!(not_call_retrievable || reference.call0()?.is(&reference.get_object()));
723                    assert!(not_call_retrievable || reference.call0()?.is_none());
724                    assert!(reference.get_object().is_none());
725
726                    Ok(())
727                })
728            }
729
730            inner(new_reference, true)?;
731            inner(new_proxy, false)
732        }
733    }
734}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here