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