pyo3/types/weakref/
proxy.rs

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, PyNone};
6use crate::{ffi, Borrowed, Bound, ToPyObject};
7
8#[cfg(feature = "gil-refs")]
9use crate::{type_object::PyTypeInfo, PyNativeType};
10
11use super::PyWeakrefMethods;
12
13/// Represents any Python `weakref` Proxy type.
14///
15/// In Python this is created by calling `weakref.proxy`.
16/// This is either a `weakref.ProxyType` or a `weakref.CallableProxyType` (`weakref.ProxyTypes`).
17#[repr(transparent)]
18pub struct PyWeakrefProxy(PyAny);
19
20pyobject_native_type_named!(PyWeakrefProxy);
21pyobject_native_type_extract!(PyWeakrefProxy);
22
23// TODO: We known the layout but this cannot be implemented, due to the lack of public typeobject pointers. And it is 2 distinct types
24// #[cfg(not(Py_LIMITED_API))]
25// pyobject_native_type_sized!(PyWeakrefProxy, ffi::PyWeakReference);
26
27impl PyTypeCheck for PyWeakrefProxy {
28    const NAME: &'static str = "weakref.ProxyTypes";
29
30    fn type_check(object: &Bound<'_, PyAny>) -> bool {
31        unsafe { ffi::PyWeakref_CheckProxy(object.as_ptr()) > 0 }
32    }
33}
34
35/// TODO: UPDATE DOCS
36impl PyWeakrefProxy {
37    /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`/`weakref.CallableProxyType`) for the given object.
38    ///
39    /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag).
40    ///
41    /// # Examples
42    #[cfg_attr(
43        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
44        doc = "```rust,ignore"
45    )]
46    #[cfg_attr(
47        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
48        doc = "```rust"
49    )]
50    /// use pyo3::prelude::*;
51    /// use pyo3::types::PyWeakrefProxy;
52    ///
53    /// #[pyclass(weakref)]
54    /// struct Foo { /* fields omitted */ }
55    ///
56    /// # fn main() -> PyResult<()> {
57    /// Python::with_gil(|py| {
58    ///     let foo = Bound::new(py, Foo {})?;
59    ///     let weakref = PyWeakrefProxy::new_bound(&foo)?;
60    ///     assert!(
61    ///         // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::<Foo>`
62    ///         weakref.upgrade()
63    ///             .map_or(false, |obj| obj.is(&foo))
64    ///     );
65    ///
66    ///     let weakref2 = PyWeakrefProxy::new_bound(&foo)?;
67    ///     assert!(weakref.is(&weakref2));
68    ///
69    ///     drop(foo);
70    ///
71    ///     assert!(weakref.upgrade().is_none());
72    ///     Ok(())
73    /// })
74    /// # }
75    /// ```
76    #[inline]
77    pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyWeakrefProxy>> {
78        // TODO: Is this inner pattern still necessary Here?
79        fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyWeakrefProxy>> {
80            unsafe {
81                Bound::from_owned_ptr_or_err(
82                    object.py(),
83                    ffi::PyWeakref_NewProxy(object.as_ptr(), ffi::Py_None()),
84                )
85                .downcast_into_unchecked()
86            }
87        }
88
89        inner(object)
90    }
91
92    /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`/`weakref.CallableProxyType`) for the given object with a callback.
93    ///
94    /// 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.
95    ///
96    /// # Examples
97    #[cfg_attr(
98        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
99        doc = "```rust,ignore"
100    )]
101    #[cfg_attr(
102        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
103        doc = "```rust"
104    )]
105    /// use pyo3::prelude::*;
106    /// use pyo3::types::PyWeakrefProxy;
107    ///
108    /// #[pyclass(weakref)]
109    /// struct Foo { /* fields omitted */ }
110    ///
111    /// #[pyfunction]
112    /// fn callback(wref: Bound<'_, PyWeakrefProxy>) -> PyResult<()> {
113    ///         let py = wref.py();
114    ///         assert!(wref.upgrade_as::<Foo>()?.is_none());
115    ///         py.run_bound("counter = 1", None, None)
116    /// }
117    ///
118    /// # fn main() -> PyResult<()> {
119    /// Python::with_gil(|py| {
120    ///     py.run_bound("counter = 0", None, None)?;
121    ///     assert_eq!(py.eval_bound("counter", None, None)?.extract::<u32>()?, 0);
122    ///     let foo = Bound::new(py, Foo{})?;
123    ///
124    ///     // This is fine.
125    ///     let weakref = PyWeakrefProxy::new_bound_with(&foo, py.None())?;
126    ///     assert!(weakref.upgrade_as::<Foo>()?.is_some());
127    ///     assert!(
128    ///         // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::<Foo>`
129    ///         weakref.upgrade()
130    ///             .map_or(false, |obj| obj.is(&foo))
131    ///     );
132    ///     assert_eq!(py.eval_bound("counter", None, None)?.extract::<u32>()?, 0);
133    ///
134    ///     let weakref2 = PyWeakrefProxy::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?;
135    ///     assert!(!weakref.is(&weakref2)); // Not the same weakref
136    ///     assert!(weakref.eq(&weakref2)?);  // But Equal, since they point to the same object
137    ///
138    ///     drop(foo);
139    ///
140    ///     assert!(weakref.upgrade_as::<Foo>()?.is_none());
141    ///     assert_eq!(py.eval_bound("counter", None, None)?.extract::<u32>()?, 1);
142    ///     Ok(())
143    /// })
144    /// # }
145    /// ```
146    #[inline]
147    pub fn new_bound_with<'py, C>(
148        object: &Bound<'py, PyAny>,
149        callback: C,
150    ) -> PyResult<Bound<'py, PyWeakrefProxy>>
151    where
152        C: ToPyObject,
153    {
154        fn inner<'py>(
155            object: &Bound<'py, PyAny>,
156            callback: Bound<'py, PyAny>,
157        ) -> PyResult<Bound<'py, PyWeakrefProxy>> {
158            unsafe {
159                Bound::from_owned_ptr_or_err(
160                    object.py(),
161                    ffi::PyWeakref_NewProxy(object.as_ptr(), callback.as_ptr()),
162                )
163                .downcast_into_unchecked()
164            }
165        }
166
167        let py = object.py();
168        inner(object, callback.to_object(py).into_bound(py))
169    }
170}
171
172/// TODO: UPDATE DOCS
173#[cfg(feature = "gil-refs")]
174impl PyWeakrefProxy {
175    /// Deprecated form of [`PyWeakrefProxy::new_bound`].
176    #[inline]
177    #[deprecated(
178        since = "0.21.0",
179        note = "`PyWeakrefProxy::new` will be replaced by `PyWeakrefProxy::new_bound` in a future PyO3 version"
180    )]
181    pub fn new<T>(object: &T) -> PyResult<&PyWeakrefProxy>
182    where
183        T: PyNativeType,
184    {
185        Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref)
186    }
187
188    /// Deprecated form of [`PyWeakrefProxy::new_bound_with`].
189    #[inline]
190    #[deprecated(
191        since = "0.21.0",
192        note = "`PyWeakrefProxy::new_with` will be replaced by `PyWeakrefProxy::new_bound_with` in a future PyO3 version"
193    )]
194    pub fn new_with<T, C>(object: &T, callback: C) -> PyResult<&PyWeakrefProxy>
195    where
196        T: PyNativeType,
197        C: ToPyObject,
198    {
199        Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref)
200    }
201
202    /// Upgrade the weakref to a direct object reference.
203    ///
204    /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
205    /// In Python it would be equivalent to [`PyWeakref_GetObject`].
206    ///
207    /// # Example
208    #[cfg_attr(
209        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
210        doc = "```rust,ignore"
211    )]
212    #[cfg_attr(
213        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
214        doc = "```rust"
215    )]
216    /// use pyo3::prelude::*;
217    /// use pyo3::types::PyWeakrefProxy;
218    ///
219    /// #[pyclass(weakref)]
220    /// struct Foo { /* fields omitted */ }
221    ///
222    /// #[pymethods]
223    /// impl Foo {
224    ///     fn get_data(&self) -> (&str, u32) {
225    ///         ("Dave", 10)
226    ///     }
227    /// }
228    ///
229    /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> PyResult<String> {
230    ///     if let Some(data_src) = reference.upgrade_as::<Foo>()? {
231    ///         let data = data_src.borrow();
232    ///         let (name, score) = data.get_data();
233    ///         Ok(format!("Processing '{}': score = {}", name, score))
234    ///     } else {
235    ///         Ok("The supplied data reference is nolonger relavent.".to_owned())
236    ///     }
237    /// }
238    ///
239    /// # fn main() -> PyResult<()> {
240    /// Python::with_gil(|py| {
241    ///     let data = Bound::new(py, Foo{})?;
242    ///     let reference = PyWeakrefProxy::new_bound(&data)?;
243    ///
244    ///     assert_eq!(
245    ///         parse_data(reference.as_borrowed())?,
246    ///         "Processing 'Dave': score = 10"
247    ///     );
248    ///
249    ///     drop(data);
250    ///
251    ///     assert_eq!(
252    ///         parse_data(reference.as_borrowed())?,
253    ///         "The supplied data reference is nolonger relavent."
254    ///     );
255    ///
256    ///     Ok(())
257    /// })
258    /// # }
259    /// ```
260    ///
261    /// # Panics
262    /// This function panics is the current object is invalid.
263    /// If used propperly this is never the case. (NonNull and actually a weakref type)
264    ///
265    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
266    /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType
267    /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy
268    pub fn upgrade_as<T>(&self) -> PyResult<Option<&T::AsRefTarget>>
269    where
270        T: PyTypeCheck,
271    {
272        Ok(self
273            .as_borrowed()
274            .upgrade_as::<T>()?
275            .map(Bound::into_gil_ref))
276    }
277
278    /// 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`.
279    ///
280    /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
281    /// In Python it would be equivalent to [`PyWeakref_GetObject`].
282    ///
283    /// # Safety
284    /// Callers must ensure that the type is valid or risk type confusion.
285    /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up.
286    ///
287    /// # Example
288    #[cfg_attr(
289        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
290        doc = "```rust,ignore"
291    )]
292    #[cfg_attr(
293        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
294        doc = "```rust"
295    )]
296    /// use pyo3::prelude::*;
297    /// use pyo3::types::PyWeakrefProxy;
298    ///
299    /// #[pyclass(weakref)]
300    /// struct Foo { /* fields omitted */ }
301    ///
302    /// #[pymethods]
303    /// impl Foo {
304    ///     fn get_data(&self) -> (&str, u32) {
305    ///         ("Dave", 10)
306    ///     }
307    /// }
308    ///
309    /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> String {
310    ///     if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::<Foo>() } {
311    ///         let data = data_src.borrow();
312    ///         let (name, score) = data.get_data();
313    ///         format!("Processing '{}': score = {}", name, score)
314    ///     } else {
315    ///         "The supplied data reference is nolonger relavent.".to_owned()
316    ///     }
317    /// }
318    ///
319    /// # fn main() -> PyResult<()> {
320    /// Python::with_gil(|py| {
321    ///     let data = Bound::new(py, Foo{})?;
322    ///     let reference = PyWeakrefProxy::new_bound(&data)?;
323    ///
324    ///     assert_eq!(
325    ///         parse_data(reference.as_borrowed()),
326    ///         "Processing 'Dave': score = 10"
327    ///     );
328    ///
329    ///     drop(data);
330    ///
331    ///     assert_eq!(
332    ///         parse_data(reference.as_borrowed()),
333    ///         "The supplied data reference is nolonger relavent."
334    ///     );
335    ///
336    ///     Ok(())
337    /// })
338    /// # }
339    /// ```
340    ///
341    /// # Panics
342    /// This function panics is the current object is invalid.
343    /// If used propperly this is never the case. (NonNull and actually a weakref type)
344    ///
345    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
346    /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType
347    /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy
348    pub unsafe fn upgrade_as_unchecked<T>(&self) -> Option<&T::AsRefTarget>
349    where
350        T: PyTypeCheck,
351    {
352        self.as_borrowed()
353            .upgrade_as_unchecked::<T>()
354            .map(Bound::into_gil_ref)
355    }
356
357    /// Upgrade the weakref to an exact direct object reference.
358    ///
359    /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
360    /// In Python it would be equivalent to [`PyWeakref_GetObject`].
361    ///
362    /// # Example
363    #[cfg_attr(
364        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
365        doc = "```rust,ignore"
366    )]
367    #[cfg_attr(
368        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
369        doc = "```rust"
370    )]
371    /// use pyo3::prelude::*;
372    /// use pyo3::types::PyWeakrefProxy;
373    ///
374    /// #[pyclass(weakref)]
375    /// struct Foo { /* fields omitted */ }
376    ///
377    /// #[pymethods]
378    /// impl Foo {
379    ///     fn get_data(&self) -> (&str, u32) {
380    ///         ("Dave", 10)
381    ///     }
382    /// }
383    ///
384    /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> PyResult<String> {
385    ///     if let Some(data_src) = reference.upgrade_as_exact::<Foo>()? {
386    ///         let data = data_src.borrow();
387    ///         let (name, score) = data.get_data();
388    ///         Ok(format!("Processing '{}': score = {}", name, score))
389    ///     } else {
390    ///         Ok("The supplied data reference is nolonger relavent.".to_owned())
391    ///     }
392    /// }
393    ///
394    /// # fn main() -> PyResult<()> {
395    /// Python::with_gil(|py| {
396    ///     let data = Bound::new(py, Foo{})?;
397    ///     let reference = PyWeakrefProxy::new_bound(&data)?;
398    ///
399    ///     assert_eq!(
400    ///         parse_data(reference.as_borrowed())?,
401    ///         "Processing 'Dave': score = 10"
402    ///     );
403    ///
404    ///     drop(data);
405    ///
406    ///     assert_eq!(
407    ///         parse_data(reference.as_borrowed())?,
408    ///         "The supplied data reference is nolonger relavent."
409    ///     );
410    ///
411    ///     Ok(())
412    /// })
413    /// # }
414    /// ```
415    ///
416    /// # Panics
417    /// This function panics is the current object is invalid.
418    /// If used propperly this is never the case. (NonNull and actually a weakref type)
419    ///
420    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
421    /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType
422    /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy
423    pub fn upgrade_as_exact<T>(&self) -> PyResult<Option<&T::AsRefTarget>>
424    where
425        T: PyTypeInfo,
426    {
427        Ok(self
428            .as_borrowed()
429            .upgrade_as_exact::<T>()?
430            .map(Bound::into_gil_ref))
431    }
432
433    /// Upgrade the weakref to a [`PyAny`] reference to the target if possible.
434    ///
435    /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade).
436    /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned.
437    ///
438    /// This function gets the optional target of this [`weakref.ProxyType`] (or [`weakref.CallableProxyType`], result of calling [`weakref.proxy`]).
439    /// It produces similair results using [`PyWeakref_GetObject`] in the C api.
440    ///
441    /// # Example
442    #[cfg_attr(
443        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
444        doc = "```rust,ignore"
445    )]
446    #[cfg_attr(
447        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
448        doc = "```rust"
449    )]
450    /// use pyo3::prelude::*;
451    /// use pyo3::types::PyWeakrefProxy;
452    ///
453    /// #[pyclass(weakref)]
454    /// struct Foo { /* fields omitted */ }
455    ///
456    /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> PyResult<String> {
457    ///     if let Some(object) = reference.upgrade() {
458    ///         Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?))
459    ///     } else {
460    ///         Ok("The object, which this reference refered to, no longer exists".to_owned())
461    ///     }
462    /// }
463    ///
464    /// # fn main() -> PyResult<()> {
465    /// Python::with_gil(|py| {
466    ///     let data = Bound::new(py, Foo{})?;
467    ///     let reference = PyWeakrefProxy::new_bound(&data)?;
468    ///
469    ///     assert_eq!(
470    ///         parse_data(reference.as_borrowed())?,
471    ///         "The object 'Foo' refered by this reference still exists."
472    ///     );
473    ///
474    ///     drop(data);
475    ///
476    ///     assert_eq!(
477    ///         parse_data(reference.as_borrowed())?,
478    ///         "The object, which this reference refered to, no longer exists"
479    ///     );
480    ///
481    ///     Ok(())
482    /// })
483    /// # }
484    /// ```
485    ///
486    /// # Panics
487    /// This function panics is the current object is invalid.
488    /// If used propperly this is never the case. (NonNull and actually a weakref type)
489    ///
490    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
491    /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType
492    /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType
493    /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy
494    pub fn upgrade(&self) -> Option<&'_ PyAny> {
495        self.as_borrowed().upgrade().map(Bound::into_gil_ref)
496    }
497
498    /// Retrieve to a object pointed to by the weakref.
499    ///
500    /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone).
501    ///
502    /// This function gets the optional target of this [`weakref.ProxyType`] (or [`weakref.CallableProxyType`], result of calling [`weakref.proxy`]).
503    /// It produces similair results using [`PyWeakref_GetObject`] in the C api.
504    ///
505    /// # Example
506    #[cfg_attr(
507        not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))),
508        doc = "```rust,ignore"
509    )]
510    #[cfg_attr(
511        all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))),
512        doc = "```rust"
513    )]
514    /// use pyo3::prelude::*;
515    /// use pyo3::types::PyWeakrefProxy;
516    ///
517    /// #[pyclass(weakref)]
518    /// struct Foo { /* fields omitted */ }
519    ///
520    /// fn get_class(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> PyResult<String> {
521    ///     reference
522    ///         .get_object()
523    ///         .getattr("__class__")?
524    ///         .repr()
525    ///         .map(|repr| repr.to_string())
526    /// }
527    ///
528    /// # fn main() -> PyResult<()> {
529    /// Python::with_gil(|py| {
530    ///     let object = Bound::new(py, Foo{})?;
531    ///     let reference = PyWeakrefProxy::new_bound(&object)?;
532    ///
533    ///     assert_eq!(
534    ///         get_class(reference.as_borrowed())?,
535    ///         "<class 'builtins.Foo'>"
536    ///     );
537    ///
538    ///     drop(object);
539    ///
540    ///     assert_eq!(get_class(reference.as_borrowed())?, "<class 'NoneType'>");
541    ///
542    ///     Ok(())
543    /// })
544    /// # }
545    /// ```
546    ///
547    /// # Panics
548    /// This function panics is the current object is invalid.
549    /// If used propperly this is never the case. (NonNull and actually a weakref type)
550    ///
551    /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject
552    /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType
553    /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType
554    /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy
555    pub fn get_object(&self) -> &'_ PyAny {
556        self.as_borrowed().get_object().into_gil_ref()
557    }
558}
559
560impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakrefProxy> {
561    fn get_object(&self) -> Bound<'py, PyAny> {
562        let mut obj: *mut ffi::PyObject = std::ptr::null_mut();
563        match unsafe { ffi::compat::PyWeakref_GetRef(self.as_ptr(), &mut obj) } {
564            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)"),
565            0 => PyNone::get_bound(self.py()).to_owned().into_any(),
566            1..=std::os::raw::c_int::MAX => unsafe { obj.assume_owned(self.py()) },
567        }
568    }
569
570    fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> {
571        // XXX: this deliberately leaks a reference, but this is a necessary safety measure
572        // to ensure that the object is not deallocated while we are using it.
573        unsafe {
574            self.get_object()
575                .into_ptr()
576                .assume_borrowed_unchecked(self.py())
577        }
578    }
579}
580
581#[cfg(test)]
582mod tests {
583    use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError};
584    use crate::types::any::{PyAny, PyAnyMethods};
585    use crate::types::weakref::{PyWeakrefMethods, PyWeakrefProxy};
586    use crate::{Bound, PyResult, Python};
587
588    #[cfg(all(Py_3_13, not(Py_LIMITED_API)))]
589    const DEADREF_FIX: Option<&str> = None;
590    #[cfg(all(not(Py_3_13), not(Py_LIMITED_API)))]
591    const DEADREF_FIX: Option<&str> = Some("NoneType");
592
593    #[cfg(not(Py_LIMITED_API))]
594    fn check_repr(
595        reference: &Bound<'_, PyWeakrefProxy>,
596        object: &Bound<'_, PyAny>,
597        class: Option<&str>,
598    ) -> PyResult<()> {
599        let repr = reference.repr()?.to_string();
600
601        #[cfg(Py_3_13)]
602        let (first_part, second_part) = repr.split_once(';').unwrap();
603        #[cfg(not(Py_3_13))]
604        let (first_part, second_part) = repr.split_once(" to ").unwrap();
605
606        {
607            let (msg, addr) = first_part.split_once("0x").unwrap();
608
609            assert_eq!(msg, "<weakproxy at ");
610            assert!(addr
611                .to_lowercase()
612                .contains(format!("{:x?}", reference.as_ptr()).split_at(2).1));
613        }
614
615        if let Some(class) = class.or(DEADREF_FIX) {
616            let (msg, addr) = second_part.split_once("0x").unwrap();
617
618            // Avoids not succeeding at unreliable quotation (Python 3.13-dev adds ' around classname without documenting)
619            #[cfg(Py_3_13)]
620            assert!(msg.starts_with(" to '"));
621            assert!(msg.contains(class));
622            assert!(msg.ends_with(" at "));
623
624            assert!(addr
625                .to_lowercase()
626                .contains(format!("{:x?}", object.as_ptr()).split_at(2).1));
627        } else {
628            assert!(second_part.contains("dead"));
629        }
630
631        Ok(())
632    }
633
634    mod proxy {
635        use super::*;
636
637        #[cfg(all(not(Py_LIMITED_API), Py_3_10))]
638        const CLASS_NAME: &str = "'weakref.ProxyType'";
639        #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
640        const CLASS_NAME: &str = "'weakproxy'";
641
642        mod python_class {
643            use super::*;
644            use crate::{py_result_ext::PyResultExt, types::PyType};
645
646            fn get_type(py: Python<'_>) -> PyResult<Bound<'_, PyType>> {
647                py.run_bound("class A:\n    pass\n", None, None)?;
648                py.eval_bound("A", None, None).downcast_into::<PyType>()
649            }
650
651            #[test]
652            fn test_weakref_proxy_behavior() -> PyResult<()> {
653                Python::with_gil(|py| {
654                    let class = get_type(py)?;
655                    let object = class.call0()?;
656                    let reference = PyWeakrefProxy::new_bound(&object)?;
657
658                    assert!(!reference.is(&object));
659                    assert!(reference.get_object().is(&object));
660
661                    #[cfg(not(Py_LIMITED_API))]
662                    assert_eq!(
663                        reference.get_type().to_string(),
664                        format!("<class {}>", CLASS_NAME)
665                    );
666
667                    assert_eq!(
668                        reference.getattr("__class__")?.to_string(),
669                        "<class '__main__.A'>"
670                    );
671                    #[cfg(not(Py_LIMITED_API))]
672                    check_repr(&reference, &object, Some("A"))?;
673
674                    assert!(reference
675                        .getattr("__callback__")
676                        .err()
677                        .map_or(false, |err| err.is_instance_of::<PyAttributeError>(py)));
678
679                    assert!(reference.call0().err().map_or(false, |err| {
680                        let result = err.is_instance_of::<PyTypeError>(py);
681                        #[cfg(not(Py_LIMITED_API))]
682                        let result = result
683                            & (err.value_bound(py).to_string()
684                                == format!("{} object is not callable", CLASS_NAME));
685                        result
686                    }));
687
688                    drop(object);
689
690                    assert!(reference.get_object().is_none());
691                    assert!(reference
692                        .getattr("__class__")
693                        .err()
694                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)));
695                    #[cfg(not(Py_LIMITED_API))]
696                    check_repr(&reference, py.None().bind(py), None)?;
697
698                    assert!(reference
699                        .getattr("__callback__")
700                        .err()
701                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)));
702
703                    assert!(reference.call0().err().map_or(false, |err| {
704                        let result = err.is_instance_of::<PyTypeError>(py);
705                        #[cfg(not(Py_LIMITED_API))]
706                        let result = result
707                            & (err.value_bound(py).to_string()
708                                == format!("{} object is not callable", CLASS_NAME));
709                        result
710                    }));
711
712                    Ok(())
713                })
714            }
715
716            #[test]
717            fn test_weakref_upgrade_as() -> PyResult<()> {
718                Python::with_gil(|py| {
719                    let class = get_type(py)?;
720                    let object = class.call0()?;
721                    let reference = PyWeakrefProxy::new_bound(&object)?;
722
723                    {
724                        // This test is a bit weird but ok.
725                        let obj = reference.upgrade_as::<PyAny>();
726
727                        assert!(obj.is_ok());
728                        let obj = obj.unwrap();
729
730                        assert!(obj.is_some());
731                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
732                            && obj.is_exact_instance(&class)));
733                    }
734
735                    drop(object);
736
737                    {
738                        // This test is a bit weird but ok.
739                        let obj = reference.upgrade_as::<PyAny>();
740
741                        assert!(obj.is_ok());
742                        let obj = obj.unwrap();
743
744                        assert!(obj.is_none());
745                    }
746
747                    Ok(())
748                })
749            }
750
751            #[test]
752            fn test_weakref_upgrade_borrowed_as() -> PyResult<()> {
753                Python::with_gil(|py| {
754                    let class = get_type(py)?;
755                    let object = class.call0()?;
756                    let reference = PyWeakrefProxy::new_bound(&object)?;
757
758                    {
759                        // This test is a bit weird but ok.
760                        #[allow(deprecated)]
761                        let obj = reference.upgrade_borrowed_as::<PyAny>();
762
763                        assert!(obj.is_ok());
764                        let obj = obj.unwrap();
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                        #[allow(deprecated)]
776                        let obj = reference.upgrade_borrowed_as::<PyAny>();
777
778                        assert!(obj.is_ok());
779                        let obj = obj.unwrap();
780
781                        // XXX: have to leak in the borrowed methods for safety :(
782                        assert!(obj.is_some());
783                    }
784
785                    Ok(())
786                })
787            }
788
789            #[test]
790            fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
791                Python::with_gil(|py| {
792                    let class = get_type(py)?;
793                    let object = class.call0()?;
794                    let reference = PyWeakrefProxy::new_bound(&object)?;
795
796                    {
797                        // This test is a bit weird but ok.
798                        let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
799
800                        assert!(obj.is_some());
801                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
802                            && obj.is_exact_instance(&class)));
803                    }
804
805                    drop(object);
806
807                    {
808                        // This test is a bit weird but ok.
809                        let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
810
811                        assert!(obj.is_none());
812                    }
813
814                    Ok(())
815                })
816            }
817
818            #[test]
819            fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> {
820                Python::with_gil(|py| {
821                    let class = get_type(py)?;
822                    let object = class.call0()?;
823                    let reference = PyWeakrefProxy::new_bound(&object)?;
824
825                    {
826                        // This test is a bit weird but ok.
827                        #[allow(deprecated)]
828                        let obj = unsafe { reference.upgrade_borrowed_as_unchecked::<PyAny>() };
829
830                        assert!(obj.is_some());
831                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
832                            && obj.is_exact_instance(&class)));
833                    }
834
835                    drop(object);
836
837                    {
838                        // This test is a bit weird but ok.
839                        #[allow(deprecated)]
840                        let obj = unsafe { reference.upgrade_borrowed_as_unchecked::<PyAny>() };
841
842                        // XXX: have to leak in the borrowed methods for safety :(
843                        assert!(obj.is_some());
844                    }
845
846                    Ok(())
847                })
848            }
849
850            #[test]
851            fn test_weakref_upgrade() -> PyResult<()> {
852                Python::with_gil(|py| {
853                    let class = get_type(py)?;
854                    let object = class.call0()?;
855                    let reference = PyWeakrefProxy::new_bound(&object)?;
856
857                    assert!(reference.upgrade().is_some());
858                    assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
859
860                    drop(object);
861
862                    assert!(reference.upgrade().is_none());
863
864                    Ok(())
865                })
866            }
867
868            #[test]
869            #[allow(deprecated)]
870            fn test_weakref_upgrade_borrowed() -> PyResult<()> {
871                Python::with_gil(|py| {
872                    let class = get_type(py)?;
873                    let object = class.call0()?;
874                    let reference = PyWeakrefProxy::new_bound(&object)?;
875
876                    assert!(reference.upgrade_borrowed().is_some());
877                    assert!(reference
878                        .upgrade_borrowed()
879                        .map_or(false, |obj| obj.is(&object)));
880
881                    drop(object);
882
883                    // XXX: have to leak in the borrowed methods for safety :(
884                    assert!(reference.upgrade_borrowed().is_some());
885
886                    Ok(())
887                })
888            }
889
890            #[test]
891            fn test_weakref_get_object() -> PyResult<()> {
892                Python::with_gil(|py| {
893                    let class = get_type(py)?;
894                    let object = class.call0()?;
895                    let reference = PyWeakrefProxy::new_bound(&object)?;
896
897                    assert!(reference.get_object().is(&object));
898
899                    drop(object);
900
901                    assert!(reference.get_object().is_none());
902
903                    Ok(())
904                })
905            }
906
907            #[test]
908            #[allow(deprecated)]
909            fn test_weakref_get_object_borrowed() -> PyResult<()> {
910                Python::with_gil(|py| {
911                    let class = get_type(py)?;
912                    let object = class.call0()?;
913                    let reference = PyWeakrefProxy::new_bound(&object)?;
914
915                    assert!(reference.get_object_borrowed().is(&object));
916
917                    drop(object);
918
919                    // XXX: have to leak in the borrowed methods for safety :(
920                    assert!(!reference.get_object_borrowed().is_none());
921
922                    Ok(())
923                })
924            }
925        }
926
927        // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable.
928        #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))]
929        mod pyo3_pyclass {
930            use super::*;
931            use crate::{pyclass, Py};
932
933            #[pyclass(weakref, crate = "crate")]
934            struct WeakrefablePyClass {}
935
936            #[test]
937            fn test_weakref_proxy_behavior() -> PyResult<()> {
938                Python::with_gil(|py| {
939                    let object: Bound<'_, WeakrefablePyClass> =
940                        Bound::new(py, WeakrefablePyClass {})?;
941                    let reference = PyWeakrefProxy::new_bound(&object)?;
942
943                    assert!(!reference.is(&object));
944                    assert!(reference.get_object().is(&object));
945                    #[cfg(not(Py_LIMITED_API))]
946                    assert_eq!(
947                        reference.get_type().to_string(),
948                        format!("<class {}>", CLASS_NAME)
949                    );
950
951                    assert_eq!(
952                        reference.getattr("__class__")?.to_string(),
953                        "<class 'builtins.WeakrefablePyClass'>"
954                    );
955                    #[cfg(not(Py_LIMITED_API))]
956                    check_repr(&reference, object.as_any(), Some("WeakrefablePyClass"))?;
957
958                    assert!(reference
959                        .getattr("__callback__")
960                        .err()
961                        .map_or(false, |err| err.is_instance_of::<PyAttributeError>(py)));
962
963                    assert!(reference.call0().err().map_or(false, |err| {
964                        let result = err.is_instance_of::<PyTypeError>(py);
965                        #[cfg(not(Py_LIMITED_API))]
966                        let result = result
967                            & (err.value_bound(py).to_string()
968                                == format!("{} object is not callable", CLASS_NAME));
969                        result
970                    }));
971
972                    drop(object);
973
974                    assert!(reference.get_object().is_none());
975                    assert!(reference
976                        .getattr("__class__")
977                        .err()
978                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)));
979                    #[cfg(not(Py_LIMITED_API))]
980                    check_repr(&reference, py.None().bind(py), None)?;
981
982                    assert!(reference
983                        .getattr("__callback__")
984                        .err()
985                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)));
986
987                    assert!(reference.call0().err().map_or(false, |err| {
988                        let result = err.is_instance_of::<PyTypeError>(py);
989                        #[cfg(not(Py_LIMITED_API))]
990                        let result = result
991                            & (err.value_bound(py).to_string()
992                                == format!("{} object is not callable", CLASS_NAME));
993                        result
994                    }));
995
996                    Ok(())
997                })
998            }
999
1000            #[test]
1001            fn test_weakref_upgrade_as() -> PyResult<()> {
1002                Python::with_gil(|py| {
1003                    let object = Py::new(py, WeakrefablePyClass {})?;
1004                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1005
1006                    {
1007                        let obj = reference.upgrade_as::<WeakrefablePyClass>();
1008
1009                        assert!(obj.is_ok());
1010                        let obj = obj.unwrap();
1011
1012                        assert!(obj.is_some());
1013                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1014                    }
1015
1016                    drop(object);
1017
1018                    {
1019                        let obj = reference.upgrade_as::<WeakrefablePyClass>();
1020
1021                        assert!(obj.is_ok());
1022                        let obj = obj.unwrap();
1023
1024                        assert!(obj.is_none());
1025                    }
1026
1027                    Ok(())
1028                })
1029            }
1030
1031            #[test]
1032            #[allow(deprecated)]
1033            fn test_weakref_upgrade_borrowed_as() -> PyResult<()> {
1034                Python::with_gil(|py| {
1035                    let object = Py::new(py, WeakrefablePyClass {})?;
1036                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1037
1038                    {
1039                        let obj = reference.upgrade_borrowed_as::<WeakrefablePyClass>();
1040
1041                        assert!(obj.is_ok());
1042                        let obj = obj.unwrap();
1043
1044                        assert!(obj.is_some());
1045                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1046                    }
1047
1048                    drop(object);
1049
1050                    {
1051                        let obj = reference.upgrade_borrowed_as::<WeakrefablePyClass>();
1052
1053                        assert!(obj.is_ok());
1054                        let obj = obj.unwrap();
1055
1056                        // XXX: have to leak in the borrowed methods for safety :(
1057                        assert!(obj.is_some());
1058                    }
1059
1060                    Ok(())
1061                })
1062            }
1063
1064            #[test]
1065            fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
1066                Python::with_gil(|py| {
1067                    let object = Py::new(py, WeakrefablePyClass {})?;
1068                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1069
1070                    {
1071                        let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
1072
1073                        assert!(obj.is_some());
1074                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1075                    }
1076
1077                    drop(object);
1078
1079                    {
1080                        let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
1081
1082                        assert!(obj.is_none());
1083                    }
1084
1085                    Ok(())
1086                })
1087            }
1088
1089            #[test]
1090            #[allow(deprecated)]
1091            fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> {
1092                Python::with_gil(|py| {
1093                    let object = Py::new(py, WeakrefablePyClass {})?;
1094                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1095
1096                    {
1097                        let obj = unsafe {
1098                            reference.upgrade_borrowed_as_unchecked::<WeakrefablePyClass>()
1099                        };
1100
1101                        assert!(obj.is_some());
1102                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1103                    }
1104
1105                    drop(object);
1106
1107                    {
1108                        let obj = unsafe {
1109                            reference.upgrade_borrowed_as_unchecked::<WeakrefablePyClass>()
1110                        };
1111
1112                        // XXX: have to leak in the borrowed methods for safety :(
1113                        assert!(obj.is_some());
1114                    }
1115
1116                    Ok(())
1117                })
1118            }
1119
1120            #[test]
1121            fn test_weakref_upgrade() -> PyResult<()> {
1122                Python::with_gil(|py| {
1123                    let object = Py::new(py, WeakrefablePyClass {})?;
1124                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1125
1126                    assert!(reference.upgrade().is_some());
1127                    assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
1128
1129                    drop(object);
1130
1131                    assert!(reference.upgrade().is_none());
1132
1133                    Ok(())
1134                })
1135            }
1136
1137            #[test]
1138            #[allow(deprecated)]
1139            fn test_weakref_upgrade_borrowed() -> PyResult<()> {
1140                Python::with_gil(|py| {
1141                    let object = Py::new(py, WeakrefablePyClass {})?;
1142                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1143
1144                    assert!(reference.upgrade_borrowed().is_some());
1145                    assert!(reference
1146                        .upgrade_borrowed()
1147                        .map_or(false, |obj| obj.is(&object)));
1148
1149                    drop(object);
1150
1151                    // XXX: have to leak in the borrowed methods for safety :(
1152                    assert!(reference.upgrade_borrowed().is_some());
1153
1154                    Ok(())
1155                })
1156            }
1157
1158            #[test]
1159            fn test_weakref_get_object() -> PyResult<()> {
1160                Python::with_gil(|py| {
1161                    let object = Py::new(py, WeakrefablePyClass {})?;
1162                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1163
1164                    assert!(reference.get_object().is(&object));
1165
1166                    drop(object);
1167
1168                    assert!(reference.get_object().is_none());
1169
1170                    Ok(())
1171                })
1172            }
1173
1174            #[test]
1175            #[allow(deprecated)]
1176            fn test_weakref_get_object_borrowed() -> PyResult<()> {
1177                Python::with_gil(|py| {
1178                    let object = Py::new(py, WeakrefablePyClass {})?;
1179                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1180
1181                    assert!(reference.get_object_borrowed().is(&object));
1182
1183                    drop(object);
1184
1185                    // XXX: have to leak in the borrowed methods for safety :(
1186                    assert!(!reference.get_object_borrowed().is_none());
1187
1188                    Ok(())
1189                })
1190            }
1191        }
1192    }
1193
1194    mod callable_proxy {
1195        use super::*;
1196
1197        #[cfg(all(not(Py_LIMITED_API), Py_3_10))]
1198        const CLASS_NAME: &str = "<class 'weakref.CallableProxyType'>";
1199        #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))]
1200        const CLASS_NAME: &str = "<class 'weakcallableproxy'>";
1201
1202        mod python_class {
1203            use super::*;
1204            use crate::{py_result_ext::PyResultExt, types::PyType};
1205
1206            fn get_type(py: Python<'_>) -> PyResult<Bound<'_, PyType>> {
1207                py.run_bound(
1208                    "class A:\n    def __call__(self):\n        return 'This class is callable!'\n",
1209                    None,
1210                    None,
1211                )?;
1212                py.eval_bound("A", None, None).downcast_into::<PyType>()
1213            }
1214
1215            #[test]
1216            fn test_weakref_proxy_behavior() -> PyResult<()> {
1217                Python::with_gil(|py| {
1218                    let class = get_type(py)?;
1219                    let object = class.call0()?;
1220                    let reference = PyWeakrefProxy::new_bound(&object)?;
1221
1222                    assert!(!reference.is(&object));
1223                    assert!(reference.get_object().is(&object));
1224                    #[cfg(not(Py_LIMITED_API))]
1225                    assert_eq!(reference.get_type().to_string(), CLASS_NAME);
1226
1227                    assert_eq!(
1228                        reference.getattr("__class__")?.to_string(),
1229                        "<class '__main__.A'>"
1230                    );
1231                    #[cfg(not(Py_LIMITED_API))]
1232                    check_repr(&reference, &object, Some("A"))?;
1233
1234                    assert!(reference
1235                        .getattr("__callback__")
1236                        .err()
1237                        .map_or(false, |err| err.is_instance_of::<PyAttributeError>(py)));
1238
1239                    assert_eq!(reference.call0()?.to_string(), "This class is callable!");
1240
1241                    drop(object);
1242
1243                    assert!(reference.get_object().is_none());
1244                    assert!(reference
1245                        .getattr("__class__")
1246                        .err()
1247                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)));
1248                    #[cfg(not(Py_LIMITED_API))]
1249                    check_repr(&reference, py.None().bind(py), None)?;
1250
1251                    assert!(reference
1252                        .getattr("__callback__")
1253                        .err()
1254                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)));
1255
1256                    assert!(reference
1257                        .call0()
1258                        .err()
1259                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)
1260                            & (err.value_bound(py).to_string()
1261                                == "weakly-referenced object no longer exists")));
1262
1263                    Ok(())
1264                })
1265            }
1266
1267            #[test]
1268            fn test_weakref_upgrade_as() -> PyResult<()> {
1269                Python::with_gil(|py| {
1270                    let class = get_type(py)?;
1271                    let object = class.call0()?;
1272                    let reference = PyWeakrefProxy::new_bound(&object)?;
1273
1274                    {
1275                        // This test is a bit weird but ok.
1276                        let obj = reference.upgrade_as::<PyAny>();
1277
1278                        assert!(obj.is_ok());
1279                        let obj = obj.unwrap();
1280
1281                        assert!(obj.is_some());
1282                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
1283                            && obj.is_exact_instance(&class)));
1284                    }
1285
1286                    drop(object);
1287
1288                    {
1289                        // This test is a bit weird but ok.
1290                        let obj = reference.upgrade_as::<PyAny>();
1291
1292                        assert!(obj.is_ok());
1293                        let obj = obj.unwrap();
1294
1295                        assert!(obj.is_none());
1296                    }
1297
1298                    Ok(())
1299                })
1300            }
1301
1302            #[test]
1303            #[allow(deprecated)]
1304            fn test_weakref_upgrade_borrowed_as() -> PyResult<()> {
1305                Python::with_gil(|py| {
1306                    let class = get_type(py)?;
1307                    let object = class.call0()?;
1308                    let reference = PyWeakrefProxy::new_bound(&object)?;
1309
1310                    {
1311                        // This test is a bit weird but ok.
1312                        let obj = reference.upgrade_borrowed_as::<PyAny>();
1313
1314                        assert!(obj.is_ok());
1315                        let obj = obj.unwrap();
1316
1317                        assert!(obj.is_some());
1318                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
1319                            && obj.is_exact_instance(&class)));
1320                    }
1321
1322                    drop(object);
1323
1324                    {
1325                        // This test is a bit weird but ok.
1326                        let obj = reference.upgrade_borrowed_as::<PyAny>();
1327
1328                        assert!(obj.is_ok());
1329                        let obj = obj.unwrap();
1330
1331                        // XXX: have to leak in the borrowed methods for safety :(
1332                        assert!(obj.is_some());
1333                    }
1334
1335                    Ok(())
1336                })
1337            }
1338
1339            #[test]
1340            fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
1341                Python::with_gil(|py| {
1342                    let class = get_type(py)?;
1343                    let object = class.call0()?;
1344                    let reference = PyWeakrefProxy::new_bound(&object)?;
1345
1346                    {
1347                        // This test is a bit weird but ok.
1348                        let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
1349
1350                        assert!(obj.is_some());
1351                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
1352                            && obj.is_exact_instance(&class)));
1353                    }
1354
1355                    drop(object);
1356
1357                    {
1358                        // This test is a bit weird but ok.
1359                        let obj = unsafe { reference.upgrade_as_unchecked::<PyAny>() };
1360
1361                        assert!(obj.is_none());
1362                    }
1363
1364                    Ok(())
1365                })
1366            }
1367
1368            #[test]
1369            #[allow(deprecated)]
1370            fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> {
1371                Python::with_gil(|py| {
1372                    let class = get_type(py)?;
1373                    let object = class.call0()?;
1374                    let reference = PyWeakrefProxy::new_bound(&object)?;
1375
1376                    {
1377                        // This test is a bit weird but ok.
1378                        let obj = unsafe { reference.upgrade_borrowed_as_unchecked::<PyAny>() };
1379
1380                        assert!(obj.is_some());
1381                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()
1382                            && obj.is_exact_instance(&class)));
1383                    }
1384
1385                    drop(object);
1386
1387                    {
1388                        // This test is a bit weird but ok.
1389                        let obj = unsafe { reference.upgrade_borrowed_as_unchecked::<PyAny>() };
1390
1391                        // XXX: have to leak in the borrowed methods for safety :(
1392                        assert!(obj.is_some());
1393                    }
1394
1395                    Ok(())
1396                })
1397            }
1398
1399            #[test]
1400            fn test_weakref_upgrade() -> PyResult<()> {
1401                Python::with_gil(|py| {
1402                    let class = get_type(py)?;
1403                    let object = class.call0()?;
1404                    let reference = PyWeakrefProxy::new_bound(&object)?;
1405
1406                    assert!(reference.upgrade().is_some());
1407                    assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
1408
1409                    drop(object);
1410
1411                    assert!(reference.upgrade().is_none());
1412
1413                    Ok(())
1414                })
1415            }
1416
1417            #[test]
1418            #[allow(deprecated)]
1419            fn test_weakref_upgrade_borrowed() -> PyResult<()> {
1420                Python::with_gil(|py| {
1421                    let class = get_type(py)?;
1422                    let object = class.call0()?;
1423                    let reference = PyWeakrefProxy::new_bound(&object)?;
1424
1425                    assert!(reference.upgrade_borrowed().is_some());
1426                    assert!(reference
1427                        .upgrade_borrowed()
1428                        .map_or(false, |obj| obj.is(&object)));
1429
1430                    drop(object);
1431
1432                    // XXX: have to leak in the borrowed methods for safety :(
1433                    assert!(reference.upgrade_borrowed().is_some());
1434
1435                    Ok(())
1436                })
1437            }
1438
1439            #[test]
1440            fn test_weakref_get_object() -> PyResult<()> {
1441                Python::with_gil(|py| {
1442                    let class = get_type(py)?;
1443                    let object = class.call0()?;
1444                    let reference = PyWeakrefProxy::new_bound(&object)?;
1445
1446                    assert!(reference.get_object().is(&object));
1447
1448                    drop(object);
1449
1450                    assert!(reference.get_object().is_none());
1451
1452                    Ok(())
1453                })
1454            }
1455
1456            #[test]
1457            #[allow(deprecated)]
1458            fn test_weakref_get_object_borrowed() -> PyResult<()> {
1459                Python::with_gil(|py| {
1460                    let class = get_type(py)?;
1461                    let object = class.call0()?;
1462                    let reference = PyWeakrefProxy::new_bound(&object)?;
1463
1464                    assert!(reference.get_object_borrowed().is(&object));
1465
1466                    drop(object);
1467
1468                    // XXX: have to leak in the borrowed methods for safety :(
1469                    assert!(!reference.get_object_borrowed().is_none());
1470
1471                    Ok(())
1472                })
1473            }
1474        }
1475
1476        // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable.
1477        #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))]
1478        mod pyo3_pyclass {
1479            use super::*;
1480            use crate::{pyclass, pymethods, Py};
1481
1482            #[pyclass(weakref, crate = "crate")]
1483            struct WeakrefablePyClass {}
1484
1485            #[pymethods(crate = "crate")]
1486            impl WeakrefablePyClass {
1487                fn __call__(&self) -> &str {
1488                    "This class is callable!"
1489                }
1490            }
1491
1492            #[test]
1493            fn test_weakref_proxy_behavior() -> PyResult<()> {
1494                Python::with_gil(|py| {
1495                    let object: Bound<'_, WeakrefablePyClass> =
1496                        Bound::new(py, WeakrefablePyClass {})?;
1497                    let reference = PyWeakrefProxy::new_bound(&object)?;
1498
1499                    assert!(!reference.is(&object));
1500                    assert!(reference.get_object().is(&object));
1501                    #[cfg(not(Py_LIMITED_API))]
1502                    assert_eq!(reference.get_type().to_string(), CLASS_NAME);
1503
1504                    assert_eq!(
1505                        reference.getattr("__class__")?.to_string(),
1506                        "<class 'builtins.WeakrefablePyClass'>"
1507                    );
1508                    #[cfg(not(Py_LIMITED_API))]
1509                    check_repr(&reference, object.as_any(), Some("WeakrefablePyClass"))?;
1510
1511                    assert!(reference
1512                        .getattr("__callback__")
1513                        .err()
1514                        .map_or(false, |err| err.is_instance_of::<PyAttributeError>(py)));
1515
1516                    assert_eq!(reference.call0()?.to_string(), "This class is callable!");
1517
1518                    drop(object);
1519
1520                    assert!(reference.get_object().is_none());
1521                    assert!(reference
1522                        .getattr("__class__")
1523                        .err()
1524                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)));
1525                    #[cfg(not(Py_LIMITED_API))]
1526                    check_repr(&reference, py.None().bind(py), None)?;
1527
1528                    assert!(reference
1529                        .getattr("__callback__")
1530                        .err()
1531                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)));
1532
1533                    assert!(reference
1534                        .call0()
1535                        .err()
1536                        .map_or(false, |err| err.is_instance_of::<PyReferenceError>(py)
1537                            & (err.value_bound(py).to_string()
1538                                == "weakly-referenced object no longer exists")));
1539
1540                    Ok(())
1541                })
1542            }
1543
1544            #[test]
1545            fn test_weakref_upgrade_as() -> PyResult<()> {
1546                Python::with_gil(|py| {
1547                    let object = Py::new(py, WeakrefablePyClass {})?;
1548                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1549
1550                    {
1551                        let obj = reference.upgrade_as::<WeakrefablePyClass>();
1552
1553                        assert!(obj.is_ok());
1554                        let obj = obj.unwrap();
1555
1556                        assert!(obj.is_some());
1557                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1558                    }
1559
1560                    drop(object);
1561
1562                    {
1563                        let obj = reference.upgrade_as::<WeakrefablePyClass>();
1564
1565                        assert!(obj.is_ok());
1566                        let obj = obj.unwrap();
1567
1568                        assert!(obj.is_none());
1569                    }
1570
1571                    Ok(())
1572                })
1573            }
1574            #[test]
1575            #[allow(deprecated)]
1576            fn test_weakref_upgrade_borrowed_as() -> PyResult<()> {
1577                Python::with_gil(|py| {
1578                    let object = Py::new(py, WeakrefablePyClass {})?;
1579                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1580
1581                    {
1582                        let obj = reference.upgrade_borrowed_as::<WeakrefablePyClass>();
1583
1584                        assert!(obj.is_ok());
1585                        let obj = obj.unwrap();
1586
1587                        assert!(obj.is_some());
1588                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1589                    }
1590
1591                    drop(object);
1592
1593                    {
1594                        let obj = reference.upgrade_borrowed_as::<WeakrefablePyClass>();
1595
1596                        assert!(obj.is_ok());
1597                        let obj = obj.unwrap();
1598
1599                        // XXX: have to leak in the borrowed methods for safety :(
1600                        assert!(obj.is_some());
1601                    }
1602
1603                    Ok(())
1604                })
1605            }
1606
1607            #[test]
1608            fn test_weakref_upgrade_as_unchecked() -> PyResult<()> {
1609                Python::with_gil(|py| {
1610                    let object = Py::new(py, WeakrefablePyClass {})?;
1611                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1612
1613                    {
1614                        let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
1615
1616                        assert!(obj.is_some());
1617                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1618                    }
1619
1620                    drop(object);
1621
1622                    {
1623                        let obj = unsafe { reference.upgrade_as_unchecked::<WeakrefablePyClass>() };
1624
1625                        assert!(obj.is_none());
1626                    }
1627
1628                    Ok(())
1629                })
1630            }
1631            #[test]
1632            #[allow(deprecated)]
1633            fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> {
1634                Python::with_gil(|py| {
1635                    let object = Py::new(py, WeakrefablePyClass {})?;
1636                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1637
1638                    {
1639                        let obj = unsafe {
1640                            reference.upgrade_borrowed_as_unchecked::<WeakrefablePyClass>()
1641                        };
1642
1643                        assert!(obj.is_some());
1644                        assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr()));
1645                    }
1646
1647                    drop(object);
1648
1649                    {
1650                        let obj = unsafe {
1651                            reference.upgrade_borrowed_as_unchecked::<WeakrefablePyClass>()
1652                        };
1653
1654                        // XXX: have to leak in the borrowed methods for safety :(
1655                        assert!(obj.is_some());
1656                    }
1657
1658                    Ok(())
1659                })
1660            }
1661
1662            #[test]
1663            fn test_weakref_upgrade() -> PyResult<()> {
1664                Python::with_gil(|py| {
1665                    let object = Py::new(py, WeakrefablePyClass {})?;
1666                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1667
1668                    assert!(reference.upgrade().is_some());
1669                    assert!(reference.upgrade().map_or(false, |obj| obj.is(&object)));
1670
1671                    drop(object);
1672
1673                    assert!(reference.upgrade().is_none());
1674
1675                    Ok(())
1676                })
1677            }
1678
1679            #[test]
1680            #[allow(deprecated)]
1681            fn test_weakref_upgrade_borrowed() -> PyResult<()> {
1682                Python::with_gil(|py| {
1683                    let object = Py::new(py, WeakrefablePyClass {})?;
1684                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1685
1686                    assert!(reference.upgrade_borrowed().is_some());
1687                    assert!(reference
1688                        .upgrade_borrowed()
1689                        .map_or(false, |obj| obj.is(&object)));
1690
1691                    drop(object);
1692
1693                    // XXX: have to leak in the borrowed methods for safety :(
1694                    assert!(reference.upgrade_borrowed().is_some());
1695
1696                    Ok(())
1697                })
1698            }
1699
1700            #[test]
1701            fn test_weakref_get_object() -> PyResult<()> {
1702                Python::with_gil(|py| {
1703                    let object = Py::new(py, WeakrefablePyClass {})?;
1704                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1705
1706                    assert!(reference.get_object().is(&object));
1707
1708                    drop(object);
1709
1710                    assert!(reference.get_object().is_none());
1711
1712                    Ok(())
1713                })
1714            }
1715
1716            #[test]
1717            #[allow(deprecated)]
1718            fn test_weakref_get_object_borrowed() -> PyResult<()> {
1719                Python::with_gil(|py| {
1720                    let object = Py::new(py, WeakrefablePyClass {})?;
1721                    let reference = PyWeakrefProxy::new_bound(object.bind(py))?;
1722
1723                    assert!(reference.get_object_borrowed().is(&object));
1724
1725                    drop(object);
1726
1727                    // XXX: have to leak in the borrowed methods for safety :(
1728                    assert!(!reference.get_object_borrowed().is_none());
1729
1730                    Ok(())
1731                })
1732            }
1733        }
1734    }
1735}