1use super::PyWeakrefMethods;
2use crate::err::PyResult;
3use crate::ffi_ptr_ext::FfiPtrExt;
4#[cfg(feature = "experimental-inspect")]
5use crate::inspect::{type_hint_identifier, type_hint_union, PyStaticExpr};
6use crate::py_result_ext::PyResultExt;
7use crate::sync::PyOnceLock;
8use crate::type_object::PyTypeCheck;
9use crate::types::any::PyAny;
10use crate::{ffi, Borrowed, Bound, BoundObject, IntoPyObject, IntoPyObjectExt, Py, Python};
11
12#[repr(transparent)]
17pub struct PyWeakrefProxy(PyAny);
18
19pyobject_native_type_named!(PyWeakrefProxy);
20
21unsafe impl PyTypeCheck for PyWeakrefProxy {
26 const NAME: &'static str = "weakref.ProxyTypes";
27
28 #[cfg(feature = "experimental-inspect")]
29 const TYPE_HINT: PyStaticExpr = type_hint_union!(
30 type_hint_identifier!("weakref", "ProxyType"),
31 type_hint_identifier!("weakref", "CallableProxyType")
32 );
33
34 #[inline]
35 fn type_check(object: &Bound<'_, PyAny>) -> bool {
36 unsafe { ffi::PyWeakref_CheckProxy(object.as_ptr()) > 0 }
37 }
38
39 fn classinfo_object(py: Python<'_>) -> Bound<'_, PyAny> {
40 static TYPE: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
41 TYPE.import(py, "weakref", "ProxyTypes").unwrap().clone()
42 }
43}
44
45impl PyWeakrefProxy {
47 #[cfg_attr(
53 not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
54 doc = "```rust,ignore"
55 )]
56 #[cfg_attr(
57 all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
58 doc = "```rust"
59 )]
60 #[inline]
86 pub fn new<'py>(object: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyWeakrefProxy>> {
87 unsafe {
88 Bound::from_owned_ptr_or_err(
89 object.py(),
90 ffi::PyWeakref_NewProxy(object.as_ptr(), ffi::Py_None()),
91 )
92 .cast_into_unchecked()
93 }
94 }
95
96 #[cfg_attr(
102 not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
103 doc = "```rust,ignore"
104 )]
105 #[cfg_attr(
106 all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
107 doc = "```rust"
108 )]
109 #[inline]
150 pub fn new_with<'py, C>(
151 object: &Bound<'py, PyAny>,
152 callback: C,
153 ) -> PyResult<Bound<'py, PyWeakrefProxy>>
154 where
155 C: IntoPyObject<'py>,
156 {
157 fn inner<'py>(
158 object: &Bound<'py, PyAny>,
159 callback: Borrowed<'_, 'py, PyAny>,
160 ) -> PyResult<Bound<'py, PyWeakrefProxy>> {
161 unsafe {
162 Bound::from_owned_ptr_or_err(
163 object.py(),
164 ffi::PyWeakref_NewProxy(object.as_ptr(), callback.as_ptr()),
165 )
166 .cast_into_unchecked()
167 }
168 }
169
170 let py = object.py();
171 inner(
172 object,
173 callback
174 .into_pyobject_or_pyerr(py)?
175 .into_any()
176 .as_borrowed(),
177 )
178 }
179}
180
181impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakrefProxy> {
182 fn upgrade(&self) -> Option<Bound<'py, PyAny>> {
183 let mut obj: *mut ffi::PyObject = core::ptr::null_mut();
184 match unsafe { ffi::compat::PyWeakref_GetRef(self.as_ptr(), &mut obj) } {
185 core::ffi::c_int::MIN..=-1 => panic!("The 'weakref.ProxyType' (or `weakref.CallableProxyType`) instance should be valid (non-null and actually a weakref reference)"),
186 0 => None,
187 1..=core::ffi::c_int::MAX => Some(unsafe { obj.assume_owned_unchecked(self.py()) }),
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError};
195 use crate::types::any::{PyAny, PyAnyMethods};
196 use crate::types::weakref::{PyWeakrefMethods, PyWeakrefProxy};
197 use crate::{Bound, PyResult, Python};
198
199 #[cfg(all(Py_3_13, not(Py_LIMITED_API)))]
200 const DEADREF_FIX: Option<&str> = None;
201 #[cfg(all(not(Py_3_13), not(Py_LIMITED_API)))]
202 const DEADREF_FIX: Option<&str> = Some("NoneType");
203
204 #[cfg(not(Py_LIMITED_API))]
205 fn check_repr(
206 reference: &Bound<'_, PyWeakrefProxy>,
207 object: &Bound<'_, PyAny>,
208 class: Option<&str>,
209 ) -> PyResult<()> {
210 let repr = reference.repr()?.to_string();
211
212 #[cfg(Py_3_13)]
213 let (first_part, second_part) = repr.split_once(';').unwrap();
214 #[cfg(not(Py_3_13))]
215 let (first_part, second_part) = repr.split_once(" to ").unwrap();
216
217 {
218 let (msg, addr) = first_part.split_once("0x").unwrap();
219
220 assert_eq!(msg, "<weakproxy at ");
221 assert!(addr
222 .to_lowercase()
223 .contains(format!("{:x?}", reference.as_ptr()).split_at(2).1));
224 }
225
226 if let Some(class) = class.or(DEADREF_FIX) {
227 let (msg, addr) = second_part.split_once("0x").unwrap();
228
229 #[cfg(Py_3_13)]
231 assert!(msg.starts_with(" to '"));
232 assert!(msg.contains(class));
233 assert!(msg.ends_with(" at "));
234
235 assert!(addr
236 .to_lowercase()
237 .contains(format!("{:x?}", object.as_ptr()).split_at(2).1));
238 } else {
239 assert!(second_part.contains("dead"));
240 }
241
242 Ok(())
243 }
244
245 mod proxy {
246 use super::*;
247
248 #[cfg(all(not(Py_LIMITED_API), Py_3_10))]
249 const CLASS_NAME: &str = "'weakref.ProxyType'";
250 #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
251 const CLASS_NAME: &str = "'weakproxy'";
252
253 mod python_class {
254 use super::*;
255 #[cfg(Py_3_10)]
256 use crate::types::PyInt;
257 use crate::PyTypeCheck;
258 use crate::{py_result_ext::PyResultExt, types::PyDict, types::PyType};
259 use core::ptr;
260
261 fn get_type(py: Python<'_>) -> PyResult<Bound<'_, PyType>> {
262 let globals = PyDict::new(py);
263 py.run(c"class A:\n pass\n", Some(&globals), None)?;
264 py.eval(c"A", Some(&globals), None).cast_into::<PyType>()
265 }
266
267 #[test]
268 fn test_weakref_proxy_behavior() -> PyResult<()> {
269 Python::attach(|py| {
270 let class = get_type(py)?;
271 let object = class.call0()?;
272 let reference = PyWeakrefProxy::new(&object)?;
273
274 assert!(!reference.is(&object));
275 assert!(reference.upgrade().unwrap().is(&object));
276
277 #[cfg(not(Py_LIMITED_API))]
278 assert_eq!(
279 reference.get_type().to_string(),
280 format!("<class {CLASS_NAME}>")
281 );
282
283 assert_eq!(reference.getattr("__class__")?.to_string(), "<class 'A'>");
284 #[cfg(not(Py_LIMITED_API))]
285 check_repr(&reference, &object, Some("A"))?;
286
287 assert!(reference
288 .getattr("__callback__")
289 .err()
290 .is_some_and(|err| err.is_instance_of::<PyAttributeError>(py)));
291
292 assert!(reference.call0().err().is_some_and(|err| {
293 let result = err.is_instance_of::<PyTypeError>(py);
294 #[cfg(not(Py_LIMITED_API))]
295 let result = result
296 & (err.value(py).to_string()
297 == format!("{CLASS_NAME} object is not callable"));
298 result
299 }));
300
301 drop(object);
302
303 assert!(reference.upgrade().is_none());
304 assert!(reference
305 .getattr("__class__")
306 .err()
307 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)));
308 #[cfg(not(Py_LIMITED_API))]
309 check_repr(&reference, py.None().bind(py), None)?;
310
311 assert!(reference
312 .getattr("__callback__")
313 .err()
314 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)));
315
316 assert!(reference.call0().err().is_some_and(|err| {
317 let result = err.is_instance_of::<PyTypeError>(py);
318 #[cfg(not(Py_LIMITED_API))]
319 let result = result
320 & (err.value(py).to_string()
321 == format!("{CLASS_NAME} object is not callable"));
322 result
323 }));
324
325 Ok(())
326 })
327 }
328
329 #[test]
330 fn test_weakref_upgrade_as() -> PyResult<()> {
331 Python::attach(|py| {
332 let class = get_type(py)?;
333 let object = class.call0()?;
334 let reference = PyWeakrefProxy::new(&object)?;
335
336 {
337 let obj = reference.upgrade_as::<PyAny>();
339
340 assert!(obj.is_ok());
341 let obj = obj.unwrap();
342
343 assert!(obj.is_some());
344 assert!(obj.is_some_and(|obj| ptr::eq(obj.as_ptr(), object.as_ptr())
345 && obj.is_exact_instance(&class)));
346 }
347
348 drop(object);
349
350 {
351 let obj = reference.upgrade_as::<PyAny>();
353
354 assert!(obj.is_ok());
355 let obj = obj.unwrap();
356
357 assert!(obj.is_none());
358 }
359
360 Ok(())
361 })
362 }
363
364 #[test]
365 fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
366 Python::attach(|py| {
367 let class = get_type(py)?;
368 let object = class.call0()?;
369 let reference = PyWeakrefProxy::new(&object)?;
370
371 {
372 let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
374
375 assert!(obj.is_some());
376 assert!(obj.is_some_and(|obj| ptr::eq(obj.as_ptr(), object.as_ptr())
377 && obj.is_exact_instance(&class)));
378 }
379
380 drop(object);
381
382 {
383 let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
385
386 assert!(obj.is_none());
387 }
388
389 Ok(())
390 })
391 }
392
393 #[test]
394 fn test_weakref_upgrade() -> PyResult<()> {
395 Python::attach(|py| {
396 let class = get_type(py)?;
397 let object = class.call0()?;
398 let reference = PyWeakrefProxy::new(&object)?;
399
400 assert!(reference.upgrade().is_some());
401 assert!(reference.upgrade().is_some_and(|obj| obj.is(&object)));
402
403 drop(object);
404
405 assert!(reference.upgrade().is_none());
406
407 Ok(())
408 })
409 }
410
411 #[test]
412 fn test_weakref_get_object() -> PyResult<()> {
413 Python::attach(|py| {
414 let class = get_type(py)?;
415 let object = class.call0()?;
416 let reference = PyWeakrefProxy::new(&object)?;
417
418 assert!(reference.upgrade().unwrap().is(&object));
419
420 drop(object);
421
422 assert!(reference.upgrade().is_none());
423
424 Ok(())
425 })
426 }
427
428 #[test]
429 fn test_type_object() -> PyResult<()> {
430 Python::attach(|py| {
431 let class = get_type(py)?;
432 let object = class.call0()?;
433 let reference = PyWeakrefProxy::new(&object)?;
434 let t = PyWeakrefProxy::classinfo_object(py);
435 assert!(reference.is_instance(&t)?);
436 Ok(())
437 })
438 }
439
440 #[cfg(Py_3_10)] #[test]
442 fn test_classinfo_downcast_error() -> PyResult<()> {
443 Python::attach(|py| {
444 assert_eq!(
445 PyInt::new(py, 1)
446 .cast_into::<PyWeakrefProxy>()
447 .unwrap_err()
448 .to_string(),
449 "'int' object is not an instance of 'ProxyType | CallableProxyType'"
450 );
451 Ok(())
452 })
453 }
454 }
455
456 #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))]
458 mod pyo3_pyclass {
459 use super::*;
460 use crate::{pyclass, Py};
461 use core::ptr;
462
463 #[pyclass(weakref, crate = "crate")]
464 struct WeakrefablePyClass {}
465
466 #[test]
467 fn test_weakref_proxy_behavior() -> PyResult<()> {
468 Python::attach(|py| {
469 let object: Bound<'_, WeakrefablePyClass> =
470 Bound::new(py, WeakrefablePyClass {})?;
471 let reference = PyWeakrefProxy::new(&object)?;
472
473 assert!(!reference.is(&object));
474 assert!(reference.upgrade().unwrap().is(&object));
475 #[cfg(not(Py_LIMITED_API))]
476 assert_eq!(
477 reference.get_type().to_string(),
478 format!("<class {CLASS_NAME}>")
479 );
480
481 assert_eq!(
482 reference.getattr("__class__")?.to_string(),
483 "<class 'builtins.WeakrefablePyClass'>"
484 );
485 #[cfg(not(Py_LIMITED_API))]
486 check_repr(&reference, object.as_any(), Some("WeakrefablePyClass"))?;
487
488 assert!(reference
489 .getattr("__callback__")
490 .err()
491 .is_some_and(|err| err.is_instance_of::<PyAttributeError>(py)));
492
493 assert!(reference.call0().err().is_some_and(|err| {
494 let result = err.is_instance_of::<PyTypeError>(py);
495 #[cfg(not(Py_LIMITED_API))]
496 let result = result
497 & (err.value(py).to_string()
498 == format!("{CLASS_NAME} object is not callable"));
499 result
500 }));
501
502 drop(object);
503
504 assert!(reference.upgrade().is_none());
505 assert!(reference
506 .getattr("__class__")
507 .err()
508 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)));
509 #[cfg(not(Py_LIMITED_API))]
510 check_repr(&reference, py.None().bind(py), None)?;
511
512 assert!(reference
513 .getattr("__callback__")
514 .err()
515 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)));
516
517 assert!(reference.call0().err().is_some_and(|err| {
518 let result = err.is_instance_of::<PyTypeError>(py);
519 #[cfg(not(Py_LIMITED_API))]
520 let result = result
521 & (err.value(py).to_string()
522 == format!("{CLASS_NAME} object is not callable"));
523 result
524 }));
525
526 Ok(())
527 })
528 }
529
530 #[test]
531 fn test_weakref_upgrade_as() -> PyResult<()> {
532 Python::attach(|py| {
533 let object = Py::new(py, WeakrefablePyClass {})?;
534 let reference = PyWeakrefProxy::new(object.bind(py))?;
535
536 {
537 let obj = reference.upgrade_as::<WeakrefablePyClass>();
538
539 assert!(obj.is_ok());
540 let obj = obj.unwrap();
541
542 assert!(obj.is_some());
543 assert!(obj.is_some_and(|obj| ptr::eq(obj.as_ptr(), object.as_ptr())));
544 }
545
546 drop(object);
547
548 {
549 let obj = reference.upgrade_as::<WeakrefablePyClass>();
550
551 assert!(obj.is_ok());
552 let obj = obj.unwrap();
553
554 assert!(obj.is_none());
555 }
556
557 Ok(())
558 })
559 }
560
561 #[test]
562 fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
563 Python::attach(|py| {
564 let object = Py::new(py, WeakrefablePyClass {})?;
565 let reference = PyWeakrefProxy::new(object.bind(py))?;
566
567 {
568 let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
569
570 assert!(obj.is_some());
571 assert!(obj.is_some_and(|obj| ptr::eq(obj.as_ptr(), object.as_ptr())));
572 }
573
574 drop(object);
575
576 {
577 let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
578
579 assert!(obj.is_none());
580 }
581
582 Ok(())
583 })
584 }
585
586 #[test]
587 fn test_weakref_upgrade() -> PyResult<()> {
588 Python::attach(|py| {
589 let object = Py::new(py, WeakrefablePyClass {})?;
590 let reference = PyWeakrefProxy::new(object.bind(py))?;
591
592 assert!(reference.upgrade().is_some());
593 assert!(reference.upgrade().is_some_and(|obj| obj.is(&object)));
594
595 drop(object);
596
597 assert!(reference.upgrade().is_none());
598
599 Ok(())
600 })
601 }
602 }
603 }
604
605 mod callable_proxy {
606 use super::*;
607
608 #[cfg(all(not(Py_LIMITED_API), Py_3_10))]
609 const CLASS_NAME: &str = "<class 'weakref.CallableProxyType'>";
610 #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
611 const CLASS_NAME: &str = "<class 'weakcallableproxy'>";
612
613 mod python_class {
614 use super::*;
615 use crate::PyTypeCheck;
616 use crate::{py_result_ext::PyResultExt, types::PyDict, types::PyType};
617 use core::ptr;
618
619 fn get_type(py: Python<'_>) -> PyResult<Bound<'_, PyType>> {
620 let globals = PyDict::new(py);
621 py.run(
622 c"class A:\n def __call__(self):\n return 'This class is callable!'\n",
623 Some(&globals),
624 None,
625 )?;
626 py.eval(c"A", Some(&globals), None).cast_into::<PyType>()
627 }
628
629 #[test]
630 fn test_weakref_proxy_behavior() -> PyResult<()> {
631 Python::attach(|py| {
632 let class = get_type(py)?;
633 let object = class.call0()?;
634 let reference = PyWeakrefProxy::new(&object)?;
635
636 assert!(!reference.is(&object));
637 assert!(reference.upgrade().unwrap().is(&object));
638 #[cfg(not(Py_LIMITED_API))]
639 assert_eq!(reference.get_type().to_string(), CLASS_NAME);
640
641 assert_eq!(reference.getattr("__class__")?.to_string(), "<class 'A'>");
642 #[cfg(not(Py_LIMITED_API))]
643 check_repr(&reference, &object, Some("A"))?;
644
645 assert!(reference
646 .getattr("__callback__")
647 .err()
648 .is_some_and(|err| err.is_instance_of::<PyAttributeError>(py)));
649
650 assert_eq!(reference.call0()?.to_string(), "This class is callable!");
651
652 drop(object);
653
654 assert!(reference.upgrade().is_none());
655 assert!(reference
656 .getattr("__class__")
657 .err()
658 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)));
659 #[cfg(not(Py_LIMITED_API))]
660 check_repr(&reference, py.None().bind(py), None)?;
661
662 assert!(reference
663 .getattr("__callback__")
664 .err()
665 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)));
666
667 assert!(reference
668 .call0()
669 .err()
670 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)
671 & (err.value(py).to_string()
672 == "weakly-referenced object no longer exists")));
673
674 Ok(())
675 })
676 }
677
678 #[test]
679 fn test_weakref_upgrade_as() -> PyResult<()> {
680 Python::attach(|py| {
681 let class = get_type(py)?;
682 let object = class.call0()?;
683 let reference = PyWeakrefProxy::new(&object)?;
684
685 {
686 let obj = reference.upgrade_as::<PyAny>();
688
689 assert!(obj.is_ok());
690 let obj = obj.unwrap();
691
692 assert!(obj.is_some());
693 assert!(obj.is_some_and(|obj| ptr::eq(obj.as_ptr(), object.as_ptr())
694 && obj.is_exact_instance(&class)));
695 }
696
697 drop(object);
698
699 {
700 let obj = reference.upgrade_as::<PyAny>();
702
703 assert!(obj.is_ok());
704 let obj = obj.unwrap();
705
706 assert!(obj.is_none());
707 }
708
709 Ok(())
710 })
711 }
712
713 #[test]
714 fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
715 Python::attach(|py| {
716 let class = get_type(py)?;
717 let object = class.call0()?;
718 let reference = PyWeakrefProxy::new(&object)?;
719
720 {
721 let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
723
724 assert!(obj.is_some());
725 assert!(obj.is_some_and(|obj| ptr::eq(obj.as_ptr(), object.as_ptr())
726 && obj.is_exact_instance(&class)));
727 }
728
729 drop(object);
730
731 {
732 let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
734
735 assert!(obj.is_none());
736 }
737
738 Ok(())
739 })
740 }
741
742 #[test]
743 fn test_weakref_upgrade() -> PyResult<()> {
744 Python::attach(|py| {
745 let class = get_type(py)?;
746 let object = class.call0()?;
747 let reference = PyWeakrefProxy::new(&object)?;
748
749 assert!(reference.upgrade().is_some());
750 assert!(reference.upgrade().is_some_and(|obj| obj.is(&object)));
751
752 drop(object);
753
754 assert!(reference.upgrade().is_none());
755
756 Ok(())
757 })
758 }
759
760 #[test]
761 fn test_type_object() -> PyResult<()> {
762 Python::attach(|py| {
763 let class = get_type(py)?;
764 let object = class.call0()?;
765 let reference = PyWeakrefProxy::new(&object)?;
766 let t = PyWeakrefProxy::classinfo_object(py);
767 assert!(reference.is_instance(&t)?);
768 Ok(())
769 })
770 }
771 }
772
773 #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))]
775 mod pyo3_pyclass {
776 use super::*;
777 use crate::{pyclass, pymethods, Py};
778 use core::ptr;
779
780 #[pyclass(weakref, crate = "crate")]
781 struct WeakrefablePyClass {}
782
783 #[pymethods(crate = "crate")]
784 impl WeakrefablePyClass {
785 fn __call__(&self) -> &str {
786 "This class is callable!"
787 }
788 }
789
790 #[test]
791 fn test_weakref_proxy_behavior() -> PyResult<()> {
792 Python::attach(|py| {
793 let object: Bound<'_, WeakrefablePyClass> =
794 Bound::new(py, WeakrefablePyClass {})?;
795 let reference = PyWeakrefProxy::new(&object)?;
796
797 assert!(!reference.is(&object));
798 assert!(reference.upgrade().unwrap().is(&object));
799 #[cfg(not(Py_LIMITED_API))]
800 assert_eq!(reference.get_type().to_string(), CLASS_NAME);
801
802 assert_eq!(
803 reference.getattr("__class__")?.to_string(),
804 "<class 'builtins.WeakrefablePyClass'>"
805 );
806 #[cfg(not(Py_LIMITED_API))]
807 check_repr(&reference, object.as_any(), Some("WeakrefablePyClass"))?;
808
809 assert!(reference
810 .getattr("__callback__")
811 .err()
812 .is_some_and(|err| err.is_instance_of::<PyAttributeError>(py)));
813
814 assert_eq!(reference.call0()?.to_string(), "This class is callable!");
815
816 drop(object);
817
818 assert!(reference.upgrade().is_none());
819 assert!(reference
820 .getattr("__class__")
821 .err()
822 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)));
823 #[cfg(not(Py_LIMITED_API))]
824 check_repr(&reference, py.None().bind(py), None)?;
825
826 assert!(reference
827 .getattr("__callback__")
828 .err()
829 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)));
830
831 assert!(reference
832 .call0()
833 .err()
834 .is_some_and(|err| err.is_instance_of::<PyReferenceError>(py)
835 & (err.value(py).to_string()
836 == "weakly-referenced object no longer exists")));
837
838 Ok(())
839 })
840 }
841
842 #[test]
843 fn test_weakref_upgrade_as() -> PyResult<()> {
844 Python::attach(|py| {
845 let object = Py::new(py, WeakrefablePyClass {})?;
846 let reference = PyWeakrefProxy::new(object.bind(py))?;
847
848 {
849 let obj = reference.upgrade_as::<WeakrefablePyClass>();
850
851 assert!(obj.is_ok());
852 let obj = obj.unwrap();
853
854 assert!(obj.is_some());
855 assert!(obj.is_some_and(|obj| ptr::eq(obj.as_ptr(), object.as_ptr())));
856 }
857
858 drop(object);
859
860 {
861 let obj = reference.upgrade_as::<WeakrefablePyClass>();
862
863 assert!(obj.is_ok());
864 let obj = obj.unwrap();
865
866 assert!(obj.is_none());
867 }
868
869 Ok(())
870 })
871 }
872
873 #[test]
874 fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
875 Python::attach(|py| {
876 let object = Py::new(py, WeakrefablePyClass {})?;
877 let reference = PyWeakrefProxy::new(object.bind(py))?;
878
879 {
880 let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
881
882 assert!(obj.is_some());
883 assert!(obj.is_some_and(|obj| ptr::eq(obj.as_ptr(), object.as_ptr())));
884 }
885
886 drop(object);
887
888 {
889 let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
890
891 assert!(obj.is_none());
892 }
893
894 Ok(())
895 })
896 }
897
898 #[test]
899 fn test_weakref_upgrade() -> PyResult<()> {
900 Python::attach(|py| {
901 let object = Py::new(py, WeakrefablePyClass {})?;
902 let reference = PyWeakrefProxy::new(object.bind(py))?;
903
904 assert!(reference.upgrade().is_some());
905 assert!(reference.upgrade().is_some_and(|obj| obj.is(&object)));
906
907 drop(object);
908
909 assert!(reference.upgrade().is_none());
910
911 Ok(())
912 })
913 }
914 }
915 }
916}