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