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