pyo3/types/weakref/
reference.rs

1use crate::err::PyResult;
2use crate::ffi_ptr_ext::FfiPtrExt;
3use crate::py_result_ext::PyResultExt;
4use crate::types::{any::PyAny, PyNone};
5use crate::{ffi, Borrowed, Bound, ToPyObject};
6
7#[cfg(any(any(PyPy, GraalPy, Py_LIMITED_API), feature = "gil-refs"))]
8use crate::type_object::PyTypeCheck;
9#[cfg(feature = "gil-refs")]
10use crate::{type_object::PyTypeInfo, PyNativeType};
11
12use super::PyWeakrefMethods;
13
14/// Represents a Python `weakref.ReferenceType`.
15///
16/// In Python this is created by calling `weakref.ref`.
17#[repr(transparent)]
18pub struct PyWeakrefReference(PyAny);
19
20#[cfg(not(any(PyPy, GraalPy, Py_LIMITED_API)))]
21pyobject_native_type!(
22    PyWeakrefReference,
23    ffi::PyWeakReference,
24    pyobject_native_static_type_object!(ffi::_PyWeakref_RefType),
25    #module=Some("weakref"),
26    #checkfunction=ffi::PyWeakref_CheckRefExact
27);
28
29// When targetting alternative or multiple interpreters, it is better to not use the internal API.
30#[cfg(any(PyPy, GraalPy, Py_LIMITED_API))]
31pyobject_native_type_named!(PyWeakrefReference);
32#[cfg(any(PyPy, GraalPy, Py_LIMITED_API))]
33pyobject_native_type_extract!(PyWeakrefReference);
34
35#[cfg(any(PyPy, GraalPy, Py_LIMITED_API))]
36impl PyTypeCheck for PyWeakrefReference {
37    const NAME: &'static str = "weakref.ReferenceType";
38
39    fn type_check(object: &Bound<'_, PyAny>) -> bool {
40        unsafe { ffi::PyWeakref_CheckRef(object.as_ptr()) > 0 }
41    }
42}
43
44impl PyWeakrefReference {
45    /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object.
46    ///
47    /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag).
48    ///
49    /// # Examples
50    #[cfg_attr(
51        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
52        doc = "```rust,ignore"
53    )]
54    #[cfg_attr(
55        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
56        doc = "```rust"
57    )]
58    /// use pyo3::prelude::*;
59    /// use pyo3::types::PyWeakrefReference;
60    ///
61    /// #[pyclass(weakref)]
62    /// struct Foo { /* fields omitted */ }
63    ///
64    /// # fn main() -> PyResult<()> {
65    /// Python::with_gil(|py| {
66    ///     let foo = Bound::new(py, Foo {})?;
67    ///     let weakref = PyWeakrefReference::new_bound(&foo)?;
68    ///     assert!(
69    ///         // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::<Foo>`
70    ///         weakref.upgrade()
71    ///             .map_or(false, |obj| obj.is(&foo))
72    ///     );
73    ///
74    ///     let weakref2 = PyWeakrefReference::new_bound(&foo)?;
75    ///     assert!(weakref.is(&weakref2));
76    ///
77    ///     drop(foo);
78    ///
79    ///     assert!(weakref.upgrade().is_none());
80    ///     Ok(())
81    /// })
82    /// # }
83    /// ```
84    pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyWeakrefReference>> {
85        // TODO: Is this inner pattern still necessary Here?
86        fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyWeakrefReference>> {
87            unsafe {
88                Bound::from_owned_ptr_or_err(
89                    object.py(),
90                    ffi::PyWeakref_NewRef(object.as_ptr(), ffi::Py_None()),
91                )
92                .downcast_into_unchecked()
93            }
94        }
95
96        inner(object)
97    }
98
99    /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object with a callback.
100    ///
101    /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag) or if the `callback` is not callable or None.
102    ///
103    /// # Examples
104    #[cfg_attr(
105        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
106        doc = "```rust,ignore"
107    )]
108    #[cfg_attr(
109        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
110        doc = "```rust"
111    )]
112    /// use pyo3::prelude::*;
113    /// use pyo3::types::PyWeakrefReference;
114    ///
115    /// #[pyclass(weakref)]
116    /// struct Foo { /* fields omitted */ }
117    ///
118    /// #[pyfunction]
119    /// fn callback(wref: Bound<'_, PyWeakrefReference>) -> PyResult<()> {
120    ///         let py = wref.py();
121    ///         assert!(wref.upgrade_as::<Foo>()?.is_none());
122    ///         py.run_bound("counter = 1", None, None)
123    /// }
124    ///
125    /// # fn main() -> PyResult<()> {
126    /// Python::with_gil(|py| {
127    ///     py.run_bound("counter = 0", None, None)?;
128    ///     assert_eq!(py.eval_bound("counter", None, None)?.extract::<u32>()?, 0);
129    ///     let foo = Bound::new(py, Foo{})?;
130    ///
131    ///     // This is fine.
132    ///     let weakref = PyWeakrefReference::new_bound_with(&foo, py.None())?;
133    ///     assert!(weakref.upgrade_as::<Foo>()?.is_some());
134    ///     assert!(
135    ///         // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::<Foo>`
136    ///         weakref.upgrade()
137    ///             .map_or(false, |obj| obj.is(&foo))
138    ///     );
139    ///     assert_eq!(py.eval_bound("counter", None, None)?.extract::<u32>()?, 0);
140    ///
141    ///     let weakref2 = PyWeakrefReference::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?;
142    ///     assert!(!weakref.is(&weakref2)); // Not the same weakref
143    ///     assert!(weakref.eq(&weakref2)?);  // But Equal, since they point to the same object
144    ///
145    ///     drop(foo);
146    ///
147    ///     assert!(weakref.upgrade_as::<Foo>()?.is_none());
148    ///     assert_eq!(py.eval_bound("counter", None, None)?.extract::<u32>()?, 1);
149    ///     Ok(())
150    /// })
151    /// # }
152    /// ```
153    pub fn new_bound_with<'py, C>(
154        object: &Bound<'py, PyAny>,
155        callback: C,
156    ) -> PyResult<Bound<'py, PyWeakrefReference>>
157    where
158        C: ToPyObject,
159    {
160        fn inner<'py>(
161            object: &Bound<'py, PyAny>,
162            callback: Bound<'py, PyAny>,
163        ) -> PyResult<Bound<'py, PyWeakrefReference>> {
164            unsafe {
165                Bound::from_owned_ptr_or_err(
166                    object.py(),
167                    ffi::PyWeakref_NewRef(object.as_ptr(), callback.as_ptr()),
168                )
169                .downcast_into_unchecked()
170            }
171        }
172
173        let py = object.py();
174        inner(object, callback.to_object(py).into_bound(py))
175    }
176}
177
178#[cfg(feature = "gil-refs")]
179impl PyWeakrefReference {
180    /// Deprecated form of [`PyWeakrefReference::new_bound`].
181    #[inline]
182    #[deprecated(
183        since = "0.21.0",
184        note = "`PyWeakrefReference::new` will be replaced by `PyWeakrefReference::new_bound` in a future PyO3 version"
185    )]
186    pub fn new<T>(object: &T) -> PyResult<&PyWeakrefReference>
187    where
188        T: PyNativeType,
189    {
190        Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref)
191    }
192
193    /// Deprecated form of [`PyWeakrefReference::new_bound_with`].
194    #[inline]
195    #[deprecated(
196        since = "0.21.0",
197        note = "`PyWeakrefReference::new_with` will be replaced by `PyWeakrefReference::new_bound_with` in a future PyO3 version"
198    )]
199    pub fn new_with<T, C>(object: &T, callback: C) -> PyResult<&PyWeakrefReference>
200    where
201        T: PyNativeType,
202        C: ToPyObject,
203    {
204        Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref)
205    }
206
207    /// Upgrade the weakref to a direct object reference.
208    ///
209    /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
210    /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]).
211    ///
212    /// # Example
213    #[cfg_attr(
214        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
215        doc = "```rust,ignore"
216    )]
217    #[cfg_attr(
218        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
219        doc = "```rust"
220    )]
221    /// use pyo3::prelude::*;
222    /// use pyo3::types::PyWeakrefReference;
223    ///
224    /// #[pyclass(weakref)]
225    /// struct Foo { /* fields omitted */ }
226    ///
227    /// #[pymethods]
228    /// impl Foo {
229    ///     fn get_data(&self) -> (&str, u32) {
230    ///         ("Dave", 10)
231    ///     }
232    /// }
233    ///
234    /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult<String> {
235    ///     if let Some(data_src) = reference.upgrade_as::<Foo>()? {
236    ///         let data = data_src.borrow();
237    ///         let (name, score) = data.get_data();
238    ///         Ok(format!("Processing '{}': score = {}", name, score))
239    ///     } else {
240    ///         Ok("The supplied data reference is nolonger relavent.".to_owned())
241    ///     }
242    /// }
243    ///
244    /// # fn main() -> PyResult<()> {
245    /// Python::with_gil(|py| {
246    ///     let data = Bound::new(py, Foo{})?;
247    ///     let reference = PyWeakrefReference::new_bound(&data)?;
248    ///
249    ///     assert_eq!(
250    ///         parse_data(reference.as_borrowed())?,
251    ///         "Processing 'Dave': score = 10"
252    ///     );
253    ///
254    ///     drop(data);
255    ///
256    ///     assert_eq!(
257    ///         parse_data(reference.as_borrowed())?,
258    ///         "The supplied data reference is nolonger relavent."
259    ///     );
260    ///
261    ///     Ok(())
262    /// })
263    /// # }
264    /// ```
265    ///
266    /// # Panics
267    /// This function panics is the current object is invalid.
268    /// If used propperly this is never the case. (NonNull and actually a weakref type)
269    ///
270    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
271    /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
272    /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
273    pub fn upgrade_as<T>(&self) -> PyResult<Option<&T::AsRefTarget>>
274    where
275        T: PyTypeCheck,
276    {
277        Ok(self
278            .as_borrowed()
279            .upgrade_as::<T>()?
280            .map(Bound::into_gil_ref))
281    }
282
283    /// Upgrade the weakref to a direct 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`.
284    ///
285    /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
286    /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]).
287    ///
288    /// # Safety
289    /// Callers must ensure that the type is valid or risk type confusion.
290    /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up.
291    ///
292    /// # Example
293    #[cfg_attr(
294        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
295        doc = "```rust,ignore"
296    )]
297    #[cfg_attr(
298        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
299        doc = "```rust"
300    )]
301    /// use pyo3::prelude::*;
302    /// use pyo3::types::PyWeakrefReference;
303    ///
304    /// #[pyclass(weakref)]
305    /// struct Foo { /* fields omitted */ }
306    ///
307    /// #[pymethods]
308    /// impl Foo {
309    ///     fn get_data(&self) -> (&str, u32) {
310    ///         ("Dave", 10)
311    ///     }
312    /// }
313    ///
314    /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> String {
315    ///     if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::<Foo>() } {
316    ///         let data = data_src.borrow();
317    ///         let (name, score) = data.get_data();
318    ///         format!("Processing '{}': score = {}", name, score)
319    ///     } else {
320    ///         "The supplied data reference is nolonger relavent.".to_owned()
321    ///     }
322    /// }
323    ///
324    /// # fn main() -> PyResult<()> {
325    /// Python::with_gil(|py| {
326    ///     let data = Bound::new(py, Foo{})?;
327    ///     let reference = PyWeakrefReference::new_bound(&data)?;
328    ///
329    ///     assert_eq!(
330    ///         parse_data(reference.as_borrowed()),
331    ///         "Processing 'Dave': score = 10"
332    ///     );
333    ///
334    ///     drop(data);
335    ///
336    ///     assert_eq!(
337    ///         parse_data(reference.as_borrowed()),
338    ///         "The supplied data reference is nolonger relavent."
339    ///     );
340    ///
341    ///     Ok(())
342    /// })
343    /// # }
344    /// ```
345    ///
346    /// # Panics
347    /// This function panics is the current object is invalid.
348    /// If used propperly this is never the case. (NonNull and actually a weakref type)
349    ///
350    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
351    /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
352    /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
353    pub unsafe fn upgrade_as_unchecked<T>(&self) -> Option<&T::AsRefTarget>
354    where
355        T: PyTypeCheck,
356    {
357        self.as_borrowed()
358            .upgrade_as_unchecked::<T>()
359            .map(Bound::into_gil_ref)
360    }
361
362    /// Upgrade the weakref to an exact direct object reference.
363    ///
364    /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
365    /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]).
366    ///
367    /// # Example
368    #[cfg_attr(
369        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
370        doc = "```rust,ignore"
371    )]
372    #[cfg_attr(
373        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
374        doc = "```rust"
375    )]
376    /// use pyo3::prelude::*;
377    /// use pyo3::types::PyWeakrefReference;
378    ///
379    /// #[pyclass(weakref)]
380    /// struct Foo { /* fields omitted */ }
381    ///
382    /// #[pymethods]
383    /// impl Foo {
384    ///     fn get_data(&self) -> (&str, u32) {
385    ///         ("Dave", 10)
386    ///     }
387    /// }
388    ///
389    /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult<String> {
390    ///     if let Some(data_src) = reference.upgrade_as_exact::<Foo>()? {
391    ///         let data = data_src.borrow();
392    ///         let (name, score) = data.get_data();
393    ///         Ok(format!("Processing '{}': score = {}", name, score))
394    ///     } else {
395    ///         Ok("The supplied data reference is nolonger relavent.".to_owned())
396    ///     }
397    /// }
398    ///
399    /// # fn main() -> PyResult<()> {
400    /// Python::with_gil(|py| {
401    ///     let data = Bound::new(py, Foo{})?;
402    ///     let reference = PyWeakrefReference::new_bound(&data)?;
403    ///
404    ///     assert_eq!(
405    ///         parse_data(reference.as_borrowed())?,
406    ///         "Processing 'Dave': score = 10"
407    ///     );
408    ///
409    ///     drop(data);
410    ///
411    ///     assert_eq!(
412    ///         parse_data(reference.as_borrowed())?,
413    ///         "The supplied data reference is nolonger relavent."
414    ///     );
415    ///
416    ///     Ok(())
417    /// })
418    /// # }
419    /// ```
420    ///
421    /// # Panics
422    /// This function panics is the current object is invalid.
423    /// If used propperly this is never the case. (NonNull and actually a weakref type)
424    ///
425    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
426    /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
427    /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
428    pub fn upgrade_as_exact<T>(&self) -> PyResult<Option<&T::AsRefTarget>>
429    where
430        T: PyTypeInfo,
431    {
432        Ok(self
433            .as_borrowed()
434            .upgrade_as_exact::<T>()?
435            .map(Bound::into_gil_ref))
436    }
437
438    /// Upgrade the weakref to a [`PyAny`] reference to the target if possible.
439    ///
440    /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
441    /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned.
442    ///
443    /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]).
444    /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api.
445    ///
446    /// # Example
447    #[cfg_attr(
448        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
449        doc = "```rust,ignore"
450    )]
451    #[cfg_attr(
452        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
453        doc = "```rust"
454    )]
455    /// use pyo3::prelude::*;
456    /// use pyo3::types::PyWeakrefReference;
457    ///
458    /// #[pyclass(weakref)]
459    /// struct Foo { /* fields omitted */ }
460    ///
461    /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult<String> {
462    ///     if let Some(object) = reference.upgrade() {
463    ///         Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?))
464    ///     } else {
465    ///         Ok("The object, which this reference refered to, no longer exists".to_owned())
466    ///     }
467    /// }
468    ///
469    /// # fn main() -> PyResult<()> {
470    /// Python::with_gil(|py| {
471    ///     let data = Bound::new(py, Foo{})?;
472    ///     let reference = PyWeakrefReference::new_bound(&data)?;
473    ///
474    ///     assert_eq!(
475    ///         parse_data(reference.as_borrowed())?,
476    ///         "The object 'Foo' refered by this reference still exists."
477    ///     );
478    ///
479    ///     drop(data);
480    ///
481    ///     assert_eq!(
482    ///         parse_data(reference.as_borrowed())?,
483    ///         "The object, which this reference refered to, no longer exists"
484    ///     );
485    ///
486    ///     Ok(())
487    /// })
488    /// # }
489    /// ```
490    ///
491    /// # Panics
492    /// This function panics is the current object is invalid.
493    /// If used propperly this is never the case. (NonNull and actually a weakref type)
494    ///
495    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
496    /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
497    /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
498    pub fn upgrade(&self) -> Option<&'_ PyAny> {
499        self.as_borrowed().upgrade().map(Bound::into_gil_ref)
500    }
501
502    /// Retrieve to a object pointed to by the weakref.
503    ///
504    /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone).
505    ///
506    /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]).
507    /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api.
508    ///
509    /// # Example
510    #[cfg_attr(
511        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
512        doc = "```rust,ignore"
513    )]
514    #[cfg_attr(
515        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
516        doc = "```rust"
517    )]
518    /// use pyo3::prelude::*;
519    /// use pyo3::types::PyWeakrefReference;
520    ///
521    /// #[pyclass(weakref)]
522    /// struct Foo { /* fields omitted */ }
523    ///
524    /// fn get_class(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult<String> {
525    ///     reference
526    ///         .get_object()
527    ///         .getattr("__class__")?
528    ///         .repr()
529    ///         .map(|repr| repr.to_string())
530    /// }
531    ///
532    /// # fn main() -> PyResult<()> {
533    /// Python::with_gil(|py| {
534    ///     let object = Bound::new(py, Foo{})?;
535    ///     let reference = PyWeakrefReference::new_bound(&object)?;
536    ///
537    ///     assert_eq!(
538    ///         get_class(reference.as_borrowed())?,
539    ///         "<class 'builtins.Foo'>"
540    ///     );
541    ///
542    ///     drop(object);
543    ///
544    ///     assert_eq!(get_class(reference.as_borrowed())?, "<class 'NoneType'>");
545    ///
546    ///     Ok(())
547    /// })
548    /// # }
549    /// ```
550    ///
551    /// # Panics
552    /// This function panics is the current object is invalid.
553    /// If used propperly this is never the case. (NonNull and actually a weakref type)
554    ///
555    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
556    /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType
557    /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref
558    pub fn get_object(&self) -> &'_ PyAny {
559        self.as_borrowed().get_object().into_gil_ref()
560    }
561}
562
563impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakrefReference> {
564    fn get_object(&self) -> Bound<'py, PyAny> {
565        let mut obj: *mut ffi::PyObject = std::ptr::null_mut();
566        match unsafe { ffi::compat::PyWeakref_GetRef(self.as_ptr(), &mut obj) } {
567            std::os::raw::c_int::MIN..=-1 => panic!("The 'weakref.ReferenceType' instance should be valid (non-null and actually a weakref reference)"),
568            0 => PyNone::get_bound(self.py()).to_owned().into_any(),
569            1..=std::os::raw::c_int::MAX => unsafe { obj.assume_owned(self.py()) },
570        }
571    }
572
573    fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> {
574        // XXX: this deliberately leaks a reference, but this is a necessary safety measure
575        // to ensure that the object is not deallocated while we are using it.
576        unsafe {
577            self.get_object()
578                .into_ptr()
579                .assume_borrowed_unchecked(self.py())
580        }
581    }
582}
583
584#[cfg(test)]
585mod tests {
586    use crate::types::any::{PyAny, PyAnyMethods};
587    use crate::types::weakref::{PyWeakrefMethods, PyWeakrefReference};
588    use crate::{Bound, PyResult, Python};
589
590    #[cfg(all(not(Py_LIMITED_API), Py_3_10))]
591    const CLASS_NAME: &str = "<class 'weakref.ReferenceType'>";
592    #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
593    const CLASS_NAME: &str = "<class 'weakref'>";
594
595    fn check_repr(
596        reference: &Bound<'_, PyWeakrefReference>,
597        object: Option<(&Bound<'_, PyAny>, &str)>,
598    ) -> PyResult<()> {
599        let repr = reference.repr()?.to_string();
600        let (first_part, second_part) = repr.split_once("; ").unwrap();
601
602        {
603            let (msg, addr) = first_part.split_once("0x").unwrap();
604
605            assert_eq!(msg, "<weakref at ");
606            assert!(addr
607                .to_lowercase()
608                .contains(format!("{:x?}", reference.as_ptr()).split_at(2).1));
609        }
610
611        match object {
612            Some((object, class)) => {
613                let (msg, addr) = second_part.split_once("0x").unwrap();
614
615                // Avoid testing on reprs directly since they the quoting and full path vs class name tends to be changedi undocumented.
616                assert!(msg.starts_with("to '"));
617                assert!(msg.contains(class));
618                assert!(msg.ends_with("' at "));
619
620                assert!(addr
621                    .to_lowercase()
622                    .contains(format!("{:x?}", object.as_ptr()).split_at(2).1));
623            }
624            None => {
625                assert_eq!(second_part, "dead>")
626            }
627        }
628
629        Ok(())
630    }
631
632    mod python_class {
633        use super::*;
634        use crate::{py_result_ext::PyResultExt, types::PyType};
635
636        fn get_type(py: Python<'_>) -> PyResult<Bound<'_, PyType>> {
637            py.run_bound("class A:\n    pass\n", None, None)?;
638            py.eval_bound("A", None, None).downcast_into::<PyType>()
639        }
640
641        #[test]
642        fn test_weakref_reference_behavior() -> PyResult<()> {
643            Python::with_gil(|py| {
644                let class = get_type(py)?;
645                let object = class.call0()?;
646                let reference = PyWeakrefReference::new_bound(&object)?;
647
648                assert!(!reference.is(&object));
649                assert!(reference.get_object().is(&object));
650
651                #[cfg(not(Py_LIMITED_API))]
652                assert_eq!(reference.get_type().to_string(), CLASS_NAME);
653
654                #[cfg(not(Py_LIMITED_API))]
655                assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME);
656
657                #[cfg(not(Py_LIMITED_API))]
658                check_repr(&reference, Some((object.as_any(), "A")))?;
659
660                assert!(reference
661                    .getattr("__callback__")
662                    .map_or(false, |result| result.is_none()));
663
664                assert!(reference.call0()?.is(&object));
665
666                drop(object);
667
668                assert!(reference.get_object().is_none());
669                #[cfg(not(Py_LIMITED_API))]
670                assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME);
671                check_repr(&reference, None)?;
672
673                assert!(reference
674                    .getattr("__callback__")
675                    .map_or(false, |result| result.is_none()));
676
677                assert!(reference.call0()?.is_none());
678
679                Ok(())
680            })
681        }
682
683        #[test]
684        fn test_weakref_upgrade_as() -> PyResult<()> {
685            Python::with_gil(|py| {
686                let class = get_type(py)?;
687                let object = class.call0()?;
688                let reference = PyWeakrefReference::new_bound(&object)?;
689
690                {
691                    // This test is a bit weird but ok.
692                    let obj = reference.upgrade_as::<PyAny>();
693
694                    assert!(obj.is_ok());
695                    let obj = obj.unwrap();
696
697                    assert!(obj.is_some());
698                    assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
699                        && obj.is_exact_instance(&class)));
700                }
701
702                drop(object);
703
704                {
705                    // This test is a bit weird but ok.
706                    let obj = reference.upgrade_as::<PyAny>();
707
708                    assert!(obj.is_ok());
709                    let obj = obj.unwrap();
710
711                    assert!(obj.is_none());
712                }
713
714                Ok(())
715            })
716        }
717
718        #[test]
719        #[allow(deprecated)]
720        fn test_weakref_upgrade_borrowed_as() -> PyResult<()> {
721            Python::with_gil(|py| {
722                let class = get_type(py)?;
723                let object = class.call0()?;
724                let reference = PyWeakrefReference::new_bound(&object)?;
725
726                {
727                    // This test is a bit weird but ok.
728                    let obj = reference.upgrade_borrowed_as::<PyAny>();
729
730                    assert!(obj.is_ok());
731                    let obj = obj.unwrap();
732
733                    assert!(obj.is_some());
734                    assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
735                        && obj.is_exact_instance(&class)));
736                }
737
738                drop(object);
739
740                {
741                    // This test is a bit weird but ok.
742                    let obj = reference.upgrade_borrowed_as::<PyAny>();
743
744                    assert!(obj.is_ok());
745                    let obj = obj.unwrap();
746
747                    // XXX: have to leak in the borrowed methods for safety :(
748                    assert!(obj.is_some());
749                }
750
751                Ok(())
752            })
753        }
754
755        #[test]
756        fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
757            Python::with_gil(|py| {
758                let class = get_type(py)?;
759                let object = class.call0()?;
760                let reference = PyWeakrefReference::new_bound(&object)?;
761
762                {
763                    // This test is a bit weird but ok.
764                    let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
765
766                    assert!(obj.is_some());
767                    assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
768                        && obj.is_exact_instance(&class)));
769                }
770
771                drop(object);
772
773                {
774                    // This test is a bit weird but ok.
775                    let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
776
777                    assert!(obj.is_none());
778                }
779
780                Ok(())
781            })
782        }
783
784        #[test]
785        #[allow(deprecated)]
786        fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> {
787            Python::with_gil(|py| {
788                let class = get_type(py)?;
789                let object = class.call0()?;
790                let reference = PyWeakrefReference::new_bound(&object)?;
791
792                {
793                    // This test is a bit weird but ok.
794                    let obj = unsafe { reference.upgrade_borrowed_as_unchecked::<PyAny>() };
795
796                    assert!(obj.is_some());
797                    assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
798                        && obj.is_exact_instance(&class)));
799                }
800
801                drop(object);
802
803                {
804                    // This test is a bit weird but ok.
805                    let obj = unsafe { reference.upgrade_borrowed_as_unchecked::<PyAny>() };
806
807                    // XXX: have to leak in the borrowed methods for safety :(
808                    assert!(obj.is_some());
809                }
810
811                Ok(())
812            })
813        }
814
815        #[test]
816        fn test_weakref_upgrade() -> PyResult<()> {
817            Python::with_gil(|py| {
818                let class = get_type(py)?;
819                let object = class.call0()?;
820                let reference = PyWeakrefReference::new_bound(&object)?;
821
822                assert!(reference.call0()?.is(&object));
823                assert!(reference.upgrade().is_some());
824                assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
825
826                drop(object);
827
828                assert!(reference.call0()?.is_none());
829                assert!(reference.upgrade().is_none());
830
831                Ok(())
832            })
833        }
834
835        #[test]
836        #[allow(deprecated)]
837        fn test_weakref_upgrade_borrowed() -> PyResult<()> {
838            Python::with_gil(|py| {
839                let class = get_type(py)?;
840                let object = class.call0()?;
841                let reference = PyWeakrefReference::new_bound(&object)?;
842
843                assert!(reference.call0()?.is(&object));
844                assert!(reference.upgrade_borrowed().is_some());
845                assert!(reference
846                    .upgrade_borrowed()
847                    .map_or(false, |obj| obj.is(&object)));
848
849                drop(object);
850
851                // XXX: have to leak in the borrowed methods for safety :(
852                assert!(!reference.call0()?.is_none());
853                assert!(reference.upgrade_borrowed().is_some());
854
855                Ok(())
856            })
857        }
858
859        #[test]
860        fn test_weakref_get_object() -> PyResult<()> {
861            Python::with_gil(|py| {
862                let class = get_type(py)?;
863                let object = class.call0()?;
864                let reference = PyWeakrefReference::new_bound(&object)?;
865
866                assert!(reference.call0()?.is(&object));
867                assert!(reference.get_object().is(&object));
868
869                drop(object);
870
871                assert!(reference.call0()?.is(&reference.get_object()));
872                assert!(reference.call0()?.is_none());
873                assert!(reference.get_object().is_none());
874
875                Ok(())
876            })
877        }
878
879        #[test]
880        #[allow(deprecated)]
881        fn test_weakref_get_object_borrowed() -> PyResult<()> {
882            Python::with_gil(|py| {
883                let class = get_type(py)?;
884                let object = class.call0()?;
885                let reference = PyWeakrefReference::new_bound(&object)?;
886
887                assert!(reference.call0()?.is(&object));
888                assert!(reference.get_object_borrowed().is(&object));
889
890                drop(object);
891
892                // XXX: have to leak in the borrowed methods for safety :(
893                assert!(!reference.call0()?.is_none());
894                assert!(!reference.get_object_borrowed().is_none());
895
896                Ok(())
897            })
898        }
899    }
900
901    // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable.
902    #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))]
903    mod pyo3_pyclass {
904        use super::*;
905        use crate::{pyclass, Py};
906
907        #[pyclass(weakref, crate = "crate")]
908        struct WeakrefablePyClass {}
909
910        #[test]
911        fn test_weakref_reference_behavior() -> PyResult<()> {
912            Python::with_gil(|py| {
913                let object: Bound<'_, WeakrefablePyClass> = Bound::new(py, WeakrefablePyClass {})?;
914                let reference = PyWeakrefReference::new_bound(&object)?;
915
916                assert!(!reference.is(&object));
917                assert!(reference.get_object().is(&object));
918                #[cfg(not(Py_LIMITED_API))]
919                assert_eq!(reference.get_type().to_string(), CLASS_NAME);
920
921                #[cfg(not(Py_LIMITED_API))]
922                assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME);
923                #[cfg(not(Py_LIMITED_API))]
924                check_repr(&reference, Some((object.as_any(), "WeakrefablePyClass")))?;
925
926                assert!(reference
927                    .getattr("__callback__")
928                    .map_or(false, |result| result.is_none()));
929
930                assert!(reference.call0()?.is(&object));
931
932                drop(object);
933
934                assert!(reference.get_object().is_none());
935                #[cfg(not(Py_LIMITED_API))]
936                assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME);
937                check_repr(&reference, None)?;
938
939                assert!(reference
940                    .getattr("__callback__")
941                    .map_or(false, |result| result.is_none()));
942
943                assert!(reference.call0()?.is_none());
944
945                Ok(())
946            })
947        }
948
949        #[test]
950        fn test_weakref_upgrade_as() -> PyResult<()> {
951            Python::with_gil(|py| {
952                let object = Py::new(py, WeakrefablePyClass {})?;
953                let reference = PyWeakrefReference::new_bound(object.bind(py))?;
954
955                {
956                    let obj = reference.upgrade_as::<WeakrefablePyClass>();
957
958                    assert!(obj.is_ok());
959                    let obj = obj.unwrap();
960
961                    assert!(obj.is_some());
962                    assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
963                }
964
965                drop(object);
966
967                {
968                    let obj = reference.upgrade_as::<WeakrefablePyClass>();
969
970                    assert!(obj.is_ok());
971                    let obj = obj.unwrap();
972
973                    assert!(obj.is_none());
974                }
975
976                Ok(())
977            })
978        }
979
980        #[test]
981        #[allow(deprecated)]
982        fn test_weakref_upgrade_borrowed_as() -> PyResult<()> {
983            Python::with_gil(|py| {
984                let object = Py::new(py, WeakrefablePyClass {})?;
985                let reference = PyWeakrefReference::new_bound(object.bind(py))?;
986
987                {
988                    let obj = reference.upgrade_borrowed_as::<WeakrefablePyClass>();
989
990                    assert!(obj.is_ok());
991                    let obj = obj.unwrap();
992
993                    assert!(obj.is_some());
994                    assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
995                }
996
997                drop(object);
998
999                {
1000                    let obj = reference.upgrade_borrowed_as::<WeakrefablePyClass>();
1001
1002                    assert!(obj.is_ok());
1003                    let obj = obj.unwrap();
1004
1005                    // XXX: have to leak in the borrowed methods for safety :(
1006                    assert!(obj.is_some());
1007                }
1008
1009                Ok(())
1010            })
1011        }
1012
1013        #[test]
1014        fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
1015            Python::with_gil(|py| {
1016                let object = Py::new(py, WeakrefablePyClass {})?;
1017                let reference = PyWeakrefReference::new_bound(object.bind(py))?;
1018
1019                {
1020                    let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
1021
1022                    assert!(obj.is_some());
1023                    assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1024                }
1025
1026                drop(object);
1027
1028                {
1029                    let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
1030
1031                    assert!(obj.is_none());
1032                }
1033
1034                Ok(())
1035            })
1036        }
1037
1038        #[test]
1039        #[allow(deprecated)]
1040        fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> {
1041            Python::with_gil(|py| {
1042                let object = Py::new(py, WeakrefablePyClass {})?;
1043                let reference = PyWeakrefReference::new_bound(object.bind(py))?;
1044
1045                {
1046                    let obj =
1047                        unsafe { reference.upgrade_borrowed_as_unchecked::<WeakrefablePyClass>() };
1048
1049                    assert!(obj.is_some());
1050                    assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1051                }
1052
1053                drop(object);
1054
1055                {
1056                    let obj =
1057                        unsafe { reference.upgrade_borrowed_as_unchecked::<WeakrefablePyClass>() };
1058
1059                    // XXX: have to leak in the borrowed methods for safety :(
1060                    assert!(obj.is_some());
1061                }
1062
1063                Ok(())
1064            })
1065        }
1066
1067        #[test]
1068        fn test_weakref_upgrade() -> PyResult<()> {
1069            Python::with_gil(|py| {
1070                let object = Py::new(py, WeakrefablePyClass {})?;
1071                let reference = PyWeakrefReference::new_bound(object.bind(py))?;
1072
1073                assert!(reference.call0()?.is(&object));
1074                assert!(reference.upgrade().is_some());
1075                assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
1076
1077                drop(object);
1078
1079                assert!(reference.call0()?.is_none());
1080                assert!(reference.upgrade().is_none());
1081
1082                Ok(())
1083            })
1084        }
1085
1086        #[test]
1087        #[allow(deprecated)]
1088        fn test_weakref_upgrade_borrowed() -> PyResult<()> {
1089            Python::with_gil(|py| {
1090                let object = Py::new(py, WeakrefablePyClass {})?;
1091                let reference = PyWeakrefReference::new_bound(object.bind(py))?;
1092
1093                assert!(reference.call0()?.is(&object));
1094                assert!(reference.upgrade_borrowed().is_some());
1095                assert!(reference
1096                    .upgrade_borrowed()
1097                    .map_or(false, |obj| obj.is(&object)));
1098
1099                drop(object);
1100
1101                // XXX: have to leak in the borrowed methods for safety :(
1102                assert!(!reference.call0()?.is_none());
1103                assert!(reference.upgrade_borrowed().is_some());
1104
1105                Ok(())
1106            })
1107        }
1108
1109        #[test]
1110        fn test_weakref_get_object() -> PyResult<()> {
1111            Python::with_gil(|py| {
1112                let object = Py::new(py, WeakrefablePyClass {})?;
1113                let reference = PyWeakrefReference::new_bound(object.bind(py))?;
1114
1115                assert!(reference.call0()?.is(&object));
1116                assert!(reference.get_object().is(&object));
1117
1118                drop(object);
1119
1120                assert!(reference.call0()?.is(&reference.get_object()));
1121                assert!(reference.call0()?.is_none());
1122                assert!(reference.get_object().is_none());
1123
1124                Ok(())
1125            })
1126        }
1127
1128        #[test]
1129        #[allow(deprecated)]
1130        fn test_weakref_get_object_borrowed() -> PyResult<()> {
1131            Python::with_gil(|py| {
1132                let object = Py::new(py, WeakrefablePyClass {})?;
1133                let reference = PyWeakrefReference::new_bound(object.bind(py))?;
1134
1135                assert!(reference.call0()?.is(&object));
1136                assert!(reference.get_object_borrowed().is(&object));
1137
1138                drop(object);
1139
1140                // XXX: have to leak in the borrowed methods for safety :(
1141                assert!(!reference.call0()?.is_none());
1142                assert!(!reference.get_object_borrowed().is_none());
1143
1144                Ok(())
1145            })
1146        }
1147    }
1148}