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