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