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}