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}