From cb75d281e7c9790dfd2c28fd4766395033d6015c Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Thu, 8 Feb 2024 11:54:48 +0100 Subject: [PATCH 01/42] Add vscode folder to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4240d326f71..5004209d241 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ valgrind-python.supp lcov.info netlify_build/ .nox/ +.vscode/ From da6fa2157317bdc25f66a09d3e0874680a558ebf Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Thu, 8 Feb 2024 20:10:12 +0100 Subject: [PATCH 02/42] Initial work on PyWeakRef (weakref.ReferenceType) --- src/prelude.rs | 1 + src/types/mod.rs | 2 + src/types/weakref.rs | 365 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 src/types/weakref.rs diff --git a/src/prelude.rs b/src/prelude.rs index 47951aedcce..c8bb9187873 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -41,3 +41,4 @@ pub use crate::types::set::PySetMethods; pub use crate::types::string::PyStringMethods; pub use crate::types::traceback::PyTracebackMethods; pub use crate::types::tuple::PyTupleMethods; +pub use crate::types::weakref::PyWeakRefMethods; diff --git a/src/types/mod.rs b/src/types/mod.rs index f802348f466..73e11916eb4 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -44,6 +44,7 @@ pub use self::string::{PyString, PyString as PyUnicode}; pub use self::traceback::PyTraceback; pub use self::tuple::PyTuple; pub use self::typeobject::PyType; +pub use self::weakref::PyWeakRef; /// Iteration over Python collections. /// @@ -307,3 +308,4 @@ pub(crate) mod string; pub(crate) mod traceback; pub(crate) mod tuple; mod typeobject; +pub(crate) mod weakref; diff --git a/src/types/weakref.rs b/src/types/weakref.rs new file mode 100644 index 00000000000..37079402e2e --- /dev/null +++ b/src/types/weakref.rs @@ -0,0 +1,365 @@ +use crate::err::PyResult; +use crate::ffi_ptr_ext::FfiPtrExt; +use crate::type_object::PyTypeCheck; +use crate::types::any::PyAnyMethods; +use crate::{ffi, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; + +/// Represents a Python `weakref.ReferenceType`. +/// +/// In Python this is created by calling `weakref.ref`. +#[repr(transparent)] +pub struct PyWeakRef(PyAny); + +pyobject_native_type!( + PyWeakRef, + ffi::PyWeakReference, + pyobject_native_static_type_object!(ffi::_PyWeakref_RefType), + #module=Some("weakref"), + #checkfunction=ffi::PyWeakref_CheckRef +); + +impl PyWeakRef { + /// Deprecated form of [`PyWeakRef::new_bound`]. + #[inline] + #[track_caller] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyWeakRef::new` will be replaced by `PyWeakRef::new_bound` in a future PyO3 version" + ) + )] + pub fn new(py: Python<'_>, object: T) -> PyResult<&'_ PyWeakRef> + where + T: ToPyObject, + { + Self::new_bound(py, object).map(Bound::into_gil_ref) + } + + /// Constructs a new Weak Reference (weakref.ref) for the given object. + /// + /// Returns a `TypeError` if `object` is not subclassable (Most native types and PyClasses without `weakref` flag). + /// + /// # Examples + /// + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let foo = Py::new(py, Foo {})?; + /// let weakref = PyWeakRef::new_bound(py, foo.clone_ref(py))?; + /// assert!(weakref.call0()?.is(&foo)); + /// + /// let weakref2 = PyWeakRef::new_bound(py, foo.clone_ref(py))?; + /// assert!(weakref.is(&weakref2)); + /// + /// drop(foo); + /// + /// assert!(weakref.call0()?.is(&py.None())); + /// Ok(()) + /// }) + /// # } + /// ```` + #[track_caller] + pub fn new_bound(py: Python<'_>, object: T) -> PyResult> + where + T: ToPyObject, + { + unsafe { + Bound::from_owned_ptr_or_err( + py, + ffi::PyWeakref_NewRef(object.to_object(py).as_ptr(), ffi::Py_None()), + ) + .map(|obj| obj.downcast_into_unchecked()) + } + } + + #[inline] + #[track_caller] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyWeakRef::new_with` will be replaced by `PyWeakRef::new_bound_with` in a future PyO3 version" + ) + )] + pub fn new_with<'py, T, C>(py: Python<'py>, object: T, callback: C) -> PyResult<&'py PyWeakRef> + where + T: ToPyObject, + C: ToPyObject, + { + Self::new_bound_with(py, object, callback).map(Bound::into_gil_ref) + } + + /// Constructs a new Weak Reference (weakref.ref) for the given object with a callback. + /// + /// Returns a `TypeError` if `object` is not subclassable (Most native types and PyClasses without `weakref` flag) or if the `callback` is not callable or None. + /// + /// # Examples + /// + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// #[staticmethod] + /// fn finalizer(slf_ref: Bound<'_, PyWeakRef>) -> PyResult<()> { + /// let py = slf_ref.py(); + /// assert!(slf_ref.call0()?.is(&py.None())); + /// py.run("counter = 1", None, None)?; + /// Ok(()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// py.run("counter = 0", None, None)?; + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// let foo = Bound::new(py, Foo { } )?; + /// + /// // This is fine. + /// let weakref = PyWeakRef::new_bound_with(py, foo.clone(), py.None())?; + /// assert!(weakref.call0()?.is(&foo)); + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// + /// let weakref2 = PyWeakRef::new_bound_with(py, foo.clone(), py.get_type::().getattr("finalizer")?)?; + /// assert!(!weakref.is(&weakref2)); // Not the same weakref + /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object + /// + /// drop(foo); + /// + /// assert!(weakref.call0()?.is(&py.None())); + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 1); + /// Ok(()) + /// }) + /// # } + /// ```` + #[track_caller] + pub fn new_bound_with<'py, T, C>( + py: Python<'py>, + object: T, + callback: C, + ) -> PyResult> + where + T: ToPyObject, + C: ToPyObject, + { + unsafe { + Bound::from_owned_ptr_or_err( + py, + ffi::PyWeakref_NewRef( + object.to_object(py).as_ptr(), + callback.to_object(py).as_ptr(), + ), + ) + .map(|obj| obj.downcast_into_unchecked()) + } + } + + pub fn upgrade(&self) -> PyResult> + where + T: PyTypeCheck, + { + Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) + } + + pub fn get_object(&self) -> PyResult> { + Ok(self.as_borrowed().get_object()?.map(Bound::into_gil_ref)) + } + + pub fn get_object_raw(&self) -> PyResult<&'_ PyAny> { + self.as_borrowed().get_object_raw().map(Bound::into_gil_ref) + } +} + +/// Implementation of functionality for [`PyWeakRef`]. +/// +/// These methods are defined for the `Bound<'py, PyWeakRef>` smart pointer, so to use method call +/// syntax these methods are separated into a trait, because stable Rust does not yet support +/// `arbitrary_self_types`. +#[doc(alias = "PyWeakRef")] +pub trait PyWeakRefMethods<'py> { + // Named it upgrade to allign with rust's Weak::upgrade. + fn upgrade(&self) -> PyResult>> + where + T: PyTypeCheck; + // fn borrowed_upgrade(&self) -> PyResult>>; + + // TODO: NAMING + // maybe upgrade_any + fn get_object(&self) -> PyResult>>; + // maybe upgrade_any_borrowed + fn borrow_object(&self) -> PyResult>>; + + // TODO: NAMING + // get_any + fn get_object_raw(&self) -> PyResult>; + // get_any_borrowed + fn borrow_object_raw(&self) -> PyResult>; +} + +impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { + fn upgrade(&self) -> PyResult>> + where + T: PyTypeCheck, + { + Ok(self.get_object()?.map(|obj| obj.downcast_into::().expect( + format!("The `weakref.ReferenceType` (`PyWeakRef`) does not refer to the requested type `{}`", T::NAME).as_str(), + ))) + } + + /* + fn borrowed_upgrade(&self) -> PyResult>> { + Ok(self.borrow_object()?.map(|obj| obj.downcast_into::().expect( + format!("The `weakref.ReferenceType` (`PyWeakRef`) does not refer to the requested type `{}`", T::NAME).as_str(), + ))) + } + */ + + fn get_object(&self) -> PyResult>> { + let object = self.get_object_raw()?; + + Ok(if object.is_none() { None } else { Some(object) }) + } + + fn borrow_object(&self) -> PyResult>> { + let object = self.borrow_object_raw()?; + + Ok(if object.is_none() { None } else { Some(object) }) + } + + fn get_object_raw(&self) -> PyResult> { + // Bound<'_, PyAny>::call0 could also be used in situations where ffi::PyWeakref_GetObject is not available. + self.borrow_object_raw().map(Borrowed::to_owned) + } + + fn borrow_object_raw(&self) -> PyResult> { + // &PyAny::call0 could also be used in situations where ffi::PyWeakref_GetObject is not available. + unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::{pyclass, Python}; + use crate::types::any::PyAnyMethods; + use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; + use crate::Py; + + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} + + #[test] + fn test_reference_upgrade() { + Python::with_gil(|py| { + let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).unwrap(); + + { + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.is_some_and(|obj| obj.as_ptr() == foo.as_ptr())); + } + + drop(foo); + + { + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + }) + } + + #[test] + fn test_reference_get_object() { + Python::with_gil(|py| { + let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).ok().unwrap(); + + assert!(reference.call0().unwrap().is(&foo)); + assert!(reference.get_object().unwrap().is_some()); + assert!(reference + .get_object() + .unwrap() + .is_some_and(|obj| obj.is(&foo))); + + drop(foo); + + assert!(reference.call0().unwrap().is_none()); + assert!(reference.get_object().unwrap().is_none()); + }) + } + + #[test] + fn test_reference_borrrow_object() { + Python::with_gil(|py| { + let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).ok().unwrap(); + + assert!(reference.call0().unwrap().is(&foo)); + assert!(reference.borrow_object().unwrap().is_some()); + assert!(reference + .borrow_object() + .unwrap() + .is_some_and(|obj| obj.is(&foo))); + + drop(foo); + + assert!(reference.call0().unwrap().is_none()); + assert!(reference.borrow_object().unwrap().is_none()); + }) + } + + #[test] + fn test_reference_get_object_raw() { + Python::with_gil(|py| { + let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).ok().unwrap(); + + assert!(reference.call0().unwrap().is(&foo)); + assert!(reference.get_object_raw().unwrap().is(&foo)); + + drop(foo); + + assert!(reference + .call0() + .unwrap() + .is(&reference.get_object_raw().unwrap())); + assert!(reference.call0().unwrap().is_none()); + assert!(reference.get_object_raw().unwrap().is_none()); + }); + } + + #[test] + fn test_reference_borrow_object_raw() { + Python::with_gil(|py| { + let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).ok().unwrap(); + + assert!(reference.call0().unwrap().is(&foo)); + assert!(reference.borrow_object_raw().unwrap().is(&foo)); + + drop(foo); + + assert!(reference.call0().unwrap().is_none()); + assert!(reference.borrow_object_raw().unwrap().is_none()); + }); + } +} From f84d6d254694c17fe84c4f09e3456e6d6d0b5a72 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Thu, 8 Feb 2024 22:54:27 +0100 Subject: [PATCH 03/42] Add documentation for PyWeakRef::upgrade --- src/types/weakref.rs | 293 ++++++++++++++++++++++++++++++------------- 1 file changed, 209 insertions(+), 84 deletions(-) diff --git a/src/types/weakref.rs b/src/types/weakref.rs index 37079402e2e..f66c5dfdcc4 100644 --- a/src/types/weakref.rs +++ b/src/types/weakref.rs @@ -36,7 +36,7 @@ impl PyWeakRef { Self::new_bound(py, object).map(Bound::into_gil_ref) } - /// Constructs a new Weak Reference (weakref.ref) for the given object. + /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object. /// /// Returns a `TypeError` if `object` is not subclassable (Most native types and PyClasses without `weakref` flag). /// @@ -51,20 +51,24 @@ impl PyWeakRef { /// /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { - /// let foo = Py::new(py, Foo {})?; - /// let weakref = PyWeakRef::new_bound(py, foo.clone_ref(py))?; - /// assert!(weakref.call0()?.is(&foo)); - /// - /// let weakref2 = PyWeakRef::new_bound(py, foo.clone_ref(py))?; + /// let foo = Bound::new(py, Foo {})?; + /// let weakref = PyWeakRef::new_bound(py, foo.clone())?; + /// assert!( + /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` + /// weakref.get_object()? + /// .is_some_and(|obj| obj.is(&foo)) + /// ); + /// + /// let weakref2 = PyWeakRef::new_bound(py, foo.clone())?; /// assert!(weakref.is(&weakref2)); /// /// drop(foo); /// - /// assert!(weakref.call0()?.is(&py.None())); + /// assert!(weakref.get_object()?.is_none()); /// Ok(()) /// }) /// # } - /// ```` + /// ``` #[track_caller] pub fn new_bound(py: Python<'_>, object: T) -> PyResult> where @@ -79,6 +83,7 @@ impl PyWeakRef { } } + /// Deprecated form of [`PyWeakRef::new_bound_with`]. #[inline] #[track_caller] #[cfg_attr( @@ -88,7 +93,7 @@ impl PyWeakRef { note = "`PyWeakRef::new_with` will be replaced by `PyWeakRef::new_bound_with` in a future PyO3 version" ) )] - pub fn new_with<'py, T, C>(py: Python<'py>, object: T, callback: C) -> PyResult<&'py PyWeakRef> + pub fn new_with(py: Python<'_>, object: T, callback: C) -> PyResult<&'_ PyWeakRef> where T: ToPyObject, C: ToPyObject, @@ -96,7 +101,7 @@ impl PyWeakRef { Self::new_bound_with(py, object, callback).map(Bound::into_gil_ref) } - /// Constructs a new Weak Reference (weakref.ref) for the given object with a callback. + /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object with a callback. /// /// Returns a `TypeError` if `object` is not subclassable (Most native types and PyClasses without `weakref` flag) or if the `callback` is not callable or None. /// @@ -109,46 +114,47 @@ impl PyWeakRef { /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } /// - /// #[pymethods] - /// impl Foo { - /// #[staticmethod] - /// fn finalizer(slf_ref: Bound<'_, PyWeakRef>) -> PyResult<()> { - /// let py = slf_ref.py(); - /// assert!(slf_ref.call0()?.is(&py.None())); - /// py.run("counter = 1", None, None)?; - /// Ok(()) - /// } + /// #[pyfunction] + /// fn callback(wref: Bound<'_, PyWeakRef>) -> PyResult<()> { + /// let py = wref.py(); + /// assert!(wref.upgrade::()?.is_none()); + /// py.run("counter = 1", None, None) /// } /// /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// py.run("counter = 0", None, None)?; /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); - /// let foo = Bound::new(py, Foo { } )?; + /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. /// let weakref = PyWeakRef::new_bound_with(py, foo.clone(), py.None())?; - /// assert!(weakref.call0()?.is(&foo)); + /// assert!(weakref.upgrade::()?.is_some()); + /// assert!( + /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` + /// weakref.get_object()? + /// .is_some_and(|obj| obj.is(&foo)) + /// ); /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakRef::new_bound_with(py, foo.clone(), py.get_type::().getattr("finalizer")?)?; + /// let weakref2 = PyWeakRef::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// /// drop(foo); /// - /// assert!(weakref.call0()?.is(&py.None())); + /// assert!(weakref.upgrade::()?.is_none()); /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 1); /// Ok(()) /// }) /// # } - /// ```` + /// ``` #[track_caller] - pub fn new_bound_with<'py, T, C>( - py: Python<'py>, + pub fn new_bound_with( + py: Python<'_>, object: T, callback: C, - ) -> PyResult> + ) -> PyResult> where T: ToPyObject, C: ToPyObject, @@ -165,6 +171,64 @@ impl PyWeakRef { } } + /// Upgrade the weakref to a direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(data_src) = reference.upgrade::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics if the requested type `T` is not the the type of the instance refered to by this `weakref.ReferenceType`. + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref pub fn upgrade(&self) -> PyResult> where T: PyTypeCheck, @@ -188,22 +252,79 @@ impl PyWeakRef { /// `arbitrary_self_types`. #[doc(alias = "PyWeakRef")] pub trait PyWeakRefMethods<'py> { - // Named it upgrade to allign with rust's Weak::upgrade. + /// Upgrade the weakref to a direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(data_src) = reference.upgrade::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics if the requested type `T` is not the the type of the instance refered to by this `weakref.ReferenceType`. + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref fn upgrade(&self) -> PyResult>> where T: PyTypeCheck; + + // TODO: Is this even possible? // fn borrowed_upgrade(&self) -> PyResult>>; - // TODO: NAMING - // maybe upgrade_any + // TODO: NAMING-ALTERNATIVE: upgrade_any fn get_object(&self) -> PyResult>>; - // maybe upgrade_any_borrowed + // TODO: NAMING-ALTERNATIVE: upgrade_any_borrowed fn borrow_object(&self) -> PyResult>>; - // TODO: NAMING - // get_any + // TODO: NAMING-ALTERNATIVE: get_any fn get_object_raw(&self) -> PyResult>; - // get_any_borrowed + // TODO: NAMING-ALTERNATIVE: get_any_borrowed fn borrow_object_raw(&self) -> PyResult>; } @@ -213,14 +334,17 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { T: PyTypeCheck, { Ok(self.get_object()?.map(|obj| obj.downcast_into::().expect( - format!("The `weakref.ReferenceType` (`PyWeakRef`) does not refer to the requested type `{}`", T::NAME).as_str(), + "The `weakref.ReferenceType` (`PyWeakRef`) should refer to an instance of the specified class", ))) } /* - fn borrowed_upgrade(&self) -> PyResult>> { + fn borrowed_upgrade(&self) -> PyResult>> + where + T: PyTypeCheck + { Ok(self.borrow_object()?.map(|obj| obj.downcast_into::().expect( - format!("The `weakref.ReferenceType` (`PyWeakRef`) does not refer to the requested type `{}`", T::NAME).as_str(), + "The `weakref.ReferenceType` (`PyWeakRef`) should refer to an instance of the specified class", ))) } */ @@ -250,19 +374,19 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { #[cfg(test)] mod tests { - use crate::prelude::{pyclass, Python}; + use crate::prelude::{pyclass, Py, Python}; use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; - use crate::Py; + use crate::PyResult; #[pyclass(weakref, crate = "crate")] struct WeakrefablePyClass {} #[test] - fn test_reference_upgrade() { + fn test_reference_upgrade() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).unwrap(); + let foo = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; { let obj = reference.upgrade::(); @@ -284,82 +408,83 @@ mod tests { assert!(obj.is_none()); } + + Ok(()) }) } #[test] - fn test_reference_get_object() { + fn test_reference_get_object() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).ok().unwrap(); + let foo = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; - assert!(reference.call0().unwrap().is(&foo)); - assert!(reference.get_object().unwrap().is_some()); - assert!(reference - .get_object() - .unwrap() - .is_some_and(|obj| obj.is(&foo))); + assert!(reference.call0()?.is(&foo)); + assert!(reference.get_object()?.is_some()); + assert!(reference.get_object()?.is_some_and(|obj| obj.is(&foo))); drop(foo); - assert!(reference.call0().unwrap().is_none()); - assert!(reference.get_object().unwrap().is_none()); + assert!(reference.call0()?.is_none()); + assert!(reference.get_object()?.is_none()); + + Ok(()) }) } #[test] - fn test_reference_borrrow_object() { + fn test_reference_borrrow_object() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).ok().unwrap(); + let foo = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; - assert!(reference.call0().unwrap().is(&foo)); - assert!(reference.borrow_object().unwrap().is_some()); - assert!(reference - .borrow_object() - .unwrap() - .is_some_and(|obj| obj.is(&foo))); + assert!(reference.call0()?.is(&foo)); + assert!(reference.borrow_object()?.is_some()); + assert!(reference.borrow_object()?.is_some_and(|obj| obj.is(&foo))); drop(foo); - assert!(reference.call0().unwrap().is_none()); - assert!(reference.borrow_object().unwrap().is_none()); + assert!(reference.call0()?.is_none()); + assert!(reference.borrow_object()?.is_none()); + + Ok(()) }) } #[test] - fn test_reference_get_object_raw() { + fn test_reference_get_object_raw() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).ok().unwrap(); + let foo = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; - assert!(reference.call0().unwrap().is(&foo)); - assert!(reference.get_object_raw().unwrap().is(&foo)); + assert!(reference.call0()?.is(&foo)); + assert!(reference.get_object_raw()?.is(&foo)); drop(foo); - assert!(reference - .call0() - .unwrap() - .is(&reference.get_object_raw().unwrap())); - assert!(reference.call0().unwrap().is_none()); - assert!(reference.get_object_raw().unwrap().is_none()); - }); + assert!(reference.call0()?.is(&reference.get_object_raw()?)); + assert!(reference.call0()?.is_none()); + assert!(reference.get_object_raw()?.is_none()); + + Ok(()) + }) } #[test] - fn test_reference_borrow_object_raw() { + fn test_reference_borrow_object_raw() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {}).unwrap(); - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py)).ok().unwrap(); + let foo = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; - assert!(reference.call0().unwrap().is(&foo)); - assert!(reference.borrow_object_raw().unwrap().is(&foo)); + assert!(reference.call0()?.is(&foo)); + assert!(reference.borrow_object_raw()?.is(&foo)); drop(foo); - assert!(reference.call0().unwrap().is_none()); - assert!(reference.borrow_object_raw().unwrap().is_none()); - }); + assert!(reference.call0()?.is_none()); + assert!(reference.borrow_object_raw()?.is_none()); + + Ok(()) + }) } } From b696f33a0726d695abdf67e24f49b199d9be1632 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 9 Feb 2024 13:02:05 +0100 Subject: [PATCH 04/42] Add missing docs for PyWeakRef --- src/types/weakref.rs | 286 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 285 insertions(+), 1 deletion(-) diff --git a/src/types/weakref.rs b/src/types/weakref.rs index f66c5dfdcc4..123b17c33a3 100644 --- a/src/types/weakref.rs +++ b/src/types/weakref.rs @@ -236,10 +236,104 @@ impl PyWeakRef { Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) } + /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. + /// + /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(object) = reference.get_object()? { + /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) + /// } else { + /// Ok("The object, which this reference refered to, no longer exists".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object 'Foo' refered by this reference still exists." + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object, which this reference refered to, no longer exists" + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref pub fn get_object(&self) -> PyResult> { Ok(self.as_borrowed().get_object()?.map(Bound::into_gil_ref)) } + /// Retrieve to a object pointed to by the weakref. + /// + /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// reference + /// .get_object_raw()? + /// .getattr("__class__")? + /// .repr()? + /// .to_str() + /// .map(ToOwned::to_owned) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let object = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, object.clone())?; + /// + /// assert_eq!( + /// get_class(reference.as_borrowed())?, + /// "" + /// ); + /// + /// drop(object); + /// + /// assert_eq!(get_class(reference.as_borrowed())?, ""); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref pub fn get_object_raw(&self) -> PyResult<&'_ PyAny> { self.as_borrowed().get_object_raw().map(Bound::into_gil_ref) } @@ -252,7 +346,7 @@ impl PyWeakRef { /// `arbitrary_self_types`. #[doc(alias = "PyWeakRef")] pub trait PyWeakRefMethods<'py> { - /// Upgrade the weakref to a direct object reference. + /// Upgrade the weakref to a direct Bound object reference. /// /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). @@ -318,13 +412,203 @@ pub trait PyWeakRefMethods<'py> { // fn borrowed_upgrade(&self) -> PyResult>>; // TODO: NAMING-ALTERNATIVE: upgrade_any + /// Upgrade the weakref to a Bound [`PyAny`] reference to the target object if possible. + /// + /// This function returns `Some(Bound<'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(object) = reference.get_object()? { + /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) + /// } else { + /// Ok("The object, which this reference refered to, no longer exists".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object 'Foo' refered by this reference still exists." + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object, which this reference refered to, no longer exists" + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref fn get_object(&self) -> PyResult>>; + // TODO: NAMING-ALTERNATIVE: upgrade_any_borrowed + /// Upgrade the weakref to a Borrowed [`PyAny`] reference to the target object if possible. + /// + /// This function returns `Some(Borrowed<'_, 'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(object) = reference.borrow_object()? { + /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) + /// } else { + /// Ok("The object, which this reference refered to, no longer exists".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object 'Foo' refered by this reference still exists." + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object, which this reference refered to, no longer exists" + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref fn borrow_object(&self) -> PyResult>>; // TODO: NAMING-ALTERNATIVE: get_any + /// Retrieve to a Bound object pointed to by the weakref. + /// + /// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// reference + /// .get_object_raw()? + /// .getattr("__class__")? + /// .repr()? + /// .to_str() + /// .map(ToOwned::to_owned) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let object = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, object.clone())?; + /// + /// assert_eq!( + /// get_class(reference.as_borrowed())?, + /// "" + /// ); + /// + /// drop(object); + /// + /// assert_eq!(get_class(reference.as_borrowed())?, ""); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref fn get_object_raw(&self) -> PyResult>; + // TODO: NAMING-ALTERNATIVE: get_any_borrowed + /// Retrieve to a Borrowed object pointed to by the weakref. + /// + /// This function returns `Borrowed<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// reference + /// .borrow_object_raw()? + /// .getattr("__class__")? + /// .repr()? + /// .to_str() + /// .map(ToOwned::to_owned) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let object = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, object.clone())?; + /// + /// assert_eq!( + /// get_class(reference.as_borrowed())?, + /// "" + /// ); + /// + /// drop(object); + /// + /// assert_eq!(get_class(reference.as_borrowed())?, ""); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref fn borrow_object_raw(&self) -> PyResult>; } From 880fab739ff44f565dadacdf1dd096f93baf74f4 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:48:42 +0100 Subject: [PATCH 05/42] Add PyWeakProxy --- src/types/mod.rs | 2 +- src/types/weakref/mod.rs | 5 + src/types/weakref/proxy.rs | 540 ++++++++++++++++++ .../{weakref.rs => weakref/reference.rs} | 160 ++++-- 4 files changed, 653 insertions(+), 54 deletions(-) create mode 100644 src/types/weakref/mod.rs create mode 100644 src/types/weakref/proxy.rs rename src/types/{weakref.rs => weakref/reference.rs} (86%) diff --git a/src/types/mod.rs b/src/types/mod.rs index 73e11916eb4..91d37348a5e 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -44,7 +44,7 @@ pub use self::string::{PyString, PyString as PyUnicode}; pub use self::traceback::PyTraceback; pub use self::tuple::PyTuple; pub use self::typeobject::PyType; -pub use self::weakref::PyWeakRef; +pub use self::weakref::{PyWeakProxy, PyWeakRef}; /// Iteration over Python collections. /// diff --git a/src/types/weakref/mod.rs b/src/types/weakref/mod.rs new file mode 100644 index 00000000000..52112467e57 --- /dev/null +++ b/src/types/weakref/mod.rs @@ -0,0 +1,5 @@ +pub use proxy::PyWeakProxy; +pub use reference::{PyWeakRef, PyWeakRefMethods}; + +pub(crate) mod proxy; +pub(crate) mod reference; diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs new file mode 100644 index 00000000000..ce676bcbd95 --- /dev/null +++ b/src/types/weakref/proxy.rs @@ -0,0 +1,540 @@ +#[cfg(feature = "experimental-any-proxy")] +use std::cmp::Ordering; + +#[cfg(feature = "experimental-any-proxy")] +use crate::{ + basic::CompareOp, + exceptions::PyReferenceError, + type_object::PyTypeCheck, + types::{PyDict, PyIterator, PyList, PyString, PySuper, PyTuple, PyType}, + DowncastError, DowncastIntoError, FromPyObject, IntoPy, Py, PyTypeInfo, +}; + +use crate::err::PyResult; +use crate::ffi_ptr_ext::FfiPtrExt; +use crate::types::any::PyAnyMethods; +use crate::{ + ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, PyTypeCheck, Python, ToPyObject, +}; + +use super::PyWeakRefMethods; + +/// Represents a Python `weakref.ProxyType`. +/// +/// In Python this is created by calling `weakref.proxy`. +#[repr(transparent)] +pub struct PyWeakProxy(PyAny); + +pyobject_native_type!( + PyWeakProxy, + ffi::PyWeakReference, + pyobject_native_static_type_object!(ffi::_PyWeakref_ProxyType), + #module=Some("weakref"), + #checkfunction=ffi::PyWeakref_CheckProxy +); + +impl PyWeakProxy { + /// Deprecated form of [`PyWeakProxy::new_bound`]. + #[inline] + #[track_caller] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyWeakProxy::new` will be replaced by `PyWeakProxy::new_bound` in a future PyO3 version" + ) + )] + pub fn new(py: Python<'_>, object: T) -> PyResult<&'_ PyWeakProxy> + where + T: ToPyObject, + { + Self::new_bound(py, object).map(Bound::into_gil_ref) + } + + /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`) for the given object. + /// + /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag). + /// The object should also not be callable. For a callable weakref proxy see [`PyWeakCallableProxy`](crate::types::weakref::PyWeakCallableProxy). + /// + /// # Examples + /// + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let foo = Bound::new(py, Foo {})?; + /// let weakref = PyWeakProxy::new_bound(py, foo.clone())?; + /// assert!( + /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` + /// weakref.get_object()? + /// .is_some_and(|obj| obj.is(&foo)) + /// ); + /// + /// let weakref2 = PyWeakProxy::new_bound(py, foo.clone())?; + /// assert!(weakref.is(&weakref2)); + /// + /// drop(foo); + /// + /// assert!(weakref.get_object()?.is_none()); + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics if the provided object is callable. + /// + #[inline] + #[track_caller] + pub fn new_bound(py: Python<'_>, object: T) -> PyResult> + where + T: ToPyObject, + { + unsafe { + let ptr = object.to_object(py).as_ptr(); + assert_eq!( + ffi::PyCallable_Check(ptr), 0, + "An object to be referenced by a PyWeakProxy should not be callable. Use PyWeakCallableProxy instead." + ); + + Bound::from_owned_ptr_or_err(py, ffi::PyWeakref_NewProxy(ptr, ffi::Py_None())) + .map(|obj| obj.downcast_into_unchecked()) + } + } + + /// Deprecated form of [`PyWeakProxy::new_bound_with`]. + #[inline] + #[track_caller] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyWeakProxy::new_with` will be replaced by `PyWeakProxy::new_bound_with` in a future PyO3 version" + ) + )] + pub fn new_with(py: Python<'_>, object: T, callback: C) -> PyResult<&'_ PyWeakProxy> + where + T: ToPyObject, + C: ToPyObject, + { + Self::new_bound_with(py, object, callback).map(Bound::into_gil_ref) + } + + /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`) for the given object with a callback. + /// + /// 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. + /// The object should also not be callable. For a callable weakref proxy see [`PyWeakCallableProxy`](crate::types::weakref::PyWeakCallableProxy). + /// + /// # Examples + /// + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pyfunction] + /// fn callback(wref: Bound<'_, PyWeakProxy>) -> PyResult<()> { + /// let py = wref.py(); + /// assert!(wref.upgrade::()?.is_none()); + /// py.run("counter = 1", None, None) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// py.run("counter = 0", None, None)?; + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// let foo = Bound::new(py, Foo{})?; + /// + /// // This is fine. + /// let weakref = PyWeakProxy::new_bound_with(py, foo.clone(), py.None())?; + /// assert!(weakref.upgrade::()?.is_some()); + /// assert!( + /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` + /// weakref.get_object()? + /// .is_some_and(|obj| obj.is(&foo)) + /// ); + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// + /// let weakref2 = PyWeakProxy::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; + /// assert!(!weakref.is(&weakref2)); // Not the same weakref + /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object + /// + /// drop(foo); + /// + /// assert!(weakref.upgrade::()?.is_none()); + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 1); + /// Ok(()) + /// }) + /// # } + /// ``` + /// # Panics + /// This function panics if the provided object is callable. + #[track_caller] + pub fn new_bound_with( + py: Python<'_>, + object: T, + callback: C, + ) -> PyResult> + where + T: ToPyObject, + C: ToPyObject, + { + unsafe { + let ptr = object.to_object(py).as_ptr(); + assert_eq!( + ffi::PyCallable_Check(ptr), 0, + "An object to be referenced by a PyWeakProxy should not be callable. Use PyWeakCallableProxy instead." + ); + + Bound::from_owned_ptr_or_err( + py, + ffi::PyWeakref_NewProxy(ptr, callback.to_object(py).as_ptr()), + ) + .map(|obj| obj.downcast_into_unchecked()) + } + } + + /// Upgrade the weakref to a direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { + /// if let Some(data_src) = reference.upgrade::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakProxy::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics if the requested type `T` is not the the type of the instance refered to by this `weakref.ProxyType`. + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + pub fn upgrade(&self) -> PyResult> + where + T: PyTypeCheck, + { + Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) + } + + /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. + /// + /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. + /// + /// This function gets the optional target of this [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). + /// It produces similair results to calling the `weakref.ProxyType` or using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { + /// if let Some(object) = reference.get_object()? { + /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) + /// } else { + /// Ok("The object, which this reference refered to, no longer exists".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakProxy::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object 'Foo' refered by this reference still exists." + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object, which this reference refered to, no longer exists" + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + pub fn get_object(&self) -> PyResult> { + Ok(self.as_borrowed().get_object()?.map(Bound::into_gil_ref)) + } + + /// Retrieve to a object pointed to by the weakref. + /// + /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// + /// This function gets the optional target of this [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). + /// It produces similair results to calling the `weakref.ProxyType` or using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn get_class(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { + /// reference + /// .get_object_raw()? + /// .getattr("__class__")? + /// .repr()? + /// .to_str() + /// .map(ToOwned::to_owned) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let object = Bound::new(py, Foo{})?; + /// let reference = PyWeakProxy::new_bound(py, object.clone())?; + /// + /// assert_eq!( + /// get_class(reference.as_borrowed())?, + /// "" + /// ); + /// + /// drop(object); + /// + /// assert_eq!(get_class(reference.as_borrowed())?, ""); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + pub fn get_object_raw(&self) -> PyResult<&'_ PyAny> { + self.as_borrowed().get_object_raw().map(Bound::into_gil_ref) + } +} + +impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { + fn borrow_object_raw(&self) -> PyResult> { + unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } + } +} + +#[cfg(test)] +mod tests { + use crate::exceptions::{PyAttributeError, PyReferenceError}; + use crate::prelude::{pyclass, Py, PyResult, Python}; + use crate::types::any::PyAnyMethods; + use crate::types::weakref::{PyWeakProxy, PyWeakRefMethods}; + use crate::Bound; + + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} + + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let object = Bound::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone())?; + + assert!(!reference.is(&object)); + assert!(reference.get_object_raw()?.is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .is_err_and(|err| err.is_instance_of::(py))); + + drop(object); + + assert!(reference.get_object_raw()?.is_none()); + assert!(reference + .getattr("__class__") + .is_err_and(|err| err.is_instance_of::(py))); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + py.None().as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .is_err_and(|err| err.is_instance_of::(py))); + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + { + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.is_some_and(|obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.get_object()?.is_some()); + assert!(reference.get_object()?.is_some_and(|obj| obj.is(&object))); + + drop(object); + + assert!(reference.get_object()?.is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrrow_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.borrow_object()?.is_some()); + assert!(reference + .borrow_object()? + .is_some_and(|obj| obj.is(&object))); + + drop(object); + + assert!(reference.borrow_object()?.is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.get_object_raw()?.is(&object)); + + drop(object); + + assert!(reference.get_object_raw()?.is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrow_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.borrow_object_raw()?.is(&object)); + + drop(object); + + assert!(reference.borrow_object_raw()?.is_none()); + + Ok(()) + }) + } +} diff --git a/src/types/weakref.rs b/src/types/weakref/reference.rs similarity index 86% rename from src/types/weakref.rs rename to src/types/weakref/reference.rs index 123b17c33a3..d75c580d0df 100644 --- a/src/types/weakref.rs +++ b/src/types/weakref/reference.rs @@ -38,7 +38,7 @@ impl PyWeakRef { /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object. /// - /// Returns a `TypeError` if `object` is not subclassable (Most native types and PyClasses without `weakref` flag). + /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag). /// /// # Examples /// @@ -103,7 +103,7 @@ impl PyWeakRef { /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object with a callback. /// - /// Returns a `TypeError` if `object` is not subclassable (Most native types and PyClasses without `weakref` flag) or if the `callback` is not callable or None. + /// 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. /// /// # Examples /// @@ -406,7 +406,14 @@ pub trait PyWeakRefMethods<'py> { /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref fn upgrade(&self) -> PyResult>> where - T: PyTypeCheck; + T: PyTypeCheck, + { + { + Ok(self.get_object()?.map(|obj| obj.downcast_into::().expect( + "The `weakref.ReferenceType` (`PyWeakRef`) should refer to an instance of the specified class", + ))) + } + } // TODO: Is this even possible? // fn borrowed_upgrade(&self) -> PyResult>>; @@ -460,7 +467,11 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn get_object(&self) -> PyResult>>; + fn get_object(&self) -> PyResult>> { + let object = self.get_object_raw()?; + + Ok(if object.is_none() { None } else { Some(object) }) + } // TODO: NAMING-ALTERNATIVE: upgrade_any_borrowed /// Upgrade the weakref to a Borrowed [`PyAny`] reference to the target object if possible. @@ -511,7 +522,11 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn borrow_object(&self) -> PyResult>>; + fn borrow_object(&self) -> PyResult>> { + let object = self.borrow_object_raw()?; + + Ok(if object.is_none() { None } else { Some(object) }) + } // TODO: NAMING-ALTERNATIVE: get_any /// Retrieve to a Bound object pointed to by the weakref. @@ -560,7 +575,11 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn get_object_raw(&self) -> PyResult>; + fn get_object_raw(&self) -> PyResult> { + // Bound<'_, PyAny>::call0 could also be used in situations where ffi::PyWeakref_GetObject is not available. + // Only if it for weakref.ReferenceType + self.borrow_object_raw().map(Borrowed::to_owned) + } // TODO: NAMING-ALTERNATIVE: get_any_borrowed /// Retrieve to a Borrowed object pointed to by the weakref. @@ -633,23 +652,6 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { } */ - fn get_object(&self) -> PyResult>> { - let object = self.get_object_raw()?; - - Ok(if object.is_none() { None } else { Some(object) }) - } - - fn borrow_object(&self) -> PyResult>> { - let object = self.borrow_object_raw()?; - - Ok(if object.is_none() { None } else { Some(object) }) - } - - fn get_object_raw(&self) -> PyResult> { - // Bound<'_, PyAny>::call0 could also be used in situations where ffi::PyWeakref_GetObject is not available. - self.borrow_object_raw().map(Borrowed::to_owned) - } - fn borrow_object_raw(&self) -> PyResult> { // &PyAny::call0 could also be used in situations where ffi::PyWeakref_GetObject is not available. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } @@ -661,16 +663,66 @@ mod tests { use crate::prelude::{pyclass, Py, Python}; use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; - use crate::PyResult; + use crate::{Bound, PyResult}; #[pyclass(weakref, crate = "crate")] struct WeakrefablePyClass {} #[test] - fn test_reference_upgrade() -> PyResult<()> { + fn test_weakref_refence_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let object = Bound::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone())?; + + assert!(!reference.is(&object)); + assert!(reference.get_object_raw()?.is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .is_ok_and(|result| result.is_none())); + + drop(object); + + assert!(reference.get_object_raw()?.is_none()); + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!("", reference.as_ptr()) + ); + + assert!(reference + .getattr("__callback__") + .is_ok_and(|result| result.is_none())); + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; { let obj = reference.upgrade::(); @@ -679,10 +731,10 @@ mod tests { let obj = obj.unwrap(); assert!(obj.is_some()); - assert!(obj.is_some_and(|obj| obj.as_ptr() == foo.as_ptr())); + assert!(obj.is_some_and(|obj| obj.as_ptr() == object.as_ptr())); } - drop(foo); + drop(object); { let obj = reference.upgrade::(); @@ -698,16 +750,16 @@ mod tests { } #[test] - fn test_reference_get_object() -> PyResult<()> { + fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; - assert!(reference.call0()?.is(&foo)); + assert!(reference.call0()?.is(&object)); assert!(reference.get_object()?.is_some()); - assert!(reference.get_object()?.is_some_and(|obj| obj.is(&foo))); + assert!(reference.get_object()?.is_some_and(|obj| obj.is(&object))); - drop(foo); + drop(object); assert!(reference.call0()?.is_none()); assert!(reference.get_object()?.is_none()); @@ -717,16 +769,18 @@ mod tests { } #[test] - fn test_reference_borrrow_object() -> PyResult<()> { + fn test_weakref_borrrow_object() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; - assert!(reference.call0()?.is(&foo)); + assert!(reference.call0()?.is(&object)); assert!(reference.borrow_object()?.is_some()); - assert!(reference.borrow_object()?.is_some_and(|obj| obj.is(&foo))); + assert!(reference + .borrow_object()? + .is_some_and(|obj| obj.is(&object))); - drop(foo); + drop(object); assert!(reference.call0()?.is_none()); assert!(reference.borrow_object()?.is_none()); @@ -736,15 +790,15 @@ mod tests { } #[test] - fn test_reference_get_object_raw() -> PyResult<()> { + fn test_weakref_get_object_raw() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; - assert!(reference.call0()?.is(&foo)); - assert!(reference.get_object_raw()?.is(&foo)); + assert!(reference.call0()?.is(&object)); + assert!(reference.get_object_raw()?.is(&object)); - drop(foo); + drop(object); assert!(reference.call0()?.is(&reference.get_object_raw()?)); assert!(reference.call0()?.is_none()); @@ -755,15 +809,15 @@ mod tests { } #[test] - fn test_reference_borrow_object_raw() -> PyResult<()> { + fn test_weakref_borrow_object_raw() -> PyResult<()> { Python::with_gil(|py| { - let foo = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, foo.clone_ref(py))?; + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; - assert!(reference.call0()?.is(&foo)); - assert!(reference.borrow_object_raw()?.is(&foo)); + assert!(reference.call0()?.is(&object)); + assert!(reference.borrow_object_raw()?.is(&object)); - drop(foo); + drop(object); assert!(reference.call0()?.is_none()); assert!(reference.borrow_object_raw()?.is_none()); From 62616a00495bef043e803680eb9ca0a848a33b64 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 9 Feb 2024 17:41:43 +0100 Subject: [PATCH 06/42] Add PyWeakCallableProxy --- src/types/mod.rs | 2 +- src/types/weakref/callableproxy.rs | 591 +++++++++++++++++++++++++++++ src/types/weakref/mod.rs | 2 + src/types/weakref/proxy.rs | 30 +- src/types/weakref/reference.rs | 16 +- 5 files changed, 618 insertions(+), 23 deletions(-) create mode 100644 src/types/weakref/callableproxy.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index 91d37348a5e..c2d0f57df33 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -44,7 +44,7 @@ pub use self::string::{PyString, PyString as PyUnicode}; pub use self::traceback::PyTraceback; pub use self::tuple::PyTuple; pub use self::typeobject::PyType; -pub use self::weakref::{PyWeakProxy, PyWeakRef}; +pub use self::weakref::{PyWeakCallableProxy, PyWeakProxy, PyWeakRef}; /// Iteration over Python collections. /// diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs new file mode 100644 index 00000000000..54e41427fc1 --- /dev/null +++ b/src/types/weakref/callableproxy.rs @@ -0,0 +1,591 @@ +use crate::err::PyResult; +use crate::ffi_ptr_ext::FfiPtrExt; +use crate::types::any::PyAnyMethods; +use crate::{ + ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, PyTypeCheck, Python, ToPyObject, +}; + +use super::PyWeakRefMethods; + +/// Represents a Python `weakref.ProxyType`. +/// +/// In Python this is created by calling `weakref.proxy`. +#[repr(transparent)] +pub struct PyWeakCallableProxy(PyAny); + +pyobject_native_type!( + PyWeakCallableProxy, + ffi::PyWeakReference, + pyobject_native_static_type_object!(ffi::_PyWeakref_CallableProxyType), + #module=Some("weakref"), + #checkfunction=ffi::PyWeakref_CheckProxy +); + +impl PyWeakCallableProxy { + /// Deprecated form of [`PyWeakCallableProxy::new_bound`]. + #[inline] + #[track_caller] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyWeakCallableProxy::new` will be replaced by `PyWeakCallableProxy::new_bound` in a future PyO3 version" + ) + )] + pub fn new(py: Python<'_>, object: T) -> PyResult<&'_ PyWeakCallableProxy> + where + T: ToPyObject, + { + Self::new_bound(py, object).map(Bound::into_gil_ref) + } + + /// Constructs a new Weak callable Reference (`weakref.proxy`/`weakref.CallableProxyType`) for the given object. + /// + /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag). + /// The object should also be callable. For a non-callable weakref proxy see [`PyWeakProxy`](crate::types::weakref::PyWeakProxy). + /// + /// # Examples + /// + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakCallableProxy; + /// use pyo3::exceptions::PyReferenceError; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn __call__(&self) -> &str { + /// "This class is callable" + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let foo = Bound::new(py, Foo {})?; + /// let weakref = PyWeakCallableProxy::new_bound(py, foo.clone())?; + /// assert!( + /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` + /// weakref.get_object()? + /// .is_some_and(|obj| obj.is(&foo)) + /// ); + /// + /// let weakref2 = PyWeakCallableProxy::new_bound(py, foo.clone())?; + /// assert!(weakref.is(&weakref2)); + /// + /// assert_eq!(weakref.call0()?.to_string(), "This class is callable"); + /// + /// drop(foo); + /// + /// assert!(weakref.get_object()?.is_none()); + /// assert!(weakref.call0().is_err_and(|err| err.is_instance_of::(py))); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics if the provided object is not callable. + /// + #[inline] + #[track_caller] + pub fn new_bound(py: Python<'_>, object: T) -> PyResult> + where + T: ToPyObject, + { + unsafe { + let ptr = object.to_object(py).as_ptr(); + assert_eq!( + ffi::PyCallable_Check(ptr), 1, + "An object to be referenced by a PyWeakCallableProxy should be callable. Use PyWeakProxy instead." + ); + + Bound::from_owned_ptr_or_err(py, ffi::PyWeakref_NewProxy(ptr, ffi::Py_None())) + .map(|obj| obj.downcast_into_unchecked()) + } + } + + /// Deprecated form of [`PyWeakCallableProxy::new_bound_with`]. + #[inline] + #[track_caller] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyWeakCallableProxy::new_with` will be replaced by `PyWeakCallableProxy::new_bound_with` in a future PyO3 version" + ) + )] + pub fn new_with( + py: Python<'_>, + object: T, + callback: C, + ) -> PyResult<&'_ PyWeakCallableProxy> + where + T: ToPyObject, + C: ToPyObject, + { + Self::new_bound_with(py, object, callback).map(Bound::into_gil_ref) + } + + /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.CallableProxyType`) for the given object with a callback. + /// + /// 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. + /// The object should also be callable. For a non-callable weakref proxy see [`PyWeakProxy`](crate::types::weakref::PyWeakProxy). + /// + /// # Examples + /// + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakCallableProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn __call__(&self) -> &str { + /// "This class is callable" + /// } + /// } + /// + /// #[pyfunction] + /// fn callback(wref: Bound<'_, PyWeakCallableProxy>) -> PyResult<()> { + /// let py = wref.py(); + /// assert!(wref.upgrade::()?.is_none()); + /// py.run("counter = 1", None, None) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// py.run("counter = 0", None, None)?; + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// let foo = Bound::new(py, Foo{})?; + /// + /// // This is fine. + /// let weakref = PyWeakCallableProxy::new_bound_with(py, foo.clone(), py.None())?; + /// assert!(weakref.upgrade::()?.is_some()); + /// assert!( + /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` + /// weakref.get_object()? + /// .is_some_and(|obj| obj.is(&foo)) + /// ); + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// + /// let weakref2 = PyWeakCallableProxy::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; + /// assert!(!weakref.is(&weakref2)); // Not the same weakref + /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object + /// + /// assert_eq!(weakref.call0()?.to_string(), "This class is callable"); + /// + /// drop(foo); + /// + /// assert!(weakref.upgrade::()?.is_none()); + /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 1); + /// Ok(()) + /// }) + /// # } + /// ``` + /// # Panics + /// This function panics if the provided object is not callable. + #[track_caller] + pub fn new_bound_with( + py: Python<'_>, + object: T, + callback: C, + ) -> PyResult> + where + T: ToPyObject, + C: ToPyObject, + { + unsafe { + let ptr = object.to_object(py).as_ptr(); + assert_eq!( + ffi::PyCallable_Check(ptr), 1, + "An object to be referenced by a PyWeakCallableProxy should be callable. Use PyWeakProxy instead." + ); + + Bound::from_owned_ptr_or_err( + py, + ffi::PyWeakref_NewProxy(ptr, callback.to_object(py).as_ptr()), + ) + .map(|obj| obj.downcast_into_unchecked()) + } + } + + /// Upgrade the weakref to a direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakCallableProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn __call__(&self) -> &str { + /// "This class is callable" + /// } + /// + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { + /// if let Some(data_src) = reference.upgrade::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakCallableProxy::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics if the requested type `T` is not the the type of the instance refered to by this `weakref.CallableProxyType`. + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + pub fn upgrade(&self) -> PyResult> + where + T: PyTypeCheck, + { + Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) + } + + /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. + /// + /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. + /// + /// This function gets the optional target of this [`weakref.CallableProxyType`] (result of calling [`weakref.proxy`]). + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakCallableProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn __call__(&self) -> &str { + /// "This class is callable" + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { + /// if let Some(object) = reference.get_object()? { + /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) + /// } else { + /// Ok("The object, which this reference refered to, no longer exists".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakCallableProxy::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object 'Foo' refered by this reference still exists." + /// ); + /// + /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object, which this reference refered to, no longer exists" + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ProxyCallableType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + pub fn get_object(&self) -> PyResult> { + Ok(self.as_borrowed().get_object()?.map(Bound::into_gil_ref)) + } + + /// Retrieve to a object pointed to by the weakref. + /// + /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// + /// This function gets the optional target of this [`weakref.CallableProxyType`] (result of calling [`weakref.proxy`]). + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakCallableProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn __call__(&self) -> &str { + /// "This class is callable" + /// } + /// } + /// + /// fn get_class(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { + /// reference + /// .get_object_raw()? + /// .getattr("__class__")? + /// .repr()? + /// .to_str() + /// .map(ToOwned::to_owned) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let object = Bound::new(py, Foo{})?; + /// let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + /// + /// assert_eq!( + /// get_class(reference.as_borrowed())?, + /// "" + /// ); + /// + /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); + /// + /// drop(object); + /// + /// assert_eq!(get_class(reference.as_borrowed())?, ""); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + pub fn get_object_raw(&self) -> PyResult<&'_ PyAny> { + self.as_borrowed().get_object_raw().map(Bound::into_gil_ref) + } +} + +impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakCallableProxy> { + fn borrow_object_raw(&self) -> PyResult> { + unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } + } +} + +#[cfg(test)] +mod tests { + use crate::exceptions::{PyAttributeError, PyReferenceError}; + use crate::prelude::{pyclass, pymethods, Py, PyResult, Python}; + use crate::types::any::PyAnyMethods; + use crate::types::weakref::{PyWeakCallableProxy, PyWeakRefMethods}; + use crate::Bound; + + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} + + #[pymethods(crate = "crate")] + impl WeakrefablePyClass { + fn __call__(&self) -> &str { + "This class is callable!" + } + } + + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let object = Bound::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + + assert!(!reference.is(&object)); + assert!(reference.get_object_raw()?.is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .is_err_and(|err| err.is_instance_of::(py))); + + assert_eq!(reference.call0()?.to_string(), "This class is callable!"); + + drop(object); + + assert!(reference.get_object_raw()?.is_none()); + assert!(reference + .getattr("__class__") + .is_err_and(|err| err.is_instance_of::(py))); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + py.None().as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .is_err_and(|err| err.is_instance_of::(py))); + + assert!(reference + .call0() + .is_err_and(|err| err.is_instance_of::(py) + & (err.value(py).to_string() == "weakly-referenced object no longer exists"))); + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + + { + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.is_some_and(|obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.get_object()?.is_some()); + assert!(reference.get_object()?.is_some_and(|obj| obj.is(&object))); + + drop(object); + + assert!(reference.get_object()?.is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrrow_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.borrow_object()?.is_some()); + assert!(reference + .borrow_object()? + .is_some_and(|obj| obj.is(&object))); + + drop(object); + + assert!(reference.borrow_object()?.is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.get_object_raw()?.is(&object)); + + drop(object); + + assert!(reference.get_object_raw()?.is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrow_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.borrow_object_raw()?.is(&object)); + + drop(object); + + assert!(reference.borrow_object_raw()?.is_none()); + + Ok(()) + }) + } +} diff --git a/src/types/weakref/mod.rs b/src/types/weakref/mod.rs index 52112467e57..3d4f5550cab 100644 --- a/src/types/weakref/mod.rs +++ b/src/types/weakref/mod.rs @@ -1,5 +1,7 @@ +pub use callableproxy::PyWeakCallableProxy; pub use proxy::PyWeakProxy; pub use reference::{PyWeakRef, PyWeakRefMethods}; +pub(crate) mod callableproxy; pub(crate) mod proxy; pub(crate) mod reference; diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index ce676bcbd95..f3f425f5fa0 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -1,15 +1,3 @@ -#[cfg(feature = "experimental-any-proxy")] -use std::cmp::Ordering; - -#[cfg(feature = "experimental-any-proxy")] -use crate::{ - basic::CompareOp, - exceptions::PyReferenceError, - type_object::PyTypeCheck, - types::{PyDict, PyIterator, PyList, PyString, PySuper, PyTuple, PyType}, - DowncastError, DowncastIntoError, FromPyObject, IntoPy, Py, PyTypeInfo, -}; - use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::types::any::PyAnyMethods; @@ -204,7 +192,7 @@ impl PyWeakProxy { /// Upgrade the weakref to a direct object reference. /// /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example /// ```rust @@ -271,7 +259,7 @@ impl PyWeakProxy { /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. /// /// This function gets the optional target of this [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). - /// It produces similair results to calling the `weakref.ProxyType` or using [`PyWeakref_GetObject`] in the C api. + /// It produces similair results using [`PyWeakref_GetObject`] in the C api. /// /// # Example /// ```rust @@ -323,7 +311,7 @@ impl PyWeakProxy { /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). /// /// This function gets the optional target of this [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). - /// It produces similair results to calling the `weakref.ProxyType` or using [`PyWeakref_GetObject`] in the C api. + /// It produces similair results using [`PyWeakref_GetObject`] in the C api. /// /// # Example /// ```rust @@ -377,7 +365,7 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { #[cfg(test)] mod tests { - use crate::exceptions::{PyAttributeError, PyReferenceError}; + use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError}; use crate::prelude::{pyclass, Py, PyResult, Python}; use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakProxy, PyWeakRefMethods}; @@ -416,6 +404,11 @@ mod tests { .getattr("__callback__") .is_err_and(|err| err.is_instance_of::(py))); + assert!(reference + .call0() + .is_err_and(|err| err.is_instance_of::(py) + & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + drop(object); assert!(reference.get_object_raw()?.is_none()); @@ -435,6 +428,11 @@ mod tests { .getattr("__callback__") .is_err_and(|err| err.is_instance_of::(py))); + assert!(reference + .call0() + .is_err_and(|err| err.is_instance_of::(py) + & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + Ok(()) }) } diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index d75c580d0df..b0a294a5cd8 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -15,7 +15,7 @@ pyobject_native_type!( ffi::PyWeakReference, pyobject_native_static_type_object!(ffi::_PyWeakref_RefType), #module=Some("weakref"), - #checkfunction=ffi::PyWeakref_CheckRef + #checkfunction=ffi::PyWeakref_CheckRefExact ); impl PyWeakRef { @@ -349,7 +349,7 @@ pub trait PyWeakRefMethods<'py> { /// Upgrade the weakref to a direct Bound object reference. /// /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example /// ```rust @@ -424,7 +424,7 @@ pub trait PyWeakRefMethods<'py> { /// This function returns `Some(Bound<'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). - /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example /// ```rust @@ -479,7 +479,7 @@ pub trait PyWeakRefMethods<'py> { /// This function returns `Some(Borrowed<'_, 'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). - /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example /// ```rust @@ -534,7 +534,7 @@ pub trait PyWeakRefMethods<'py> { /// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). - /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example /// ```rust @@ -587,7 +587,7 @@ pub trait PyWeakRefMethods<'py> { /// This function returns `Borrowed<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). - /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example /// ```rust @@ -698,6 +698,8 @@ mod tests { .getattr("__callback__") .is_ok_and(|result| result.is_none())); + assert!(reference.call0()?.is(&object)); + drop(object); assert!(reference.get_object_raw()?.is_none()); @@ -714,6 +716,8 @@ mod tests { .getattr("__callback__") .is_ok_and(|result| result.is_none())); + assert!(reference.call0()?.is_none()); + Ok(()) }) } From 0d5667bd27273eea3e193515d62d5ea9173c9551 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 9 Feb 2024 19:30:37 +0100 Subject: [PATCH 07/42] Add PyWeakRefMethods::upgrade_exact and prevent unnecessary panicing --- src/types/weakref/callableproxy.rs | 99 ++++++++++++--- src/types/weakref/proxy.rs | 87 ++++++++++++-- src/types/weakref/reference.rs | 185 +++++++++++++++++++++++------ 3 files changed, 308 insertions(+), 63 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 54e41427fc1..a9769e663fe 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -1,9 +1,8 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; +use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; -use crate::{ - ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, PyTypeCheck, Python, ToPyObject, -}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; use super::PyWeakRefMethods; @@ -160,7 +159,7 @@ impl PyWeakCallableProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// py.run("counter = 0", None, None)?; - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. @@ -171,8 +170,8 @@ impl PyWeakCallableProxy { /// weakref.get_object()? /// .is_some_and(|obj| obj.is(&foo)) /// ); - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); - /// + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); + /// /// let weakref2 = PyWeakCallableProxy::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object @@ -182,7 +181,7 @@ impl PyWeakCallableProxy { /// drop(foo); /// /// assert!(weakref.upgrade::()?.is_none()); - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 1); + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 1); /// Ok(()) /// }) /// # } @@ -252,14 +251,14 @@ impl PyWeakCallableProxy { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakCallableProxy::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "Processing 'Dave': score = 10" /// ); - /// + /// /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); - /// + /// /// drop(data); /// /// assert_eq!( @@ -272,9 +271,6 @@ impl PyWeakCallableProxy { /// # } /// ``` /// - /// # Panics - /// This function panics if the requested type `T` is not the the type of the instance refered to by this `weakref.CallableProxyType`. - /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy @@ -285,6 +281,77 @@ impl PyWeakCallableProxy { Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) } + /// Upgrade the weakref to an exact direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakCallableProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn __call__(&self) -> &str { + /// "This class is callable" + /// } + /// + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_exact::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakCallableProxy::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + pub fn upgrade_exact(&self) -> PyResult> + where + T: PyTypeInfo, + { + Ok(self + .as_borrowed() + .upgrade_exact::()? + .map(Bound::into_gil_ref)) + } + /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. /// /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. @@ -319,12 +386,12 @@ impl PyWeakCallableProxy { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakCallableProxy::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "The object 'Foo' refered by this reference still exists." /// ); - /// + /// /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); /// /// drop(data); @@ -381,7 +448,7 @@ impl PyWeakCallableProxy { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; /// let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; - /// + /// /// assert_eq!( /// get_class(reference.as_borrowed())?, /// "" diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index f3f425f5fa0..062c22903a5 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -1,9 +1,8 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; +use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; -use crate::{ - ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, PyTypeCheck, Python, ToPyObject, -}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; use super::PyWeakRefMethods; @@ -137,7 +136,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// py.run("counter = 0", None, None)?; - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. @@ -148,8 +147,8 @@ impl PyWeakProxy { /// weakref.get_object()? /// .is_some_and(|obj| obj.is(&foo)) /// ); - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); - /// + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); + /// /// let weakref2 = PyWeakProxy::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object @@ -157,7 +156,7 @@ impl PyWeakProxy { /// drop(foo); /// /// assert!(weakref.upgrade::()?.is_none()); - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 1); + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 1); /// Ok(()) /// }) /// # } @@ -223,7 +222,7 @@ impl PyWeakProxy { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakProxy::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "Processing 'Dave': score = 10" @@ -241,9 +240,6 @@ impl PyWeakProxy { /// # } /// ``` /// - /// # Panics - /// This function panics if the requested type `T` is not the the type of the instance refered to by this `weakref.ProxyType`. - /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy @@ -254,6 +250,71 @@ impl PyWeakProxy { Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) } + /// Upgrade the weakref to an exact direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_exact::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakProxy::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + pub fn upgrade_exact(&self) -> PyResult> + where + T: PyTypeInfo, + { + Ok(self + .as_borrowed() + .upgrade_exact::()? + .map(Bound::into_gil_ref)) + } + /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. /// /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. @@ -281,7 +342,7 @@ impl PyWeakProxy { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakProxy::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "The object 'Foo' refered by this reference still exists." @@ -334,7 +395,7 @@ impl PyWeakProxy { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; /// let reference = PyWeakProxy::new_bound(py, object.clone())?; - /// + /// /// assert_eq!( /// get_class(reference.as_borrowed())?, /// "" diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index b0a294a5cd8..ee3928670ea 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -1,6 +1,6 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; -use crate::type_object::PyTypeCheck; +use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; use crate::{ffi, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; @@ -124,7 +124,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// py.run("counter = 0", None, None)?; - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. @@ -135,8 +135,8 @@ impl PyWeakRef { /// weakref.get_object()? /// .is_some_and(|obj| obj.is(&foo)) /// ); - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 0); - /// + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); + /// /// let weakref2 = PyWeakRef::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object @@ -144,7 +144,7 @@ impl PyWeakRef { /// drop(foo); /// /// assert!(weakref.upgrade::()?.is_none()); - /// assert_eq!(py.eval("counter", None, None)?.extract::()?, 1); + /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 1); /// Ok(()) /// }) /// # } @@ -205,7 +205,7 @@ impl PyWeakRef { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakRef::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "Processing 'Dave': score = 10" @@ -223,9 +223,6 @@ impl PyWeakRef { /// # } /// ``` /// - /// # Panics - /// This function panics if the requested type `T` is not the the type of the instance refered to by this `weakref.ReferenceType`. - /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref @@ -236,6 +233,71 @@ impl PyWeakRef { Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) } + /// Upgrade the weakref to an exact direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_exact::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + pub fn upgrade_exact(&self) -> PyResult> + where + T: PyTypeInfo, + { + Ok(self + .as_borrowed() + .upgrade_exact::()? + .map(Bound::into_gil_ref)) + } + /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. /// /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. @@ -263,7 +325,7 @@ impl PyWeakRef { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakRef::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "The object 'Foo' refered by this reference still exists." @@ -316,7 +378,7 @@ impl PyWeakRef { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; /// let reference = PyWeakRef::new_bound(py, object.clone())?; - /// + /// /// assert_eq!( /// get_class(reference.as_borrowed())?, /// "" @@ -380,7 +442,7 @@ pub trait PyWeakRefMethods<'py> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakRef::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "Processing 'Dave': score = 10" @@ -398,9 +460,6 @@ pub trait PyWeakRefMethods<'py> { /// # } /// ``` /// - /// # Panics - /// This function panics if the requested type `T` is not the the type of the instance refered to by this `weakref.ReferenceType`. - /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref @@ -408,16 +467,80 @@ pub trait PyWeakRefMethods<'py> { where T: PyTypeCheck, { - { - Ok(self.get_object()?.map(|obj| obj.downcast_into::().expect( - "The `weakref.ReferenceType` (`PyWeakRef`) should refer to an instance of the specified class", - ))) - } + Ok(self + .get_object()? + .map(|obj| obj.downcast_into::()) + .transpose()?) } // TODO: Is this even possible? // fn borrowed_upgrade(&self) -> PyResult>>; + /// Upgrade the weakref to a exact direct Bound object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Example + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_exact::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + fn upgrade_exact(&self) -> PyResult>> + where + T: PyTypeInfo, + { + Ok(self + .get_object()? + .map(|obj| obj.downcast_into_exact::()) + .transpose()?) + } + // TODO: NAMING-ALTERNATIVE: upgrade_any /// Upgrade the weakref to a Bound [`PyAny`] reference to the target object if possible. /// @@ -446,7 +569,7 @@ pub trait PyWeakRefMethods<'py> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakRef::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "The object 'Foo' refered by this reference still exists." @@ -501,7 +624,7 @@ pub trait PyWeakRefMethods<'py> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; /// let reference = PyWeakRef::new_bound(py, data.clone())?; - /// + /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, /// "The object 'Foo' refered by this reference still exists." @@ -522,7 +645,10 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn borrow_object(&self) -> PyResult>> { + fn borrow_object<'a>(&'a self) -> PyResult>> + where + 'py: 'a, + { let object = self.borrow_object_raw()?; Ok(if object.is_none() { None } else { Some(object) }) @@ -557,7 +683,7 @@ pub trait PyWeakRefMethods<'py> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; /// let reference = PyWeakRef::new_bound(py, object.clone())?; - /// + /// /// assert_eq!( /// get_class(reference.as_borrowed())?, /// "" @@ -610,7 +736,7 @@ pub trait PyWeakRefMethods<'py> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; /// let reference = PyWeakRef::new_bound(py, object.clone())?; - /// + /// /// assert_eq!( /// get_class(reference.as_borrowed())?, /// "" @@ -632,15 +758,6 @@ pub trait PyWeakRefMethods<'py> { } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { - fn upgrade(&self) -> PyResult>> - where - T: PyTypeCheck, - { - Ok(self.get_object()?.map(|obj| obj.downcast_into::().expect( - "The `weakref.ReferenceType` (`PyWeakRef`) should refer to an instance of the specified class", - ))) - } - /* fn borrowed_upgrade(&self) -> PyResult>> where From 5508d3a441e120fe295edd0fab457e144c9fe75f Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 9 Feb 2024 19:59:55 +0100 Subject: [PATCH 08/42] Change PyWeakRefMethods to exclude infeasible errors. As a result of the checks made about the objectpointers in PyO3, all errors in PyWeakref_GetObject are already caught. Therefor there is no need to check for these errors in the non-ffi layer. --- src/types/weakref/callableproxy.rs | 64 ++++++++------ src/types/weakref/proxy.rs | 64 ++++++++------ src/types/weakref/reference.rs | 132 +++++++++++++++++++---------- 3 files changed, 169 insertions(+), 91 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index a9769e663fe..107434279b1 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -66,7 +66,7 @@ impl PyWeakCallableProxy { /// let weakref = PyWeakCallableProxy::new_bound(py, foo.clone())?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object()? + /// weakref.get_object() /// .is_some_and(|obj| obj.is(&foo)) /// ); /// @@ -77,7 +77,7 @@ impl PyWeakCallableProxy { /// /// drop(foo); /// - /// assert!(weakref.get_object()?.is_none()); + /// assert!(weakref.get_object().is_none()); /// assert!(weakref.call0().is_err_and(|err| err.is_instance_of::(py))); /// /// Ok(()) @@ -167,7 +167,7 @@ impl PyWeakCallableProxy { /// assert!(weakref.upgrade::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object()? + /// weakref.get_object() /// .is_some_and(|obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); @@ -271,6 +271,10 @@ impl PyWeakCallableProxy { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy @@ -339,6 +343,10 @@ impl PyWeakCallableProxy { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy @@ -375,7 +383,7 @@ impl PyWeakCallableProxy { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { - /// if let Some(object) = reference.get_object()? { + /// if let Some(object) = reference.get_object() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -406,11 +414,15 @@ impl PyWeakCallableProxy { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyCallableType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn get_object(&self) -> PyResult> { - Ok(self.as_borrowed().get_object()?.map(Bound::into_gil_ref)) + pub fn get_object(&self) -> Option<&'_ PyAny> { + self.as_borrowed().get_object().map(Bound::into_gil_ref) } /// Retrieve to a object pointed to by the weakref. @@ -437,7 +449,7 @@ impl PyWeakCallableProxy { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { /// reference - /// .get_object_raw()? + /// .get_object_raw() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -465,17 +477,23 @@ impl PyWeakCallableProxy { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn get_object_raw(&self) -> PyResult<&'_ PyAny> { - self.as_borrowed().get_object_raw().map(Bound::into_gil_ref) + pub fn get_object_raw(&self) -> &'_ PyAny { + self.as_borrowed().get_object_raw().into_gil_ref() } } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakCallableProxy> { - fn borrow_object_raw(&self) -> PyResult> { + fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny> { + // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } + .expect("The 'weakref.CallableProxyType' instance should be valid (non-null and actually a weakref reference)") } } @@ -504,7 +522,7 @@ mod tests { let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw()?.is(&object)); + assert!(reference.get_object_raw().is(&object)); assert_eq!( reference.get_type().to_string(), "" @@ -531,7 +549,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw()?.is_none()); + assert!(reference.get_object_raw().is_none()); assert!(reference .getattr("__class__") .is_err_and(|err| err.is_instance_of::(py))); @@ -594,12 +612,12 @@ mod tests { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.get_object()?.is_some()); - assert!(reference.get_object()?.is_some_and(|obj| obj.is(&object))); + assert!(reference.get_object().is_some()); + assert!(reference.get_object().is_some_and(|obj| obj.is(&object))); drop(object); - assert!(reference.get_object()?.is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) @@ -611,14 +629,12 @@ mod tests { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.borrow_object()?.is_some()); - assert!(reference - .borrow_object()? - .is_some_and(|obj| obj.is(&object))); + assert!(reference.borrow_object().is_some()); + assert!(reference.borrow_object().is_some_and(|obj| obj.is(&object))); drop(object); - assert!(reference.borrow_object()?.is_none()); + assert!(reference.borrow_object().is_none()); Ok(()) }) @@ -630,11 +646,11 @@ mod tests { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.get_object_raw()?.is(&object)); + assert!(reference.get_object_raw().is(&object)); drop(object); - assert!(reference.get_object_raw()?.is_none()); + assert!(reference.get_object_raw().is_none()); Ok(()) }) @@ -646,11 +662,11 @@ mod tests { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.borrow_object_raw()?.is(&object)); + assert!(reference.borrow_object_raw().is(&object)); drop(object); - assert!(reference.borrow_object_raw()?.is_none()); + assert!(reference.borrow_object_raw().is_none()); Ok(()) }) diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 062c22903a5..d75cc044b27 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -58,7 +58,7 @@ impl PyWeakProxy { /// let weakref = PyWeakProxy::new_bound(py, foo.clone())?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object()? + /// weakref.get_object() /// .is_some_and(|obj| obj.is(&foo)) /// ); /// @@ -67,7 +67,7 @@ impl PyWeakProxy { /// /// drop(foo); /// - /// assert!(weakref.get_object()?.is_none()); + /// assert!(weakref.get_object().is_none()); /// Ok(()) /// }) /// # } @@ -144,7 +144,7 @@ impl PyWeakProxy { /// assert!(weakref.upgrade::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object()? + /// weakref.get_object() /// .is_some_and(|obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); @@ -240,6 +240,10 @@ impl PyWeakProxy { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy @@ -302,6 +306,10 @@ impl PyWeakProxy { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy @@ -331,7 +339,7 @@ impl PyWeakProxy { /// struct Foo { /* fields omitted */ } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { - /// if let Some(object) = reference.get_object()? { + /// if let Some(object) = reference.get_object() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -360,11 +368,15 @@ impl PyWeakProxy { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn get_object(&self) -> PyResult> { - Ok(self.as_borrowed().get_object()?.map(Bound::into_gil_ref)) + pub fn get_object(&self) -> Option<&'_ PyAny> { + self.as_borrowed().get_object().map(Bound::into_gil_ref) } /// Retrieve to a object pointed to by the weakref. @@ -384,7 +396,7 @@ impl PyWeakProxy { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { /// reference - /// .get_object_raw()? + /// .get_object_raw() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -410,17 +422,23 @@ impl PyWeakProxy { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn get_object_raw(&self) -> PyResult<&'_ PyAny> { - self.as_borrowed().get_object_raw().map(Bound::into_gil_ref) + pub fn get_object_raw(&self) -> &'_ PyAny { + self.as_borrowed().get_object_raw().into_gil_ref() } } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { - fn borrow_object_raw(&self) -> PyResult> { + fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny> { + // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } + .expect("The 'weakref.ProxyType' instance should be valid (non-null and actually a weakref reference)") } } @@ -442,7 +460,7 @@ mod tests { let reference = PyWeakProxy::new_bound(py, object.clone())?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw()?.is(&object)); + assert!(reference.get_object_raw().is(&object)); assert_eq!( reference.get_type().to_string(), "" @@ -472,7 +490,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw()?.is_none()); + assert!(reference.get_object_raw().is_none()); assert!(reference .getattr("__class__") .is_err_and(|err| err.is_instance_of::(py))); @@ -535,12 +553,12 @@ mod tests { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.get_object()?.is_some()); - assert!(reference.get_object()?.is_some_and(|obj| obj.is(&object))); + assert!(reference.get_object().is_some()); + assert!(reference.get_object().is_some_and(|obj| obj.is(&object))); drop(object); - assert!(reference.get_object()?.is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) @@ -552,14 +570,12 @@ mod tests { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.borrow_object()?.is_some()); - assert!(reference - .borrow_object()? - .is_some_and(|obj| obj.is(&object))); + assert!(reference.borrow_object().is_some()); + assert!(reference.borrow_object().is_some_and(|obj| obj.is(&object))); drop(object); - assert!(reference.borrow_object()?.is_none()); + assert!(reference.borrow_object().is_none()); Ok(()) }) @@ -571,11 +587,11 @@ mod tests { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.get_object_raw()?.is(&object)); + assert!(reference.get_object_raw().is(&object)); drop(object); - assert!(reference.get_object_raw()?.is_none()); + assert!(reference.get_object_raw().is_none()); Ok(()) }) @@ -587,11 +603,11 @@ mod tests { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.borrow_object_raw()?.is(&object)); + assert!(reference.borrow_object_raw().is(&object)); drop(object); - assert!(reference.borrow_object_raw()?.is_none()); + assert!(reference.borrow_object_raw().is_none()); Ok(()) }) diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index ee3928670ea..fbc54f56db1 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -55,7 +55,7 @@ impl PyWeakRef { /// let weakref = PyWeakRef::new_bound(py, foo.clone())?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object()? + /// weakref.get_object() /// .is_some_and(|obj| obj.is(&foo)) /// ); /// @@ -64,7 +64,7 @@ impl PyWeakRef { /// /// drop(foo); /// - /// assert!(weakref.get_object()?.is_none()); + /// assert!(weakref.get_object().is_none()); /// Ok(()) /// }) /// # } @@ -132,7 +132,7 @@ impl PyWeakRef { /// assert!(weakref.upgrade::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object()? + /// weakref.get_object() /// .is_some_and(|obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); @@ -223,6 +223,10 @@ impl PyWeakRef { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref @@ -285,6 +289,10 @@ impl PyWeakRef { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref @@ -314,7 +322,7 @@ impl PyWeakRef { /// struct Foo { /* fields omitted */ } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(object) = reference.get_object()? { + /// if let Some(object) = reference.get_object() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -343,11 +351,15 @@ impl PyWeakRef { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - pub fn get_object(&self) -> PyResult> { - Ok(self.as_borrowed().get_object()?.map(Bound::into_gil_ref)) + pub fn get_object(&self) -> Option<&'_ PyAny> { + self.as_borrowed().get_object().map(Bound::into_gil_ref) } /// Retrieve to a object pointed to by the weakref. @@ -367,7 +379,7 @@ impl PyWeakRef { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { /// reference - /// .get_object_raw()? + /// .get_object_raw() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -393,11 +405,15 @@ impl PyWeakRef { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - pub fn get_object_raw(&self) -> PyResult<&'_ PyAny> { - self.as_borrowed().get_object_raw().map(Bound::into_gil_ref) + pub fn get_object_raw(&self) -> &'_ PyAny { + self.as_borrowed().get_object_raw().into_gil_ref() } } @@ -460,6 +476,10 @@ pub trait PyWeakRefMethods<'py> { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref @@ -468,7 +488,7 @@ pub trait PyWeakRefMethods<'py> { T: PyTypeCheck, { Ok(self - .get_object()? + .get_object() .map(|obj| obj.downcast_into::()) .transpose()?) } @@ -528,6 +548,10 @@ pub trait PyWeakRefMethods<'py> { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref @@ -536,7 +560,7 @@ pub trait PyWeakRefMethods<'py> { T: PyTypeInfo, { Ok(self - .get_object()? + .get_object() .map(|obj| obj.downcast_into_exact::()) .transpose()?) } @@ -558,7 +582,7 @@ pub trait PyWeakRefMethods<'py> { /// struct Foo { /* fields omitted */ } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(object) = reference.get_object()? { + /// if let Some(object) = reference.get_object() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -587,13 +611,21 @@ pub trait PyWeakRefMethods<'py> { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn get_object(&self) -> PyResult>> { - let object = self.get_object_raw()?; + fn get_object(&self) -> Option> { + let object = self.get_object_raw(); - Ok(if object.is_none() { None } else { Some(object) }) + if object.is_none() { + None + } else { + Some(object) + } } // TODO: NAMING-ALTERNATIVE: upgrade_any_borrowed @@ -613,7 +645,7 @@ pub trait PyWeakRefMethods<'py> { /// struct Foo { /* fields omitted */ } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(object) = reference.borrow_object()? { + /// if let Some(object) = reference.borrow_object() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -642,16 +674,24 @@ pub trait PyWeakRefMethods<'py> { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn borrow_object<'a>(&'a self) -> PyResult>> + fn borrow_object<'a>(&'a self) -> Option> where 'py: 'a, { - let object = self.borrow_object_raw()?; + let object = self.borrow_object_raw(); - Ok(if object.is_none() { None } else { Some(object) }) + if object.is_none() { + None + } else { + Some(object) + } } // TODO: NAMING-ALTERNATIVE: get_any @@ -672,7 +712,7 @@ pub trait PyWeakRefMethods<'py> { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { /// reference - /// .get_object_raw()? + /// .get_object_raw() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -698,13 +738,16 @@ pub trait PyWeakRefMethods<'py> { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn get_object_raw(&self) -> PyResult> { - // Bound<'_, PyAny>::call0 could also be used in situations where ffi::PyWeakref_GetObject is not available. - // Only if it for weakref.ReferenceType - self.borrow_object_raw().map(Borrowed::to_owned) + fn get_object_raw(&self) -> Bound<'py, PyAny> { + // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. + self.borrow_object_raw().to_owned() } // TODO: NAMING-ALTERNATIVE: get_any_borrowed @@ -725,7 +768,7 @@ pub trait PyWeakRefMethods<'py> { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { /// reference - /// .borrow_object_raw()? + /// .borrow_object_raw() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -751,10 +794,14 @@ pub trait PyWeakRefMethods<'py> { /// # } /// ``` /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn borrow_object_raw(&self) -> PyResult>; + fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny>; } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { @@ -769,9 +816,10 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { } */ - fn borrow_object_raw(&self) -> PyResult> { - // &PyAny::call0 could also be used in situations where ffi::PyWeakref_GetObject is not available. + fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny> { + // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } + .expect("The 'weakref.ReferenceType' instance should be valid (non-null and actually a weakref reference)") } } @@ -792,7 +840,7 @@ mod tests { let reference = PyWeakRef::new_bound(py, object.clone())?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw()?.is(&object)); + assert!(reference.get_object_raw().is(&object)); assert_eq!( reference.get_type().to_string(), "" @@ -819,7 +867,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw()?.is_none()); + assert!(reference.get_object_raw().is_none()); assert_eq!( reference.getattr("__class__")?.to_string(), "" @@ -877,13 +925,13 @@ mod tests { let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; assert!(reference.call0()?.is(&object)); - assert!(reference.get_object()?.is_some()); - assert!(reference.get_object()?.is_some_and(|obj| obj.is(&object))); + assert!(reference.get_object().is_some()); + assert!(reference.get_object().is_some_and(|obj| obj.is(&object))); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.get_object()?.is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) @@ -896,15 +944,13 @@ mod tests { let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; assert!(reference.call0()?.is(&object)); - assert!(reference.borrow_object()?.is_some()); - assert!(reference - .borrow_object()? - .is_some_and(|obj| obj.is(&object))); + assert!(reference.borrow_object().is_some()); + assert!(reference.borrow_object().is_some_and(|obj| obj.is(&object))); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.borrow_object()?.is_none()); + assert!(reference.borrow_object().is_none()); Ok(()) }) @@ -917,13 +963,13 @@ mod tests { let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; assert!(reference.call0()?.is(&object)); - assert!(reference.get_object_raw()?.is(&object)); + assert!(reference.get_object_raw().is(&object)); drop(object); - assert!(reference.call0()?.is(&reference.get_object_raw()?)); + assert!(reference.call0()?.is(&reference.get_object_raw())); assert!(reference.call0()?.is_none()); - assert!(reference.get_object_raw()?.is_none()); + assert!(reference.get_object_raw().is_none()); Ok(()) }) @@ -936,12 +982,12 @@ mod tests { let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; assert!(reference.call0()?.is(&object)); - assert!(reference.borrow_object_raw()?.is(&object)); + assert!(reference.borrow_object_raw().is(&object)); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.borrow_object_raw()?.is_none()); + assert!(reference.borrow_object_raw().is_none()); Ok(()) }) From f18ba78082f23a45f577141b57f1c32cc3c89bfb Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:59:37 +0100 Subject: [PATCH 09/42] Add towncrier changes --- newsfragments/3835.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/3835.added.md diff --git a/newsfragments/3835.added.md b/newsfragments/3835.added.md new file mode 100644 index 00000000000..55871f4cdf7 --- /dev/null +++ b/newsfragments/3835.added.md @@ -0,0 +1 @@ +Add `PyWeakRef`, `PyWeakProxy` and `PyWeakCallableProxy`. From ee95efb6bc47b715a5eece817d7cede9982f18ce Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Wed, 14 Feb 2024 12:28:13 +0100 Subject: [PATCH 10/42] Update weakref type doctests to use `py.run_bound` --- src/types/weakref/callableproxy.rs | 4 ++-- src/types/weakref/proxy.rs | 4 ++-- src/types/weakref/reference.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 107434279b1..b4b0d2c6182 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -153,12 +153,12 @@ impl PyWeakCallableProxy { /// fn callback(wref: Bound<'_, PyWeakCallableProxy>) -> PyResult<()> { /// let py = wref.py(); /// assert!(wref.upgrade::()?.is_none()); - /// py.run("counter = 1", None, None) + /// py.run_bound("counter = 1", None, None) /// } /// /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { - /// py.run("counter = 0", None, None)?; + /// py.run_bound("counter = 0", None, None)?; /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// let foo = Bound::new(py, Foo{})?; /// diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index d75cc044b27..e8a7809e719 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -130,12 +130,12 @@ impl PyWeakProxy { /// fn callback(wref: Bound<'_, PyWeakProxy>) -> PyResult<()> { /// let py = wref.py(); /// assert!(wref.upgrade::()?.is_none()); - /// py.run("counter = 1", None, None) + /// py.run_bound("counter = 1", None, None) /// } /// /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { - /// py.run("counter = 0", None, None)?; + /// py.run_bound("counter = 0", None, None)?; /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// let foo = Bound::new(py, Foo{})?; /// diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index fbc54f56db1..d2510fcd88d 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -118,12 +118,12 @@ impl PyWeakRef { /// fn callback(wref: Bound<'_, PyWeakRef>) -> PyResult<()> { /// let py = wref.py(); /// assert!(wref.upgrade::()?.is_none()); - /// py.run("counter = 1", None, None) + /// py.run_bound("counter = 1", None, None) /// } /// /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { - /// py.run("counter = 0", None, None)?; + /// py.run_bound("counter = 0", None, None)?; /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// let foo = Bound::new(py, Foo{})?; /// From d28da4ee36cd370b6244e32961c02fa17e70baeb Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Thu, 15 Feb 2024 17:08:14 +0100 Subject: [PATCH 11/42] Fix to adhere to MSRV --- src/types/weakref/callableproxy.rs | 33 +++++++++++++++++----------- src/types/weakref/proxy.rs | 35 ++++++++++++++++-------------- src/types/weakref/reference.rs | 16 ++++++++------ 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index b4b0d2c6182..3f9f97bc4e8 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -67,7 +67,7 @@ impl PyWeakCallableProxy { /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() - /// .is_some_and(|obj| obj.is(&foo)) + /// .map_or(false, |obj| obj.is(&foo)) /// ); /// /// let weakref2 = PyWeakCallableProxy::new_bound(py, foo.clone())?; @@ -78,7 +78,10 @@ impl PyWeakCallableProxy { /// drop(foo); /// /// assert!(weakref.get_object().is_none()); - /// assert!(weakref.call0().is_err_and(|err| err.is_instance_of::(py))); + /// assert!(weakref.call0() + /// .err() + /// .map_or(false, |err| err.is_instance_of::(py)) + /// ); /// /// Ok(()) /// }) @@ -168,7 +171,7 @@ impl PyWeakCallableProxy { /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() - /// .is_some_and(|obj| obj.is(&foo)) + /// .map_or(false, |obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// @@ -543,7 +546,8 @@ mod tests { assert!(reference .getattr("__callback__") - .is_err_and(|err| err.is_instance_of::(py))); + .err() + .map_or(false, |err| err.is_instance_of::(py))); assert_eq!(reference.call0()?.to_string(), "This class is callable!"); @@ -552,7 +556,8 @@ mod tests { assert!(reference.get_object_raw().is_none()); assert!(reference .getattr("__class__") - .is_err_and(|err| err.is_instance_of::(py))); + .err() + .map_or(false, |err| err.is_instance_of::(py))); assert_eq!( reference.repr()?.to_string(), format!( @@ -564,12 +569,12 @@ mod tests { assert!(reference .getattr("__callback__") - .is_err_and(|err| err.is_instance_of::(py))); + .err() + .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference - .call0() - .is_err_and(|err| err.is_instance_of::(py) - & (err.value(py).to_string() == "weakly-referenced object no longer exists"))); + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "weakly-referenced object no longer exists"))); Ok(()) }) @@ -588,7 +593,7 @@ mod tests { let obj = obj.unwrap(); assert!(obj.is_some()); - assert!(obj.is_some_and(|obj| obj.as_ptr() == object.as_ptr())); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); } drop(object); @@ -613,7 +618,7 @@ mod tests { let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; assert!(reference.get_object().is_some()); - assert!(reference.get_object().is_some_and(|obj| obj.is(&object))); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); drop(object); @@ -630,7 +635,9 @@ mod tests { let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; assert!(reference.borrow_object().is_some()); - assert!(reference.borrow_object().is_some_and(|obj| obj.is(&object))); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); drop(object); diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index e8a7809e719..e249acb16ce 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -59,7 +59,7 @@ impl PyWeakProxy { /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() - /// .is_some_and(|obj| obj.is(&foo)) + /// .map_or(false, |obj| obj.is(&foo)) /// ); /// /// let weakref2 = PyWeakProxy::new_bound(py, foo.clone())?; @@ -145,7 +145,7 @@ impl PyWeakProxy { /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() - /// .is_some_and(|obj| obj.is(&foo)) + /// .map_or(false, |obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// @@ -481,19 +481,20 @@ mod tests { assert!(reference .getattr("__callback__") - .is_err_and(|err| err.is_instance_of::(py))); + .err() + .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference - .call0() - .is_err_and(|err| err.is_instance_of::(py) - & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); drop(object); assert!(reference.get_object_raw().is_none()); assert!(reference .getattr("__class__") - .is_err_and(|err| err.is_instance_of::(py))); + .err() + .map_or(false, |err| err.is_instance_of::(py))); assert_eq!( reference.repr()?.to_string(), format!( @@ -505,12 +506,12 @@ mod tests { assert!(reference .getattr("__callback__") - .is_err_and(|err| err.is_instance_of::(py))); + .err() + .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference - .call0() - .is_err_and(|err| err.is_instance_of::(py) - & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); Ok(()) }) @@ -529,7 +530,7 @@ mod tests { let obj = obj.unwrap(); assert!(obj.is_some()); - assert!(obj.is_some_and(|obj| obj.as_ptr() == object.as_ptr())); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); } drop(object); @@ -554,7 +555,7 @@ mod tests { let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; assert!(reference.get_object().is_some()); - assert!(reference.get_object().is_some_and(|obj| obj.is(&object))); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); drop(object); @@ -571,7 +572,9 @@ mod tests { let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; assert!(reference.borrow_object().is_some()); - assert!(reference.borrow_object().is_some_and(|obj| obj.is(&object))); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); drop(object); diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index d2510fcd88d..ecd4dd624bf 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -56,7 +56,7 @@ impl PyWeakRef { /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() - /// .is_some_and(|obj| obj.is(&foo)) + /// .map_or(false, |obj| obj.is(&foo)) /// ); /// /// let weakref2 = PyWeakRef::new_bound(py, foo.clone())?; @@ -133,7 +133,7 @@ impl PyWeakRef { /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() - /// .is_some_and(|obj| obj.is(&foo)) + /// .map_or(false, |obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// @@ -861,7 +861,7 @@ mod tests { assert!(reference .getattr("__callback__") - .is_ok_and(|result| result.is_none())); + .map_or(false, |result| result.is_none())); assert!(reference.call0()?.is(&object)); @@ -879,7 +879,7 @@ mod tests { assert!(reference .getattr("__callback__") - .is_ok_and(|result| result.is_none())); + .map_or(false, |result| result.is_none())); assert!(reference.call0()?.is_none()); @@ -900,7 +900,7 @@ mod tests { let obj = obj.unwrap(); assert!(obj.is_some()); - assert!(obj.is_some_and(|obj| obj.as_ptr() == object.as_ptr())); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); } drop(object); @@ -926,7 +926,7 @@ mod tests { assert!(reference.call0()?.is(&object)); assert!(reference.get_object().is_some()); - assert!(reference.get_object().is_some_and(|obj| obj.is(&object))); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); drop(object); @@ -945,7 +945,9 @@ mod tests { assert!(reference.call0()?.is(&object)); assert!(reference.borrow_object().is_some()); - assert!(reference.borrow_object().is_some_and(|obj| obj.is(&object))); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); drop(object); From dff0914dd03106c92ff96b1aef4c3e267e17f5e6 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Thu, 15 Feb 2024 18:09:14 +0100 Subject: [PATCH 12/42] Make weakref tests independent from macros feature --- src/types/weakref/callableproxy.rs | 447 ++++++++++++++++++++-------- src/types/weakref/proxy.rs | 439 +++++++++++++++++++-------- src/types/weakref/reference.rs | 458 ++++++++++++++++++++--------- 3 files changed, 953 insertions(+), 391 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 3f9f97bc4e8..ebc96978d81 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -503,179 +503,368 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakCallableProxy> { #[cfg(test)] mod tests { use crate::exceptions::{PyAttributeError, PyReferenceError}; - use crate::prelude::{pyclass, pymethods, Py, PyResult, Python}; use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakCallableProxy, PyWeakRefMethods}; - use crate::Bound; + use crate::{Bound, PyResult, Python}; + + mod python_class { + use super::*; + use crate::{py_result_ext::PyResultExt, types::PyType, PyAny}; + + fn get_type(py: Python<'_>) -> PyResult> { + py.run_bound( + "class A:\n def __call__(self):\n return 'This class is callable!'\n", + None, + None, + )?; + py.eval_bound("A", None, None).downcast_into::() + } + + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + + assert!(!reference.is(&object)); + assert!(reference.get_object_raw().is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert_eq!(reference.call0()?.to_string(), "This class is callable!"); + + drop(object); + + assert!(reference.get_object_raw().is_none()); + assert!(reference + .getattr("__class__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + py.None().as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "weakly-referenced object no longer exists"))); + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + + { + // This test is a bit weird but ok. + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } - #[pyclass(weakref, crate = "crate")] - struct WeakrefablePyClass {} + drop(object); - #[pymethods(crate = "crate")] - impl WeakrefablePyClass { - fn __call__(&self) -> &str { - "This class is callable!" + { + // This test is a bit weird but ok. + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) } - } - #[test] - fn test_weakref_proxy_behavior() -> PyResult<()> { - Python::with_gil(|py| { - let object = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; - assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); - assert_eq!( - reference.get_type().to_string(), - "" - ); + assert!(reference.get_object().is_some()); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); + drop(object); - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); + assert!(reference.get_object().is_none()); - assert_eq!(reference.call0()?.to_string(), "This class is callable!"); + Ok(()) + }) + } - drop(object); + #[test] + fn test_weakref_borrrow_object() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; - assert!(reference.get_object_raw().is_none()); - assert!(reference - .getattr("__class__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - py.None().as_ptr() - ) - ); + assert!(reference.borrow_object().is_some()); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); + drop(object); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "weakly-referenced object no longer exists"))); + assert!(reference.borrow_object().is_none()); - Ok(()) - }) - } + Ok(()) + }) + } - #[test] - fn test_weakref_upgrade() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + #[test] + fn test_weakref_get_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; - { - let obj = reference.upgrade::(); + assert!(reference.get_object_raw().is(&object)); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + drop(object); - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } + assert!(reference.get_object_raw().is_none()); - drop(object); + Ok(()) + }) + } - { - let obj = reference.upgrade::(); + #[test] + fn test_weakref_borrow_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(reference.borrow_object_raw().is(&object)); - assert!(obj.is_none()); - } + drop(object); + + assert!(reference.borrow_object_raw().is_none()); - Ok(()) - }) + Ok(()) + }) + } } - #[test] - fn test_weakref_get_object() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + #[cfg(feature = "macros")] + mod pyo3_pyclass { + use super::*; + use crate::{pyclass, pymethods, Py}; - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} - drop(object); + #[pymethods(crate = "crate")] + impl WeakrefablePyClass { + fn __call__(&self) -> &str { + "This class is callable!" + } + } - assert!(reference.get_object().is_none()); + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let object = Bound::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + + assert!(!reference.is(&object)); + assert!(reference.get_object_raw().is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert_eq!(reference.call0()?.to_string(), "This class is callable!"); + + drop(object); + + assert!(reference.get_object_raw().is_none()); + assert!(reference + .getattr("__class__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + py.None().as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "weakly-referenced object no longer exists"))); + + Ok(()) + }) + } - Ok(()) - }) - } + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; - #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + { + let obj = reference.upgrade::(); - assert!(reference.borrow_object().is_some()); - assert!(reference - .borrow_object() - .map_or(false, |obj| obj.is(&object))); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - drop(object); + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } - assert!(reference.borrow_object().is_none()); + drop(object); - Ok(()) - }) - } + { + let obj = reference.upgrade::(); - #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(reference.get_object_raw().is(&object)); + assert!(obj.is_none()); + } - drop(object); + Ok(()) + }) + } - assert!(reference.get_object_raw().is_none()); + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; - Ok(()) - }) - } + assert!(reference.get_object().is_some()); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.get_object().is_none()); - #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + Ok(()) + }) + } + + #[test] + fn test_weakref_borrrow_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.borrow_object_raw().is(&object)); + assert!(reference.borrow_object().is_some()); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); - drop(object); + drop(object); - assert!(reference.borrow_object_raw().is_none()); + assert!(reference.borrow_object().is_none()); - Ok(()) - }) + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.get_object_raw().is(&object)); + + drop(object); + + assert!(reference.get_object_raw().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrow_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.borrow_object_raw().is(&object)); + + drop(object); + + assert!(reference.borrow_object_raw().is_none()); + + Ok(()) + }) + } } } diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index e249acb16ce..aa82a62ac3c 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -445,174 +445,361 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { #[cfg(test)] mod tests { use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError}; - use crate::prelude::{pyclass, Py, PyResult, Python}; use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakProxy, PyWeakRefMethods}; - use crate::Bound; + use crate::{Bound, PyResult, Python}; - #[pyclass(weakref, crate = "crate")] - struct WeakrefablePyClass {} + mod python_class { + use super::*; + use crate::{py_result_ext::PyResultExt, types::PyType, PyAny}; - #[test] - fn test_weakref_proxy_behavior() -> PyResult<()> { - Python::with_gil(|py| { - let object = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone())?; + fn get_type(py: Python<'_>) -> PyResult> { + py.run_bound("class A:\n pass\n", None, None)?; + py.eval_bound("A", None, None).downcast_into::() + } - assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); - assert_eq!( - reference.get_type().to_string(), - "" - ); + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(py, object.clone())?; + + assert!(!reference.is(&object)); + assert!(reference.get_object_raw().is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + + drop(object); + + assert!(reference.get_object_raw().is_none()); + assert!(reference + .getattr("__class__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + py.None().as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + + Ok(()) + }) + } - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(py, object.clone())?; - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); + { + // This test is a bit weird but ok. + let obj = reference.upgrade::(); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - drop(object); + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } - assert!(reference.get_object_raw().is_none()); - assert!(reference - .getattr("__class__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - py.None().as_ptr() - ) - ); + drop(object); - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); + { + // This test is a bit weird but ok. + let obj = reference.upgrade::(); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - Ok(()) - }) - } + assert!(obj.is_none()); + } + + Ok(()) + }) + } - #[test] - fn test_weakref_upgrade() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(py, object.clone())?; - { - let obj = reference.upgrade::(); + assert!(reference.get_object().is_some()); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + drop(object); - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } + assert!(reference.get_object().is_none()); - drop(object); + Ok(()) + }) + } - { - let obj = reference.upgrade::(); + #[test] + fn test_weakref_borrrow_object() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(py, object.clone())?; - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(reference.borrow_object().is_some()); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); - assert!(obj.is_none()); - } + drop(object); - Ok(()) - }) - } + assert!(reference.borrow_object().is_none()); - #[test] - fn test_weakref_get_object() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + Ok(()) + }) + } - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + #[test] + fn test_weakref_get_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(py, object.clone())?; - drop(object); + assert!(reference.get_object_raw().is(&object)); - assert!(reference.get_object().is_none()); + drop(object); - Ok(()) - }) - } + assert!(reference.get_object_raw().is_none()); + + Ok(()) + }) + } - #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + #[test] + fn test_weakref_borrow_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(py, object.clone())?; - assert!(reference.borrow_object().is_some()); - assert!(reference - .borrow_object() - .map_or(false, |obj| obj.is(&object))); + assert!(reference.borrow_object_raw().is(&object)); - drop(object); + drop(object); - assert!(reference.borrow_object().is_none()); + assert!(reference.borrow_object_raw().is_none()); - Ok(()) - }) + Ok(()) + }) + } } - #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + #[cfg(feature = "macros")] + mod pyo3_pyclass { + use super::*; + use crate::{pyclass, Py}; + + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} + + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let object = Bound::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone())?; + + assert!(!reference.is(&object)); + assert!(reference.get_object_raw().is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + + drop(object); + + assert!(reference.get_object_raw().is_none()); + assert!(reference + .getattr("__class__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + py.None().as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| err + .is_instance_of::(py) + & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; - assert!(reference.get_object_raw().is(&object)); + { + let obj = reference.upgrade::(); - drop(object); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(reference.get_object_raw().is_none()); + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } - Ok(()) - }) - } + drop(object); - #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + { + let obj = reference.upgrade::(); - assert!(reference.borrow_object_raw().is(&object)); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - drop(object); + assert!(obj.is_none()); + } - assert!(reference.borrow_object_raw().is_none()); + Ok(()) + }) + } - Ok(()) - }) + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.get_object().is_some()); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.get_object().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrrow_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.borrow_object().is_some()); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.borrow_object().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.get_object_raw().is(&object)); + + drop(object); + + assert!(reference.get_object_raw().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrow_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + + assert!(reference.borrow_object_raw().is(&object)); + + drop(object); + + assert!(reference.borrow_object_raw().is_none()); + + Ok(()) + }) + } } } diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index ecd4dd624bf..8c3bc0a0a23 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -825,173 +825,359 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { #[cfg(test)] mod tests { - use crate::prelude::{pyclass, Py, Python}; use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; - use crate::{Bound, PyResult}; - - #[pyclass(weakref, crate = "crate")] - struct WeakrefablePyClass {} - - #[test] - fn test_weakref_refence_behavior() -> PyResult<()> { - Python::with_gil(|py| { - let object = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone())?; - - assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); - assert_eq!( - reference.get_type().to_string(), - "" - ); - - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); - - assert!(reference - .getattr("__callback__") - .map_or(false, |result| result.is_none())); - - assert!(reference.call0()?.is(&object)); - - drop(object); - - assert!(reference.get_object_raw().is_none()); - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); - assert_eq!( - reference.repr()?.to_string(), - format!("", reference.as_ptr()) - ); - - assert!(reference - .getattr("__callback__") - .map_or(false, |result| result.is_none())); - - assert!(reference.call0()?.is_none()); - - Ok(()) - }) - } + use crate::{Bound, PyResult, Python}; - #[test] - fn test_weakref_upgrade() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + mod python_class { + use super::*; + use crate::{py_result_ext::PyResultExt, types::PyType, PyAny}; - { - let obj = reference.upgrade::(); + fn get_type(py: Python<'_>) -> PyResult> { + py.run_bound("class A:\n pass\n", None, None)?; + py.eval_bound("A", None, None).downcast_into::() + } - assert!(obj.is_ok()); - let obj = obj.unwrap(); + #[test] + fn test_weakref_refence_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(py, object.clone())?; - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } + assert!(!reference.is(&object)); + assert!(reference.get_object_raw().is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); - drop(object); + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); - { - let obj = reference.upgrade::(); + assert!(reference + .getattr("__callback__") + .map_or(false, |result| result.is_none())); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(reference.call0()?.is(&object)); - assert!(obj.is_none()); - } + drop(object); - Ok(()) - }) - } + assert!(reference.get_object_raw().is_none()); + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!("", reference.as_ptr()) + ); - #[test] - fn test_weakref_get_object() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + assert!(reference + .getattr("__callback__") + .map_or(false, |result| result.is_none())); - assert!(reference.call0()?.is(&object)); - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + assert!(reference.call0()?.is_none()); - drop(object); + Ok(()) + }) + } - assert!(reference.call0()?.is_none()); - assert!(reference.get_object().is_none()); + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(py, object.clone())?; - Ok(()) - }) - } + { + // This test is a bit weird but ok. + let obj = reference.upgrade::(); - #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(reference.call0()?.is(&object)); - assert!(reference.borrow_object().is_some()); - assert!(reference - .borrow_object() - .map_or(false, |obj| obj.is(&object))); + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } - drop(object); + drop(object); - assert!(reference.call0()?.is_none()); - assert!(reference.borrow_object().is_none()); + { + // This test is a bit weird but ok. + let obj = reference.upgrade::(); - Ok(()) - }) - } + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } - #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(py, object.clone())?; - assert!(reference.call0()?.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.call0()?.is(&object)); + assert!(reference.get_object().is_some()); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); - drop(object); + drop(object); + + assert!(reference.call0()?.is_none()); + assert!(reference.get_object().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrrow_object() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(py, object.clone())?; + + assert!(reference.call0()?.is(&object)); + assert!(reference.borrow_object().is_some()); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.call0()?.is_none()); + assert!(reference.borrow_object().is_none()); + + Ok(()) + }) + } - assert!(reference.call0()?.is(&reference.get_object_raw())); - assert!(reference.call0()?.is_none()); - assert!(reference.get_object_raw().is_none()); + #[test] + fn test_weakref_get_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(py, object.clone())?; - Ok(()) - }) + assert!(reference.call0()?.is(&object)); + assert!(reference.get_object_raw().is(&object)); + + drop(object); + + assert!(reference.call0()?.is(&reference.get_object_raw())); + assert!(reference.call0()?.is_none()); + assert!(reference.get_object_raw().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrow_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(py, object.clone())?; + + assert!(reference.call0()?.is(&object)); + assert!(reference.borrow_object_raw().is(&object)); + + drop(object); + + assert!(reference.call0()?.is_none()); + assert!(reference.borrow_object_raw().is_none()); + + Ok(()) + }) + } } - #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + #[cfg(feature = "macros")] + mod pyo3_pyclass { + use super::*; + use crate::{pyclass, Py}; + + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} + + #[test] + fn test_weakref_refence_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let object = Bound::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone())?; + + assert!(!reference.is(&object)); + assert!(reference.get_object_raw().is(&object)); + assert_eq!( + reference.get_type().to_string(), + "" + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!( + "", + reference.as_ptr(), + object.as_ptr() + ) + ); + + assert!(reference + .getattr("__callback__") + .map_or(false, |result| result.is_none())); + + assert!(reference.call0()?.is(&object)); + + drop(object); - assert!(reference.call0()?.is(&object)); - assert!(reference.borrow_object_raw().is(&object)); + assert!(reference.get_object_raw().is_none()); + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + assert_eq!( + reference.repr()?.to_string(), + format!("", reference.as_ptr()) + ); - drop(object); + assert!(reference + .getattr("__callback__") + .map_or(false, |result| result.is_none())); - assert!(reference.call0()?.is_none()); - assert!(reference.borrow_object_raw().is_none()); + assert!(reference.call0()?.is_none()); - Ok(()) - }) + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + + { + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = reference.upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + + assert!(reference.call0()?.is(&object)); + assert!(reference.get_object().is_some()); + assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.call0()?.is_none()); + assert!(reference.get_object().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrrow_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + + assert!(reference.call0()?.is(&object)); + assert!(reference.borrow_object().is_some()); + assert!(reference + .borrow_object() + .map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.call0()?.is_none()); + assert!(reference.borrow_object().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + + assert!(reference.call0()?.is(&object)); + assert!(reference.get_object_raw().is(&object)); + + drop(object); + + assert!(reference.call0()?.is(&reference.get_object_raw())); + assert!(reference.call0()?.is_none()); + assert!(reference.get_object_raw().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_borrow_object_raw() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + + assert!(reference.call0()?.is(&object)); + assert!(reference.borrow_object_raw().is(&object)); + + drop(object); + + assert!(reference.call0()?.is_none()); + assert!(reference.borrow_object_raw().is_none()); + + Ok(()) + }) + } } } From e06e383b76de72e3f80130b291eecc067c0d4d16 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:04:18 +0100 Subject: [PATCH 13/42] Change Weakref tests --- src/types/weakref/callableproxy.rs | 67 ++++++++++++++--------------- src/types/weakref/proxy.rs | 67 ++++++++++++++--------------- src/types/weakref/reference.rs | 69 +++++++++++++++++++----------- 3 files changed, 109 insertions(+), 94 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index ebc96978d81..f2a7f050eae 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -505,11 +505,38 @@ mod tests { use crate::exceptions::{PyAttributeError, PyReferenceError}; use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakCallableProxy, PyWeakRefMethods}; - use crate::{Bound, PyResult, Python}; + use crate::{Bound, PyAny, PyResult, Python}; + + fn check_repr( + reference: &Bound<'_, PyWeakCallableProxy>, + object: &Bound<'_, PyAny>, + class: &str, + ) -> PyResult<()> { + let repr = reference.repr()?.to_string(); + let (first_part, second_part) = repr.split_once(" to ").unwrap(); + + { + let (msg, addr) = first_part.split_once("0x").unwrap(); + + assert_eq!(msg, ") -> PyResult> { py.run_bound( @@ -538,14 +565,7 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); + check_repr(&reference, &object, "A")?; assert!(reference .getattr("__callback__") @@ -561,14 +581,7 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - py.None().as_ptr() - ) - ); + check_repr(&reference, &py.None().bind(py), "NoneType")?; assert!(reference .getattr("__callback__") @@ -723,14 +736,7 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); + check_repr(&reference, object.as_any(), "builtins.WeakrefablePyClass")?; assert!(reference .getattr("__callback__") @@ -746,14 +752,7 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - py.None().as_ptr() - ) - ); + check_repr(&reference, py.None().bind(py), "NoneType")?; assert!(reference .getattr("__callback__") diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index aa82a62ac3c..7cd9dcc8391 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -447,11 +447,38 @@ mod tests { use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError}; use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakProxy, PyWeakRefMethods}; - use crate::{Bound, PyResult, Python}; + use crate::{Bound, PyAny, PyResult, Python}; + + fn check_repr( + reference: &Bound<'_, PyWeakProxy>, + object: &Bound<'_, PyAny>, + class: &str, + ) -> PyResult<()> { + let repr = reference.repr()?.to_string(); + let (first_part, second_part) = repr.split_once(" to ").unwrap(); + + { + let (msg, addr) = first_part.split_once("0x").unwrap(); + + assert_eq!(msg, ") -> PyResult> { py.run_bound("class A:\n pass\n", None, None)?; @@ -476,14 +503,7 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); + check_repr(&reference, &object, "A")?; assert!(reference .getattr("__callback__") @@ -501,14 +521,7 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - py.None().as_ptr() - ) - ); + check_repr(&reference, py.None().bind(py), "NoneType")?; assert!(reference .getattr("__callback__") @@ -656,14 +669,7 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); + check_repr(&reference, object.as_any(), "builtins.WeakrefablePyClass")?; assert!(reference .getattr("__callback__") @@ -681,14 +687,7 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - py.None().as_ptr() - ) - ); + check_repr(&reference, py.None().bind(py), "NoneType")?; assert!(reference .getattr("__callback__") diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 8c3bc0a0a23..01cd424f755 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -827,11 +827,44 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { mod tests { use crate::types::any::PyAnyMethods; use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; - use crate::{Bound, PyResult, Python}; + use crate::{Bound, PyAny, PyResult, Python}; + + fn check_repr( + reference: &Bound<'_, PyWeakRef>, + object: Option<(&Bound<'_, PyAny>, &str)>, + ) -> PyResult<()> { + let repr = reference.repr()?.to_string(); + let (first_part, second_part) = repr.split_once("; ").unwrap(); + + { + let (msg, addr) = first_part.split_once("0x").unwrap(); + + assert_eq!(msg, " { + let (msg, addr) = second_part.split_once("0x").unwrap(); + + assert_eq!(msg, format!("to '{}' at ", class)); + assert!(addr + .to_lowercase() + .contains(format!("{:x?}", object.as_ptr()).split_at(2).1)); + } + None => { + assert_eq!(second_part, "dead>") + } + } + + Ok(()) + } mod python_class { use super::*; - use crate::{py_result_ext::PyResultExt, types::PyType, PyAny}; + use crate::{py_result_ext::PyResultExt, types::PyType}; fn get_type(py: Python<'_>) -> PyResult> { py.run_bound("class A:\n pass\n", None, None)?; @@ -856,14 +889,8 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); + + check_repr(&reference, Some((object.as_any(), "A")))?; assert!(reference .getattr("__callback__") @@ -878,10 +905,7 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - assert_eq!( - reference.repr()?.to_string(), - format!("", reference.as_ptr()) - ); + check_repr(&reference, None)?; assert!(reference .getattr("__callback__") @@ -1035,14 +1059,10 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - assert_eq!( - reference.repr()?.to_string(), - format!( - "", - reference.as_ptr(), - object.as_ptr() - ) - ); + check_repr( + &reference, + Some((object.as_any(), "builtins.WeakrefablePyClass")), + )?; assert!(reference .getattr("__callback__") @@ -1057,10 +1077,7 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - assert_eq!( - reference.repr()?.to_string(), - format!("", reference.as_ptr()) - ); + check_repr(&reference, None)?; assert!(reference .getattr("__callback__") From f140841950ebfaa70d736318badb4ae0c46591bd Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:32:36 +0100 Subject: [PATCH 14/42] Remove forgotten Debug marcos --- src/types/weakref/callableproxy.rs | 4 ++-- src/types/weakref/proxy.rs | 4 ++-- src/types/weakref/reference.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index f2a7f050eae..5026ca57c36 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -519,9 +519,9 @@ mod tests { let (msg, addr) = first_part.split_once("0x").unwrap(); assert_eq!(msg, " Date: Fri, 8 Mar 2024 15:15:18 +0100 Subject: [PATCH 15/42] Processed .gitignore and PyResultExt feedback --- .gitignore | 1 - src/types/weakref/callableproxy.rs | 5 +++-- src/types/weakref/proxy.rs | 5 +++-- src/types/weakref/reference.rs | 9 +++------ 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 5004209d241..4240d326f71 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,3 @@ valgrind-python.supp lcov.info netlify_build/ .nox/ -.vscode/ diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 5026ca57c36..16937f5a40a 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -1,5 +1,6 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; +use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; @@ -105,7 +106,7 @@ impl PyWeakCallableProxy { ); Bound::from_owned_ptr_or_err(py, ffi::PyWeakref_NewProxy(ptr, ffi::Py_None())) - .map(|obj| obj.downcast_into_unchecked()) + .downcast_into_unchecked() } } @@ -212,7 +213,7 @@ impl PyWeakCallableProxy { py, ffi::PyWeakref_NewProxy(ptr, callback.to_object(py).as_ptr()), ) - .map(|obj| obj.downcast_into_unchecked()) + .downcast_into_unchecked() } } diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 76ed3d44f78..951df8e524f 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -1,5 +1,6 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; +use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; @@ -90,7 +91,7 @@ impl PyWeakProxy { ); Bound::from_owned_ptr_or_err(py, ffi::PyWeakref_NewProxy(ptr, ffi::Py_None())) - .map(|obj| obj.downcast_into_unchecked()) + .downcast_into_unchecked() } } @@ -184,7 +185,7 @@ impl PyWeakProxy { py, ffi::PyWeakref_NewProxy(ptr, callback.to_object(py).as_ptr()), ) - .map(|obj| obj.downcast_into_unchecked()) + .downcast_into_unchecked() } } diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 6fa57820201..043b61e4eb2 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -1,5 +1,6 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; +use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; use crate::{ffi, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; @@ -21,7 +22,6 @@ pyobject_native_type!( impl PyWeakRef { /// Deprecated form of [`PyWeakRef::new_bound`]. #[inline] - #[track_caller] #[cfg_attr( not(feature = "gil-refs"), deprecated( @@ -69,7 +69,6 @@ impl PyWeakRef { /// }) /// # } /// ``` - #[track_caller] pub fn new_bound(py: Python<'_>, object: T) -> PyResult> where T: ToPyObject, @@ -79,13 +78,12 @@ impl PyWeakRef { py, ffi::PyWeakref_NewRef(object.to_object(py).as_ptr(), ffi::Py_None()), ) - .map(|obj| obj.downcast_into_unchecked()) + .downcast_into_unchecked() } } /// Deprecated form of [`PyWeakRef::new_bound_with`]. #[inline] - #[track_caller] #[cfg_attr( not(feature = "gil-refs"), deprecated( @@ -149,7 +147,6 @@ impl PyWeakRef { /// }) /// # } /// ``` - #[track_caller] pub fn new_bound_with( py: Python<'_>, object: T, @@ -167,7 +164,7 @@ impl PyWeakRef { callback.to_object(py).as_ptr(), ), ) - .map(|obj| obj.downcast_into_unchecked()) + .downcast_into_unchecked() } } From 130c800304e77ab2638a5ff74bcc8f9939f76f07 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:04:52 +0100 Subject: [PATCH 16/42] Change new methods of weakref types to remove dangling pointers --- src/types/weakref/callableproxy.rs | 114 ++++++++++++++------------- src/types/weakref/proxy.rs | 111 ++++++++++++++------------ src/types/weakref/reference.rs | 120 +++++++++++++++-------------- 3 files changed, 184 insertions(+), 161 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 16937f5a40a..89ffa5f905a 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -3,7 +3,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; -use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, ToPyObject}; use super::PyWeakRefMethods; @@ -32,11 +32,11 @@ impl PyWeakCallableProxy { note = "`PyWeakCallableProxy::new` will be replaced by `PyWeakCallableProxy::new_bound` in a future PyO3 version" ) )] - pub fn new(py: Python<'_>, object: T) -> PyResult<&'_ PyWeakCallableProxy> + pub fn new(object: &T) -> PyResult<&PyWeakCallableProxy> where - T: ToPyObject, + T: PyNativeType, { - Self::new_bound(py, object).map(Bound::into_gil_ref) + Self::new_bound(&object.as_borrowed()).map(Bound::into_gil_ref) } /// Constructs a new Weak callable Reference (`weakref.proxy`/`weakref.CallableProxyType`) for the given object. @@ -64,14 +64,14 @@ impl PyWeakCallableProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let foo = Bound::new(py, Foo {})?; - /// let weakref = PyWeakCallableProxy::new_bound(py, foo.clone())?; + /// let weakref = PyWeakCallableProxy::new_bound(&foo)?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// - /// let weakref2 = PyWeakCallableProxy::new_bound(py, foo.clone())?; + /// let weakref2 = PyWeakCallableProxy::new_bound(&foo)?; /// assert!(weakref.is(&weakref2)); /// /// assert_eq!(weakref.call0()?.to_string(), "This class is callable"); @@ -94,20 +94,24 @@ impl PyWeakCallableProxy { /// #[inline] #[track_caller] - pub fn new_bound(py: Python<'_>, object: T) -> PyResult> - where - T: ToPyObject, - { - unsafe { - let ptr = object.to_object(py).as_ptr(); - assert_eq!( - ffi::PyCallable_Check(ptr), 1, + pub fn new_bound<'py, T>(object: &Bound<'py, T>) -> PyResult> { + // TODO: Must track caller be used here? + fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { + assert!( + object.is_callable(), "An object to be referenced by a PyWeakCallableProxy should be callable. Use PyWeakProxy instead." ); - Bound::from_owned_ptr_or_err(py, ffi::PyWeakref_NewProxy(ptr, ffi::Py_None())) + unsafe { + Bound::from_owned_ptr_or_err( + object.py(), + ffi::PyWeakref_NewProxy(object.as_ptr(), ffi::Py_None()), + ) .downcast_into_unchecked() + } } + + inner(object.as_any()) } /// Deprecated form of [`PyWeakCallableProxy::new_bound_with`]. @@ -120,16 +124,12 @@ impl PyWeakCallableProxy { note = "`PyWeakCallableProxy::new_with` will be replaced by `PyWeakCallableProxy::new_bound_with` in a future PyO3 version" ) )] - pub fn new_with( - py: Python<'_>, - object: T, - callback: C, - ) -> PyResult<&'_ PyWeakCallableProxy> + pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakCallableProxy> where - T: ToPyObject, + T: PyNativeType, C: ToPyObject, { - Self::new_bound_with(py, object, callback).map(Bound::into_gil_ref) + Self::new_bound_with(&object.as_borrowed(), callback).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.CallableProxyType`) for the given object with a callback. @@ -167,7 +167,7 @@ impl PyWeakCallableProxy { /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. - /// let weakref = PyWeakCallableProxy::new_bound_with(py, foo.clone(), py.None())?; + /// let weakref = PyWeakCallableProxy::new_bound_with(&foo, py.None())?; /// assert!(weakref.upgrade::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` @@ -176,7 +176,7 @@ impl PyWeakCallableProxy { /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakCallableProxy::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; + /// let weakref2 = PyWeakCallableProxy::new_bound_with(&foo, wrap_pyfunction!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// @@ -193,28 +193,34 @@ impl PyWeakCallableProxy { /// # Panics /// This function panics if the provided object is not callable. #[track_caller] - pub fn new_bound_with( - py: Python<'_>, - object: T, + pub fn new_bound_with<'py, T, C>( + object: &Bound<'py, T>, callback: C, - ) -> PyResult> + ) -> PyResult> where - T: ToPyObject, C: ToPyObject, { - unsafe { - let ptr = object.to_object(py).as_ptr(); - assert_eq!( - ffi::PyCallable_Check(ptr), 1, + // TODO: Must track caller be used here? + fn inner<'py>( + object: &Bound<'py, PyAny>, + callback: Bound<'py, PyAny>, + ) -> PyResult> { + assert!( + object.is_callable(), "An object to be referenced by a PyWeakCallableProxy should be callable. Use PyWeakProxy instead." ); - Bound::from_owned_ptr_or_err( - py, - ffi::PyWeakref_NewProxy(ptr, callback.to_object(py).as_ptr()), - ) - .downcast_into_unchecked() + unsafe { + Bound::from_owned_ptr_or_err( + object.py(), + ffi::PyWeakref_NewProxy(object.as_ptr(), callback.as_ptr()), + ) + .downcast_into_unchecked() + } } + + let py = object.py(); + inner(object.as_any(), callback.to_object(py).into_bound(py)) } /// Upgrade the weakref to a direct object reference. @@ -254,7 +260,7 @@ impl PyWeakCallableProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(py, data.clone())?; + /// let reference = PyWeakCallableProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -326,7 +332,7 @@ impl PyWeakCallableProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(py, data.clone())?; + /// let reference = PyWeakCallableProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -397,7 +403,7 @@ impl PyWeakCallableProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(py, data.clone())?; + /// let reference = PyWeakCallableProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -463,7 +469,7 @@ impl PyWeakCallableProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + /// let reference = PyWeakCallableProxy::new_bound(&object)?; /// /// assert_eq!( /// get_class(reference.as_borrowed())?, @@ -553,7 +559,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); @@ -602,7 +608,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + let reference = PyWeakCallableProxy::new_bound(&object)?; { // This test is a bit weird but ok. @@ -637,7 +643,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(reference.get_object().is_some()); assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); @@ -655,7 +661,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(reference.borrow_object().is_some()); assert!(reference @@ -675,7 +681,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(reference.get_object_raw().is(&object)); @@ -692,7 +698,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(reference.borrow_object_raw().is(&object)); @@ -724,7 +730,7 @@ mod tests { fn test_weakref_proxy_behavior() -> PyResult<()> { Python::with_gil(|py| { let object = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone())?; + let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); @@ -772,7 +778,7 @@ mod tests { fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; { let obj = reference.upgrade::(); @@ -803,7 +809,7 @@ mod tests { fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; assert!(reference.get_object().is_some()); assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); @@ -820,7 +826,7 @@ mod tests { fn test_weakref_borrrow_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; assert!(reference.borrow_object().is_some()); assert!(reference @@ -839,7 +845,7 @@ mod tests { fn test_weakref_get_object_raw() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; assert!(reference.get_object_raw().is(&object)); @@ -855,7 +861,7 @@ mod tests { fn test_weakref_borrow_object_raw() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; assert!(reference.borrow_object_raw().is(&object)); diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 951df8e524f..d0a2e2e3983 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -3,7 +3,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; -use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, ToPyObject}; use super::PyWeakRefMethods; @@ -32,11 +32,11 @@ impl PyWeakProxy { note = "`PyWeakProxy::new` will be replaced by `PyWeakProxy::new_bound` in a future PyO3 version" ) )] - pub fn new(py: Python<'_>, object: T) -> PyResult<&'_ PyWeakProxy> + pub fn new(object: &T) -> PyResult<&PyWeakProxy> where - T: ToPyObject, + T: PyNativeType, { - Self::new_bound(py, object).map(Bound::into_gil_ref) + Self::new_bound(&object.as_borrowed()).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`) for the given object. @@ -56,14 +56,14 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let foo = Bound::new(py, Foo {})?; - /// let weakref = PyWeakProxy::new_bound(py, foo.clone())?; + /// let weakref = PyWeakProxy::new_bound(&foo)?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// - /// let weakref2 = PyWeakProxy::new_bound(py, foo.clone())?; + /// let weakref2 = PyWeakProxy::new_bound(&foo)?; /// assert!(weakref.is(&weakref2)); /// /// drop(foo); @@ -79,20 +79,25 @@ impl PyWeakProxy { /// #[inline] #[track_caller] - pub fn new_bound(py: Python<'_>, object: T) -> PyResult> - where - T: ToPyObject, - { - unsafe { - let ptr = object.to_object(py).as_ptr(); - assert_eq!( - ffi::PyCallable_Check(ptr), 0, + pub fn new_bound<'py, T>(object: &Bound<'py, T>) -> PyResult> { + // TODO: Is this inner pattern still necessary Here? + // TODO: Must track caller be used here? + fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { + assert!( + !object.is_callable(), "An object to be referenced by a PyWeakProxy should not be callable. Use PyWeakCallableProxy instead." ); - Bound::from_owned_ptr_or_err(py, ffi::PyWeakref_NewProxy(ptr, ffi::Py_None())) + unsafe { + Bound::from_owned_ptr_or_err( + object.py(), + ffi::PyWeakref_NewProxy(object.as_ptr(), ffi::Py_None()), + ) .downcast_into_unchecked() + } } + + inner(object.as_any()) } /// Deprecated form of [`PyWeakProxy::new_bound_with`]. @@ -105,12 +110,12 @@ impl PyWeakProxy { note = "`PyWeakProxy::new_with` will be replaced by `PyWeakProxy::new_bound_with` in a future PyO3 version" ) )] - pub fn new_with(py: Python<'_>, object: T, callback: C) -> PyResult<&'_ PyWeakProxy> + pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakProxy> where - T: ToPyObject, + T: PyNativeType, C: ToPyObject, { - Self::new_bound_with(py, object, callback).map(Bound::into_gil_ref) + Self::new_bound_with(&object.as_borrowed(), callback).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`) for the given object with a callback. @@ -141,7 +146,7 @@ impl PyWeakProxy { /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. - /// let weakref = PyWeakProxy::new_bound_with(py, foo.clone(), py.None())?; + /// let weakref = PyWeakProxy::new_bound_with(&foo, py.None())?; /// assert!(weakref.upgrade::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` @@ -150,7 +155,7 @@ impl PyWeakProxy { /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakProxy::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; + /// let weakref2 = PyWeakProxy::new_bound_with(&foo, wrap_pyfunction!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// @@ -165,28 +170,34 @@ impl PyWeakProxy { /// # Panics /// This function panics if the provided object is callable. #[track_caller] - pub fn new_bound_with( - py: Python<'_>, - object: T, + pub fn new_bound_with<'py, T, C>( + object: &Bound<'py, T>, callback: C, - ) -> PyResult> + ) -> PyResult> where - T: ToPyObject, C: ToPyObject, { - unsafe { - let ptr = object.to_object(py).as_ptr(); - assert_eq!( - ffi::PyCallable_Check(ptr), 0, + // TODO: Must track caller be used here? + fn inner<'py>( + object: &Bound<'py, PyAny>, + callback: Bound<'py, PyAny>, + ) -> PyResult> { + assert!( + !object.is_callable(), "An object to be referenced by a PyWeakProxy should not be callable. Use PyWeakCallableProxy instead." ); - Bound::from_owned_ptr_or_err( - py, - ffi::PyWeakref_NewProxy(ptr, callback.to_object(py).as_ptr()), - ) - .downcast_into_unchecked() + unsafe { + Bound::from_owned_ptr_or_err( + object.py(), + ffi::PyWeakref_NewProxy(object.as_ptr(), callback.as_ptr()), + ) + .downcast_into_unchecked() + } } + + let py = object.py(); + inner(object.as_any(), callback.to_object(py).into_bound(py)) } /// Upgrade the weakref to a direct object reference. @@ -222,7 +233,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(py, data.clone())?; + /// let reference = PyWeakProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -288,7 +299,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(py, data.clone())?; + /// let reference = PyWeakProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -350,7 +361,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(py, data.clone())?; + /// let reference = PyWeakProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -407,7 +418,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(py, object.clone())?; + /// let reference = PyWeakProxy::new_bound(&object)?; /// /// assert_eq!( /// get_class(reference.as_borrowed())?, @@ -491,7 +502,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakProxy::new_bound(py, object.clone())?; + let reference = PyWeakProxy::new_bound(&object)?; assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); @@ -542,7 +553,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakProxy::new_bound(py, object.clone())?; + let reference = PyWeakProxy::new_bound(&object)?; { // This test is a bit weird but ok. @@ -577,7 +588,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakProxy::new_bound(py, object.clone())?; + let reference = PyWeakProxy::new_bound(&object)?; assert!(reference.get_object().is_some()); assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); @@ -595,7 +606,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakProxy::new_bound(py, object.clone())?; + let reference = PyWeakProxy::new_bound(&object)?; assert!(reference.borrow_object().is_some()); assert!(reference @@ -615,7 +626,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakProxy::new_bound(py, object.clone())?; + let reference = PyWeakProxy::new_bound(&object)?; assert!(reference.get_object_raw().is(&object)); @@ -632,7 +643,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakProxy::new_bound(py, object.clone())?; + let reference = PyWeakProxy::new_bound(&object)?; assert!(reference.borrow_object_raw().is(&object)); @@ -657,7 +668,7 @@ mod tests { fn test_weakref_proxy_behavior() -> PyResult<()> { Python::with_gil(|py| { let object = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone())?; + let reference = PyWeakProxy::new_bound(&object)?; assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); @@ -707,7 +718,7 @@ mod tests { fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakProxy::new_bound(object.bind(py))?; { let obj = reference.upgrade::(); @@ -738,7 +749,7 @@ mod tests { fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakProxy::new_bound(object.bind(py))?; assert!(reference.get_object().is_some()); assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); @@ -755,7 +766,7 @@ mod tests { fn test_weakref_borrrow_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakProxy::new_bound(object.bind(py))?; assert!(reference.borrow_object().is_some()); assert!(reference @@ -774,7 +785,7 @@ mod tests { fn test_weakref_get_object_raw() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakProxy::new_bound(object.bind(py))?; assert!(reference.get_object_raw().is(&object)); @@ -790,7 +801,7 @@ mod tests { fn test_weakref_borrow_object_raw() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakProxy::new_bound(object.bind(py))?; assert!(reference.borrow_object_raw().is(&object)); diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 043b61e4eb2..6caac1b765a 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -3,7 +3,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; -use crate::{ffi, Borrowed, Bound, PyAny, PyNativeType, Python, ToPyObject}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, ToPyObject}; /// Represents a Python `weakref.ReferenceType`. /// @@ -29,11 +29,11 @@ impl PyWeakRef { note = "`PyWeakRef::new` will be replaced by `PyWeakRef::new_bound` in a future PyO3 version" ) )] - pub fn new(py: Python<'_>, object: T) -> PyResult<&'_ PyWeakRef> + pub fn new(object: &T) -> PyResult<&PyWeakRef> where - T: ToPyObject, + T: PyNativeType, { - Self::new_bound(py, object).map(Bound::into_gil_ref) + Self::new_bound(&object.as_borrowed()).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object. @@ -52,14 +52,14 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let foo = Bound::new(py, Foo {})?; - /// let weakref = PyWeakRef::new_bound(py, foo.clone())?; + /// let weakref = PyWeakRef::new_bound(&foo)?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.get_object() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// - /// let weakref2 = PyWeakRef::new_bound(py, foo.clone())?; + /// let weakref2 = PyWeakRef::new_bound(&foo)?; /// assert!(weakref.is(&weakref2)); /// /// drop(foo); @@ -69,17 +69,19 @@ impl PyWeakRef { /// }) /// # } /// ``` - pub fn new_bound(py: Python<'_>, object: T) -> PyResult> - where - T: ToPyObject, - { - unsafe { - Bound::from_owned_ptr_or_err( - py, - ffi::PyWeakref_NewRef(object.to_object(py).as_ptr(), ffi::Py_None()), - ) - .downcast_into_unchecked() + pub fn new_bound<'py, T>(object: &Bound<'py, T>) -> PyResult> { + // TODO: Is this inner pattern still necessary Here? + fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { + unsafe { + Bound::from_owned_ptr_or_err( + object.py(), + ffi::PyWeakref_NewRef(object.as_ptr(), ffi::Py_None()), + ) + .downcast_into_unchecked() + } } + + inner(object.as_any()) } /// Deprecated form of [`PyWeakRef::new_bound_with`]. @@ -91,12 +93,12 @@ impl PyWeakRef { note = "`PyWeakRef::new_with` will be replaced by `PyWeakRef::new_bound_with` in a future PyO3 version" ) )] - pub fn new_with(py: Python<'_>, object: T, callback: C) -> PyResult<&'_ PyWeakRef> + pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakRef> where - T: ToPyObject, + T: PyNativeType, C: ToPyObject, { - Self::new_bound_with(py, object, callback).map(Bound::into_gil_ref) + Self::new_bound_with(&object.as_borrowed(), callback).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object with a callback. @@ -126,7 +128,7 @@ impl PyWeakRef { /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. - /// let weakref = PyWeakRef::new_bound_with(py, foo.clone(), py.None())?; + /// let weakref = PyWeakRef::new_bound_with(&foo, py.None())?; /// assert!(weakref.upgrade::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` @@ -135,7 +137,7 @@ impl PyWeakRef { /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakRef::new_bound_with(py, foo.clone(), wrap_pyfunction!(callback, py)?)?; + /// let weakref2 = PyWeakRef::new_bound_with(&foo, wrap_pyfunction!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// @@ -147,25 +149,29 @@ impl PyWeakRef { /// }) /// # } /// ``` - pub fn new_bound_with( - py: Python<'_>, - object: T, + pub fn new_bound_with<'py, T, C>( + object: &Bound<'py, T>, callback: C, - ) -> PyResult> + ) -> PyResult> where - T: ToPyObject, + Bound<'py, T>: AsPyPointer, C: ToPyObject, { - unsafe { - Bound::from_owned_ptr_or_err( - py, - ffi::PyWeakref_NewRef( - object.to_object(py).as_ptr(), - callback.to_object(py).as_ptr(), - ), - ) - .downcast_into_unchecked() + fn inner<'py>( + object: &Bound<'py, PyAny>, + callback: Bound<'py, PyAny>, + ) -> PyResult> { + unsafe { + Bound::from_owned_ptr_or_err( + object.py(), + ffi::PyWeakref_NewRef(object.as_ptr(), callback.as_ptr()), + ) + .downcast_into_unchecked() + } } + + let py = object.py(); + inner(object.as_any(), callback.to_object(py).into_bound(py)) } /// Upgrade the weakref to a direct object reference. @@ -201,7 +207,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// let reference = PyWeakRef::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -267,7 +273,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// let reference = PyWeakRef::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -329,7 +335,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// let reference = PyWeakRef::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -386,7 +392,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, object.clone())?; + /// let reference = PyWeakRef::new_bound(&object)?; /// /// assert_eq!( /// get_class(reference.as_borrowed())?, @@ -454,7 +460,7 @@ pub trait PyWeakRefMethods<'py> { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// let reference = PyWeakRef::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -526,7 +532,7 @@ pub trait PyWeakRefMethods<'py> { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// let reference = PyWeakRef::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -589,7 +595,7 @@ pub trait PyWeakRefMethods<'py> { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// let reference = PyWeakRef::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -652,7 +658,7 @@ pub trait PyWeakRefMethods<'py> { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, data.clone())?; + /// let reference = PyWeakRef::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -719,7 +725,7 @@ pub trait PyWeakRefMethods<'py> { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, object.clone())?; + /// let reference = PyWeakRef::new_bound(&object)?; /// /// assert_eq!( /// get_class(reference.as_borrowed())?, @@ -775,7 +781,7 @@ pub trait PyWeakRefMethods<'py> { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(py, object.clone())?; + /// let reference = PyWeakRef::new_bound(&object)?; /// /// assert_eq!( /// get_class(reference.as_borrowed())?, @@ -873,7 +879,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(py, object.clone())?; + let reference = PyWeakRef::new_bound(&object)?; assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); @@ -919,7 +925,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(py, object.clone())?; + let reference = PyWeakRef::new_bound(&object)?; { // This test is a bit weird but ok. @@ -954,7 +960,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(py, object.clone())?; + let reference = PyWeakRef::new_bound(&object)?; assert!(reference.call0()?.is(&object)); assert!(reference.get_object().is_some()); @@ -974,7 +980,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(py, object.clone())?; + let reference = PyWeakRef::new_bound(&object)?; assert!(reference.call0()?.is(&object)); assert!(reference.borrow_object().is_some()); @@ -996,7 +1002,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(py, object.clone())?; + let reference = PyWeakRef::new_bound(&object)?; assert!(reference.call0()?.is(&object)); assert!(reference.get_object_raw().is(&object)); @@ -1016,7 +1022,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(py, object.clone())?; + let reference = PyWeakRef::new_bound(&object)?; assert!(reference.call0()?.is(&object)); assert!(reference.borrow_object_raw().is(&object)); @@ -1043,7 +1049,7 @@ mod tests { fn test_weakref_refence_behavior() -> PyResult<()> { Python::with_gil(|py| { let object = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone())?; + let reference = PyWeakRef::new_bound(&object)?; assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); @@ -1090,7 +1096,7 @@ mod tests { fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakRef::new_bound(object.bind(py))?; { let obj = reference.upgrade::(); @@ -1121,7 +1127,7 @@ mod tests { fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakRef::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); assert!(reference.get_object().is_some()); @@ -1140,7 +1146,7 @@ mod tests { fn test_weakref_borrrow_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakRef::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); assert!(reference.borrow_object().is_some()); @@ -1161,7 +1167,7 @@ mod tests { fn test_weakref_get_object_raw() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakRef::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); assert!(reference.get_object_raw().is(&object)); @@ -1180,7 +1186,7 @@ mod tests { fn test_weakref_borrow_object_raw() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(py, object.clone_ref(py))?; + let reference = PyWeakRef::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); assert!(reference.borrow_object_raw().is(&object)); From dd3ad8e715b362e58e2e4daeeaf48788088b6b27 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:14:54 +0100 Subject: [PATCH 17/42] Change to reflect deprecation of PyErr::value for PyErr::value_bound --- src/types/weakref/callableproxy.rs | 18 ++++++++++----- src/types/weakref/proxy.rs | 36 ++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 89ffa5f905a..e3a68ab4c59 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -595,9 +595,12 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "weakly-referenced object no longer exists"))); + assert!(reference + .call0() + .err() + .map_or(false, |err| err.is_instance_of::(py) + & (err.value_bound(py).to_string() + == "weakly-referenced object no longer exists"))); Ok(()) }) @@ -766,9 +769,12 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "weakly-referenced object no longer exists"))); + assert!(reference + .call0() + .err() + .map_or(false, |err| err.is_instance_of::(py) + & (err.value_bound(py).to_string() + == "weakly-referenced object no longer exists"))); Ok(()) }) diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index d0a2e2e3983..f30a6c7215f 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -522,9 +522,12 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + assert!(reference + .call0() + .err() + .map_or(false, |err| err.is_instance_of::(py) + & (err.value_bound(py).to_string() + == "'weakref.ProxyType' object is not callable"))); drop(object); @@ -540,9 +543,12 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + assert!(reference + .call0() + .err() + .map_or(false, |err| err.is_instance_of::(py) + & (err.value_bound(py).to_string() + == "'weakref.ProxyType' object is not callable"))); Ok(()) }) @@ -688,9 +694,12 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + assert!(reference + .call0() + .err() + .map_or(false, |err| err.is_instance_of::(py) + & (err.value_bound(py).to_string() + == "'weakref.ProxyType' object is not callable"))); drop(object); @@ -706,9 +715,12 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference.call0().err().map_or(false, |err| err - .is_instance_of::(py) - & (err.value(py).to_string() == "'weakref.ProxyType' object is not callable"))); + assert!(reference + .call0() + .err() + .map_or(false, |err| err.is_instance_of::(py) + & (err.value_bound(py).to_string() + == "'weakref.ProxyType' object is not callable"))); Ok(()) }) From efdd0ff1596c755a3abf5da7986c674779af656a Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:31:02 +0100 Subject: [PATCH 18/42] Change Tests so different class name in older python versions is accounted for --- src/types/weakref/callableproxy.rs | 10 ++++++-- src/types/weakref/proxy.rs | 17 ++++++++----- src/types/weakref/reference.rs | 38 ++++++++++-------------------- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index e3a68ab4c59..9b9622d41c7 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -514,6 +514,12 @@ mod tests { use crate::types::weakref::{PyWeakCallableProxy, PyWeakRefMethods}; use crate::{Bound, PyAny, PyResult, Python}; + #[cfg(Py_3_9)] + const CLASS_NAME: &str = ""; + #[cfg(not(Py_3_9))] + const CLASS_NAME: &str = ""; + + fn check_repr( reference: &Bound<'_, PyWeakCallableProxy>, object: &Bound<'_, PyAny>, @@ -565,7 +571,7 @@ mod tests { assert!(reference.get_object_raw().is(&object)); assert_eq!( reference.get_type().to_string(), - "" + CLASS_NAME ); assert_eq!( @@ -739,7 +745,7 @@ mod tests { assert!(reference.get_object_raw().is(&object)); assert_eq!( reference.get_type().to_string(), - "" + CLASS_NAME ); assert_eq!( diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index f30a6c7215f..5a1d154417f 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -461,6 +461,11 @@ mod tests { use crate::types::weakref::{PyWeakProxy, PyWeakRefMethods}; use crate::{Bound, PyAny, PyResult, Python}; + #[cfg(Py_3_9)] + const CLASS_NAME: &str = "'weakref.ProxyType'"; + #[cfg(not(Py_3_9))] + const CLASS_NAME: &str = "'weakproxy'"; + fn check_repr( reference: &Bound<'_, PyWeakProxy>, object: &Bound<'_, PyAny>, @@ -508,7 +513,7 @@ mod tests { assert!(reference.get_object_raw().is(&object)); assert_eq!( reference.get_type().to_string(), - "" + format!("", CLASS_NAME) ); assert_eq!( @@ -527,7 +532,7 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py) & (err.value_bound(py).to_string() - == "'weakref.ProxyType' object is not callable"))); + == format!("{} object is not callable", CLASS_NAME)))); drop(object); @@ -548,7 +553,7 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py) & (err.value_bound(py).to_string() - == "'weakref.ProxyType' object is not callable"))); + == format!("{} object is not callable", CLASS_NAME)))); Ok(()) }) @@ -680,7 +685,7 @@ mod tests { assert!(reference.get_object_raw().is(&object)); assert_eq!( reference.get_type().to_string(), - "" + format!("", CLASS_NAME) ); assert_eq!( @@ -699,7 +704,7 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py) & (err.value_bound(py).to_string() - == "'weakref.ProxyType' object is not callable"))); + == format!("{} object is not callable", CLASS_NAME)))); drop(object); @@ -720,7 +725,7 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py) & (err.value_bound(py).to_string() - == "'weakref.ProxyType' object is not callable"))); + == format!("{} object is not callable", CLASS_NAME)))); Ok(()) }) diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 6caac1b765a..1cf049c8ca7 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -832,6 +832,11 @@ mod tests { use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; use crate::{Bound, PyAny, PyResult, Python}; + #[cfg(Py_3_9)] + const CLASS_NAME: &str = ""; + #[cfg(not(Py_3_9))] + const CLASS_NAME: &str = ""; + fn check_repr( reference: &Bound<'_, PyWeakRef>, object: Option<(&Bound<'_, PyAny>, &str)>, @@ -883,15 +888,10 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); - assert_eq!( - reference.get_type().to_string(), - "" - ); - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); + assert_eq!(reference.get_type().to_string(), CLASS_NAME); + + assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr(&reference, Some((object.as_any(), "A")))?; @@ -904,10 +904,7 @@ mod tests { drop(object); assert!(reference.get_object_raw().is_none()); - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); + assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr(&reference, None)?; assert!(reference @@ -1053,15 +1050,9 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); - assert_eq!( - reference.get_type().to_string(), - "" - ); - - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); + assert_eq!(reference.get_type().to_string(), CLASS_NAME); + + assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr( &reference, Some((object.as_any(), "builtins.WeakrefablePyClass")), @@ -1076,10 +1067,7 @@ mod tests { drop(object); assert!(reference.get_object_raw().is_none()); - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); + assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr(&reference, None)?; assert!(reference From 972c1681fcac48ecf62dfb08d9b452fd721035e2 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:34:48 +0100 Subject: [PATCH 19/42] Change formatting --- src/types/weakref/callableproxy.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 9b9622d41c7..1fdb6648309 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -519,7 +519,6 @@ mod tests { #[cfg(not(Py_3_9))] const CLASS_NAME: &str = ""; - fn check_repr( reference: &Bound<'_, PyWeakCallableProxy>, object: &Bound<'_, PyAny>, @@ -569,10 +568,7 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); - assert_eq!( - reference.get_type().to_string(), - CLASS_NAME - ); + assert_eq!(reference.get_type().to_string(), CLASS_NAME); assert_eq!( reference.getattr("__class__")?.to_string(), @@ -743,10 +739,7 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); - assert_eq!( - reference.get_type().to_string(), - CLASS_NAME - ); + assert_eq!(reference.get_type().to_string(), CLASS_NAME); assert_eq!( reference.getattr("__class__")?.to_string(), From 30c474216527421a1fd50484f8e61dc085ce8494 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:54:27 +0100 Subject: [PATCH 20/42] Make tests ABI3 compatible --- src/types/weakref/callableproxy.rs | 6 ++-- src/types/weakref/proxy.rs | 55 ++++++++++++++++++------------ src/types/weakref/reference.rs | 10 ++++-- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 1fdb6648309..d67db05b79b 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -514,9 +514,9 @@ mod tests { use crate::types::weakref::{PyWeakCallableProxy, PyWeakRefMethods}; use crate::{Bound, PyAny, PyResult, Python}; - #[cfg(Py_3_9)] + #[cfg(all(not(Py_LIMITED_API), Py_3_10))] const CLASS_NAME: &str = ""; - #[cfg(not(Py_3_9))] + #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] const CLASS_NAME: &str = ""; fn check_repr( @@ -568,6 +568,7 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); + #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.get_type().to_string(), CLASS_NAME); assert_eq!( @@ -739,6 +740,7 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); + #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.get_type().to_string(), CLASS_NAME); assert_eq!( diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 5a1d154417f..279a83acd0a 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -461,9 +461,9 @@ mod tests { use crate::types::weakref::{PyWeakProxy, PyWeakRefMethods}; use crate::{Bound, PyAny, PyResult, Python}; - #[cfg(Py_3_9)] + #[cfg(all(not(Py_LIMITED_API), Py_3_10))] const CLASS_NAME: &str = "'weakref.ProxyType'"; - #[cfg(not(Py_3_9))] + #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] const CLASS_NAME: &str = "'weakproxy'"; fn check_repr( @@ -511,6 +511,8 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); + + #[cfg(not(Py_LIMITED_API))] assert_eq!( reference.get_type().to_string(), format!("", CLASS_NAME) @@ -527,12 +529,14 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference - .call0() - .err() - .map_or(false, |err| err.is_instance_of::(py) + assert!(reference.call0().err().map_or(false, |err| { + let result = err.is_instance_of::(py); + #[cfg(not(Py_LIMITED_API))] + let result = result & (err.value_bound(py).to_string() - == format!("{} object is not callable", CLASS_NAME)))); + == format!("{} object is not callable", CLASS_NAME)); + result + })); drop(object); @@ -548,12 +552,14 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference - .call0() - .err() - .map_or(false, |err| err.is_instance_of::(py) + assert!(reference.call0().err().map_or(false, |err| { + let result = err.is_instance_of::(py); + #[cfg(not(Py_LIMITED_API))] + let result = result & (err.value_bound(py).to_string() - == format!("{} object is not callable", CLASS_NAME)))); + == format!("{} object is not callable", CLASS_NAME)); + result + })); Ok(()) }) @@ -683,6 +689,7 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); + #[cfg(not(Py_LIMITED_API))] assert_eq!( reference.get_type().to_string(), format!("", CLASS_NAME) @@ -699,12 +706,14 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference - .call0() - .err() - .map_or(false, |err| err.is_instance_of::(py) + assert!(reference.call0().err().map_or(false, |err| { + let result = err.is_instance_of::(py); + #[cfg(not(Py_LIMITED_API))] + let result = result & (err.value_bound(py).to_string() - == format!("{} object is not callable", CLASS_NAME)))); + == format!("{} object is not callable", CLASS_NAME)); + result + })); drop(object); @@ -720,12 +729,14 @@ mod tests { .err() .map_or(false, |err| err.is_instance_of::(py))); - assert!(reference - .call0() - .err() - .map_or(false, |err| err.is_instance_of::(py) + assert!(reference.call0().err().map_or(false, |err| { + let result = err.is_instance_of::(py); + #[cfg(not(Py_LIMITED_API))] + let result = result & (err.value_bound(py).to_string() - == format!("{} object is not callable", CLASS_NAME)))); + == format!("{} object is not callable", CLASS_NAME)); + result + })); Ok(()) }) diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 1cf049c8ca7..e64b9e8f681 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -832,9 +832,9 @@ mod tests { use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; use crate::{Bound, PyAny, PyResult, Python}; - #[cfg(Py_3_9)] + #[cfg(all(not(Py_LIMITED_API), Py_3_10))] const CLASS_NAME: &str = ""; - #[cfg(not(Py_3_9))] + #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] const CLASS_NAME: &str = ""; fn check_repr( @@ -889,8 +889,10 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); + #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.get_type().to_string(), CLASS_NAME); + #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr(&reference, Some((object.as_any(), "A")))?; @@ -904,6 +906,7 @@ mod tests { drop(object); assert!(reference.get_object_raw().is_none()); + #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr(&reference, None)?; @@ -1050,8 +1053,10 @@ mod tests { assert!(!reference.is(&object)); assert!(reference.get_object_raw().is(&object)); + #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.get_type().to_string(), CLASS_NAME); + #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr( &reference, @@ -1067,6 +1072,7 @@ mod tests { drop(object); assert!(reference.get_object_raw().is_none()); + #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr(&reference, None)?; From 3cc55c9d12580680dc70bac3dc0cbb4936d75209 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:42:31 +0100 Subject: [PATCH 21/42] Prevent the use of PyClass in test for weakref under abi3 Python 3.7 and 3.8 --- src/types/weakref/callableproxy.rs | 59 ++++++++++++--- src/types/weakref/proxy.rs | 59 ++++++++++++--- src/types/weakref/reference.rs | 113 +++++++++++++++++++++++++---- 3 files changed, 198 insertions(+), 33 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index d67db05b79b..c430938ba25 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -45,8 +45,14 @@ impl PyWeakCallableProxy { /// The object should also be callable. For a non-callable weakref proxy see [`PyWeakProxy`](crate::types::weakref::PyWeakProxy). /// /// # Examples - /// - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakCallableProxy; /// use pyo3::exceptions::PyReferenceError; @@ -138,8 +144,14 @@ impl PyWeakCallableProxy { /// The object should also be callable. For a non-callable weakref proxy see [`PyWeakProxy`](crate::types::weakref::PyWeakProxy). /// /// # Examples - /// - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakCallableProxy; /// @@ -229,7 +241,14 @@ impl PyWeakCallableProxy { /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakCallableProxy; /// @@ -301,7 +320,14 @@ impl PyWeakCallableProxy { /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakCallableProxy; /// @@ -378,7 +404,14 @@ impl PyWeakCallableProxy { /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakCallableProxy; /// @@ -443,7 +476,14 @@ impl PyWeakCallableProxy { /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakCallableProxy; /// @@ -717,7 +757,8 @@ mod tests { } } - #[cfg(feature = "macros")] + // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable. + #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))] mod pyo3_pyclass { use super::*; use crate::{pyclass, pymethods, Py}; diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 279a83acd0a..6bdcc47beed 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -45,8 +45,14 @@ impl PyWeakProxy { /// The object should also not be callable. For a callable weakref proxy see [`PyWeakCallableProxy`](crate::types::weakref::PyWeakCallableProxy). /// /// # Examples - /// - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakProxy; /// @@ -124,8 +130,14 @@ impl PyWeakProxy { /// The object should also not be callable. For a callable weakref proxy see [`PyWeakCallableProxy`](crate::types::weakref::PyWeakCallableProxy). /// /// # Examples - /// - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakProxy; /// @@ -206,7 +218,14 @@ impl PyWeakProxy { /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakProxy; /// @@ -272,7 +291,14 @@ impl PyWeakProxy { /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakProxy; /// @@ -343,7 +369,14 @@ impl PyWeakProxy { /// It produces similair results using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakProxy; /// @@ -399,7 +432,14 @@ impl PyWeakProxy { /// It produces similair results using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakProxy; /// @@ -673,7 +713,8 @@ mod tests { } } - #[cfg(feature = "macros")] + // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable. + #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))] mod pyo3_pyclass { use super::*; use crate::{pyclass, Py}; diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index e64b9e8f681..b0317e0513b 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -41,8 +41,14 @@ impl PyWeakRef { /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag). /// /// # Examples - /// - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -106,8 +112,14 @@ impl PyWeakRef { /// 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. /// /// # Examples - /// - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -180,7 +192,14 @@ impl PyWeakRef { /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -246,7 +265,14 @@ impl PyWeakRef { /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -317,7 +343,14 @@ impl PyWeakRef { /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -373,7 +406,14 @@ impl PyWeakRef { /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -433,7 +473,14 @@ pub trait PyWeakRefMethods<'py> { /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -505,7 +552,14 @@ pub trait PyWeakRefMethods<'py> { /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -577,7 +631,14 @@ pub trait PyWeakRefMethods<'py> { /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -640,7 +701,14 @@ pub trait PyWeakRefMethods<'py> { /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -706,7 +774,14 @@ pub trait PyWeakRefMethods<'py> { /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -762,7 +837,14 @@ pub trait PyWeakRefMethods<'py> { /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. /// /// # Example - /// ```rust + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] /// use pyo3::prelude::*; /// use pyo3::types::PyWeakRef; /// @@ -1037,7 +1119,8 @@ mod tests { } } - #[cfg(feature = "macros")] + // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable. + #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))] mod pyo3_pyclass { use super::*; use crate::{pyclass, Py}; From a570f001c8d2fbeed992bca94a297eb6c860bdd4 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:03:07 +0100 Subject: [PATCH 22/42] Disable weakref types when targeting PyPy --- src/prelude.rs | 1 + src/types/mod.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/prelude.rs b/src/prelude.rs index 76709e42b9a..ea405d3d527 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -46,4 +46,5 @@ pub use crate::types::string::PyStringMethods; pub use crate::types::traceback::PyTracebackMethods; pub use crate::types::tuple::PyTupleMethods; pub use crate::types::typeobject::PyTypeMethods; +#[cfg(not(PyPy))] pub use crate::types::weakref::PyWeakRefMethods; diff --git a/src/types/mod.rs b/src/types/mod.rs index 64f490ea062..1e9eef9e92f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -47,7 +47,8 @@ pub use self::string::{PyString, PyString as PyUnicode, PyStringMethods}; pub use self::traceback::{PyTraceback, PyTracebackMethods}; pub use self::tuple::{PyTuple, PyTupleMethods}; pub use self::typeobject::{PyType, PyTypeMethods}; -pub use self::weakref::{PyWeakCallableProxy, PyWeakProxy, PyWeakRef}; +#[cfg(not(PyPy))] +pub use self::weakref::{PyWeakCallableProxy, PyWeakProxy, PyWeakRef, PyWeakRefMethods}; /// Iteration over Python collections. /// @@ -358,4 +359,5 @@ pub(crate) mod string; pub(crate) mod traceback; pub(crate) mod tuple; pub(crate) mod typeobject; +#[cfg(not(PyPy))] pub(crate) mod weakref; From dc4018518ce7292992e78b1cd04f8b962de3cd1f Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:24:10 +0100 Subject: [PATCH 23/42] Remove needless borrow from CallableProxy test --- src/types/weakref/callableproxy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index c430938ba25..eacf96b531d 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -631,7 +631,7 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, &py.None().bind(py), "NoneType")?; + check_repr(&reference, py.None().bind(py), "NoneType")?; assert!(reference .getattr("__callback__") From 513ab14460ed571bb8aeb991ee4a435124d82640 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:02:04 +0100 Subject: [PATCH 24/42] Add Borrowed variants of upgrade and upgrade exact to trait --- src/types/weakref/reference.rs | 173 ++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 3 deletions(-) diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index b0317e0513b..2191851cc1a 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -3,7 +3,7 @@ use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAnyMethods; -use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, ToPyObject}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, DowncastError, PyAny, PyNativeType, ToPyObject}; /// Represents a Python `weakref.ReferenceType`. /// @@ -543,8 +543,90 @@ pub trait PyWeakRefMethods<'py> { .transpose()?) } - // TODO: Is this even possible? - // fn borrowed_upgrade(&self) -> PyResult>>; + // TODO: ADD TESTS + // TODO: ADD DIRECT METHODS TO IMPLEMENTORS + /// Upgrade the weakref to a Borrowed object reference. + /// + /// It is named `borrowed_upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(data_src) = reference.borrowed_upgrade::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? + fn borrowed_upgrade<'a, T>(&'a self) -> PyResult>> + where + T: PyTypeCheck, + 'py: 'a, + { + // TODO: Replace when Borrowed::downcast exists + if let Some(object) = self.borrow_object() { + if T::type_check(&object) { + Ok(Some(unsafe { object.downcast_unchecked() })) + } else { + Err(DowncastError::new(&object, T::NAME))? + } + } else { + Ok(None) + } + } /// Upgrade the weakref to a exact direct Bound object reference. /// @@ -622,6 +704,91 @@ pub trait PyWeakRefMethods<'py> { .transpose()?) } + // TODO: ADD TESTS + // TODO: ADD DIRECT METHODS TO IMPLEMENTORS + /// Upgrade the weakref to a exact Borrowed object reference. + /// + /// It is named `borrowed_upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// if let Some(data_src) = reference.borrowed_upgrade_exact::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? + fn borrowed_upgrade_exact<'a, T>(&'a self) -> PyResult>> + where + T: PyTypeInfo, + 'py: 'a, + { + // TODO: Replace when Borrowed::downcast_exact exists + if let Some(object) = self.borrow_object() { + if object.is_exact_instance_of::() { + Ok(Some(unsafe { object.downcast_unchecked() })) + } else { + Err(DowncastError::new(&object, T::NAME))? + } + } else { + Ok(None) + } + } + // TODO: NAMING-ALTERNATIVE: upgrade_any /// Upgrade the weakref to a Bound [`PyAny`] reference to the target object if possible. /// From 1ad66e86bd7f1bde00073cfadc8cd9a440ac4e86 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:00:50 +0100 Subject: [PATCH 25/42] Added tests for weakref borrow_upgrade methods --- src/types/weakref/callableproxy.rs | 73 +++++++++++++++- src/types/weakref/proxy.rs | 74 ++++++++++++++++- src/types/weakref/reference.rs | 129 ++++++++++++++++++++--------- 3 files changed, 228 insertions(+), 48 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index eacf96b531d..4816443f8ec 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -2,8 +2,8 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; -use crate::types::any::PyAnyMethods; -use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, ToPyObject}; +use crate::types::any::{PyAny, PyAnyMethods}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, PyNativeType, ToPyObject}; use super::PyWeakRefMethods; @@ -550,9 +550,9 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakCallableProxy> { #[cfg(test)] mod tests { use crate::exceptions::{PyAttributeError, PyReferenceError}; - use crate::types::any::PyAnyMethods; + use crate::types::any::{PyAny, PyAnyMethods}; use crate::types::weakref::{PyWeakCallableProxy, PyWeakRefMethods}; - use crate::{Bound, PyAny, PyResult, Python}; + use crate::{Bound, PyResult, Python}; #[cfg(all(not(Py_LIMITED_API), Py_3_10))] const CLASS_NAME: &str = ""; @@ -684,6 +684,41 @@ mod tests { }) } + #[test] + fn test_weakref_borrowed_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { @@ -852,6 +887,36 @@ mod tests { Ok(()) }) } + #[test] + fn test_weakref_borrowed_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; + + { + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } #[test] fn test_weakref_get_object() -> PyResult<()> { diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 6bdcc47beed..9ea248f3ed2 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -2,8 +2,8 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; -use crate::types::any::PyAnyMethods; -use crate::{ffi, AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, ToPyObject}; +use crate::types::any::{PyAny, PyAnyMethods}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, PyNativeType, ToPyObject}; use super::PyWeakRefMethods; @@ -497,9 +497,9 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { #[cfg(test)] mod tests { use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError}; - use crate::types::any::PyAnyMethods; + use crate::types::any::{PyAny, PyAnyMethods}; use crate::types::weakref::{PyWeakProxy, PyWeakRefMethods}; - use crate::{Bound, PyAny, PyResult, Python}; + use crate::{Bound, PyResult, Python}; #[cfg(all(not(Py_LIMITED_API), Py_3_10))] const CLASS_NAME: &str = "'weakref.ProxyType'"; @@ -640,6 +640,41 @@ mod tests { }) } + #[test] + fn test_weakref_borrowed_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { @@ -814,6 +849,37 @@ mod tests { }) } + #[test] + fn test_weakref_borrowed_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(object.bind(py))?; + + { + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 2191851cc1a..81f4e64a1d7 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -1,9 +1,9 @@ -use crate::err::PyResult; +use crate::err::{DowncastError, PyResult}; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; -use crate::types::any::PyAnyMethods; -use crate::{ffi, AsPyPointer, Borrowed, Bound, DowncastError, PyAny, PyNativeType, ToPyObject}; +use crate::types::any::{PyAny, PyAnyMethods}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, PyNativeType, ToPyObject}; /// Represents a Python `weakref.ReferenceType`. /// @@ -537,18 +537,16 @@ pub trait PyWeakRefMethods<'py> { where T: PyTypeCheck, { - Ok(self - .get_object() - .map(|obj| obj.downcast_into::()) - .transpose()?) + self.get_object() + .map(Bound::downcast_into::) + .transpose() + .map_err(Into::into) } - // TODO: ADD TESTS - // TODO: ADD DIRECT METHODS TO IMPLEMENTORS /// Upgrade the weakref to a Borrowed object reference. /// /// It is named `borrowed_upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example #[cfg_attr( @@ -617,14 +615,12 @@ pub trait PyWeakRefMethods<'py> { 'py: 'a, { // TODO: Replace when Borrowed::downcast exists - if let Some(object) = self.borrow_object() { - if T::type_check(&object) { + match self.borrow_object() { + None => Ok(None), + Some(object) if T::type_check(&object) => { Ok(Some(unsafe { object.downcast_unchecked() })) - } else { - Err(DowncastError::new(&object, T::NAME))? } - } else { - Ok(None) + Some(object) => Err(DowncastError::new(&object, T::NAME).into()), } } @@ -698,10 +694,10 @@ pub trait PyWeakRefMethods<'py> { where T: PyTypeInfo, { - Ok(self - .get_object() - .map(|obj| obj.downcast_into_exact::()) - .transpose()?) + self.get_object() + .map(Bound::downcast_into_exact) + .transpose() + .map_err(Into::into) } // TODO: ADD TESTS @@ -709,7 +705,7 @@ pub trait PyWeakRefMethods<'py> { /// Upgrade the weakref to a exact Borrowed object reference. /// /// It is named `borrowed_upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example #[cfg_attr( @@ -778,14 +774,12 @@ pub trait PyWeakRefMethods<'py> { 'py: 'a, { // TODO: Replace when Borrowed::downcast_exact exists - if let Some(object) = self.borrow_object() { - if object.is_exact_instance_of::() { + match self.borrow_object() { + None => Ok(None), + Some(object) if object.is_exact_instance_of::() => { Ok(Some(unsafe { object.downcast_unchecked() })) - } else { - Err(DowncastError::new(&object, T::NAME))? } - } else { - Ok(None) + Some(object) => Err(DowncastError::new(&object, T::NAME).into()), } } @@ -1057,17 +1051,6 @@ pub trait PyWeakRefMethods<'py> { } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { - /* - fn borrowed_upgrade(&self) -> PyResult>> - where - T: PyTypeCheck - { - Ok(self.borrow_object()?.map(|obj| obj.downcast_into::().expect( - "The `weakref.ReferenceType` (`PyWeakRef`) should refer to an instance of the specified class", - ))) - } - */ - fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } @@ -1077,9 +1060,9 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { #[cfg(test)] mod tests { - use crate::types::any::PyAnyMethods; + use crate::types::any::{PyAny, PyAnyMethods}; use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; - use crate::{Bound, PyAny, PyResult, Python}; + use crate::{Bound, PyResult, Python}; #[cfg(all(not(Py_LIMITED_API), Py_3_10))] const CLASS_NAME: &str = ""; @@ -1204,6 +1187,41 @@ mod tests { }) } + #[test] + fn test_weakref_borrowed_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { @@ -1367,6 +1385,37 @@ mod tests { }) } + #[test] + fn test_weakref_borrowed_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(object.bind(py))?; + + { + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = reference.borrowed_upgrade::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { From ede188a1a62d218f5bd030903af06f1577950199 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:42:13 +0100 Subject: [PATCH 26/42] Change PyWeakRefMethods method names to be more consistent --- src/types/weakref/callableproxy.rs | 130 +++++++++---------- src/types/weakref/proxy.rs | 130 +++++++++---------- src/types/weakref/reference.rs | 192 ++++++++++++++--------------- 3 files changed, 230 insertions(+), 222 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 4816443f8ec..3de3b9ab889 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -73,7 +73,7 @@ impl PyWeakCallableProxy { /// let weakref = PyWeakCallableProxy::new_bound(&foo)?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object() + /// weakref.upgrade() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// @@ -84,7 +84,7 @@ impl PyWeakCallableProxy { /// /// drop(foo); /// - /// assert!(weakref.get_object().is_none()); + /// assert!(weakref.upgrade().is_none()); /// assert!(weakref.call0() /// .err() /// .map_or(false, |err| err.is_instance_of::(py)) @@ -168,7 +168,7 @@ impl PyWeakCallableProxy { /// #[pyfunction] /// fn callback(wref: Bound<'_, PyWeakCallableProxy>) -> PyResult<()> { /// let py = wref.py(); - /// assert!(wref.upgrade::()?.is_none()); + /// assert!(wref.upgrade_as::()?.is_none()); /// py.run_bound("counter = 1", None, None) /// } /// @@ -180,10 +180,10 @@ impl PyWeakCallableProxy { /// /// // This is fine. /// let weakref = PyWeakCallableProxy::new_bound_with(&foo, py.None())?; - /// assert!(weakref.upgrade::()?.is_some()); + /// assert!(weakref.upgrade_as::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object() + /// weakref.upgrade() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); @@ -196,7 +196,7 @@ impl PyWeakCallableProxy { /// /// drop(foo); /// - /// assert!(weakref.upgrade::()?.is_none()); + /// assert!(weakref.upgrade_as::()?.is_none()); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 1); /// Ok(()) /// }) @@ -267,7 +267,7 @@ impl PyWeakCallableProxy { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { - /// if let Some(data_src) = reference.upgrade::()? { + /// if let Some(data_src) = reference.upgrade_as::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -307,11 +307,14 @@ impl PyWeakCallableProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn upgrade(&self) -> PyResult> + pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, { - Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) + Ok(self + .as_borrowed() + .upgrade_as::()? + .map(Bound::into_gil_ref)) } /// Upgrade the weakref to an exact direct object reference. @@ -346,7 +349,7 @@ impl PyWeakCallableProxy { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_exact::()? { + /// if let Some(data_src) = reference.upgrade_as_exact::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -386,18 +389,19 @@ impl PyWeakCallableProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn upgrade_exact(&self) -> PyResult> + pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, { Ok(self .as_borrowed() - .upgrade_exact::()? + .upgrade_as_exact::()? .map(Bound::into_gil_ref)) } /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. /// /// This function gets the optional target of this [`weakref.CallableProxyType`] (result of calling [`weakref.proxy`]). @@ -426,7 +430,7 @@ impl PyWeakCallableProxy { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { - /// if let Some(object) = reference.get_object() { + /// if let Some(object) = reference.upgrade() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -464,8 +468,8 @@ impl PyWeakCallableProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyCallableType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn get_object(&self) -> Option<&'_ PyAny> { - self.as_borrowed().get_object().map(Bound::into_gil_ref) + pub fn upgrade(&self) -> Option<&'_ PyAny> { + self.as_borrowed().upgrade().map(Bound::into_gil_ref) } /// Retrieve to a object pointed to by the weakref. @@ -499,7 +503,7 @@ impl PyWeakCallableProxy { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { /// reference - /// .get_object_raw() + /// .get_object() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -534,13 +538,13 @@ impl PyWeakCallableProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn get_object_raw(&self) -> &'_ PyAny { - self.as_borrowed().get_object_raw().into_gil_ref() + pub fn get_object(&self) -> &'_ PyAny { + self.as_borrowed().get_object().into_gil_ref() } } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakCallableProxy> { - fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny> { + fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } .expect("The 'weakref.CallableProxyType' instance should be valid (non-null and actually a weakref reference)") @@ -607,7 +611,7 @@ mod tests { let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.get_type().to_string(), CLASS_NAME); @@ -626,7 +630,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); assert!(reference .getattr("__class__") .err() @@ -650,7 +654,7 @@ mod tests { } #[test] - fn test_weakref_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_as() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; @@ -658,7 +662,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -672,7 +676,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -685,7 +689,7 @@ mod tests { } #[test] - fn test_weakref_borrowed_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; @@ -693,7 +697,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -707,7 +711,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -720,72 +724,72 @@ mod tests { } #[test] - fn test_weakref_get_object() -> PyResult<()> { + fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakCallableProxy::new_bound(&object)?; - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); drop(object); - assert!(reference.get_object().is_none()); + assert!(reference.upgrade().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { + fn test_weakref_upgrade_borrowed() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakCallableProxy::new_bound(&object)?; - assert!(reference.borrow_object().is_some()); + assert!(reference.upgrade_borrowed().is_some()); assert!(reference - .borrow_object() + .upgrade_borrowed() .map_or(false, |obj| obj.is(&object))); drop(object); - assert!(reference.borrow_object().is_none()); + assert!(reference.upgrade_borrowed().is_none()); Ok(()) }) } #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { + fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakCallableProxy::new_bound(&object)?; - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { + fn test_weakref_get_object_borrowed() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakCallableProxy::new_bound(&object)?; - assert!(reference.borrow_object_raw().is(&object)); + assert!(reference.get_object_borrowed().is(&object)); drop(object); - assert!(reference.borrow_object_raw().is_none()); + assert!(reference.get_object_borrowed().is_none()); Ok(()) }) @@ -815,7 +819,7 @@ mod tests { let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.get_type().to_string(), CLASS_NAME); @@ -834,7 +838,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); assert!(reference .getattr("__class__") .err() @@ -858,13 +862,13 @@ mod tests { } #[test] - fn test_weakref_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_as() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; { - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -876,7 +880,7 @@ mod tests { drop(object); { - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -888,13 +892,13 @@ mod tests { }) } #[test] - fn test_weakref_borrowed_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; { - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -906,7 +910,7 @@ mod tests { drop(object); { - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -919,68 +923,68 @@ mod tests { } #[test] - fn test_weakref_get_object() -> PyResult<()> { + fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); drop(object); - assert!(reference.get_object().is_none()); + assert!(reference.upgrade().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { + fn test_weakref_upgrade_borrowed() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - assert!(reference.borrow_object().is_some()); + assert!(reference.upgrade_borrowed().is_some()); assert!(reference - .borrow_object() + .upgrade_borrowed() .map_or(false, |obj| obj.is(&object))); drop(object); - assert!(reference.borrow_object().is_none()); + assert!(reference.upgrade_borrowed().is_none()); Ok(()) }) } #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { + fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { + fn test_weakref_get_object_borrowed() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - assert!(reference.borrow_object_raw().is(&object)); + assert!(reference.get_object_borrowed().is(&object)); drop(object); - assert!(reference.borrow_object_raw().is_none()); + assert!(reference.get_object_borrowed().is_none()); Ok(()) }) diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 9ea248f3ed2..abbccf5efbe 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -65,7 +65,7 @@ impl PyWeakProxy { /// let weakref = PyWeakProxy::new_bound(&foo)?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object() + /// weakref.upgrade() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// @@ -74,7 +74,7 @@ impl PyWeakProxy { /// /// drop(foo); /// - /// assert!(weakref.get_object().is_none()); + /// assert!(weakref.upgrade().is_none()); /// Ok(()) /// }) /// # } @@ -147,7 +147,7 @@ impl PyWeakProxy { /// #[pyfunction] /// fn callback(wref: Bound<'_, PyWeakProxy>) -> PyResult<()> { /// let py = wref.py(); - /// assert!(wref.upgrade::()?.is_none()); + /// assert!(wref.upgrade_as::()?.is_none()); /// py.run_bound("counter = 1", None, None) /// } /// @@ -159,10 +159,10 @@ impl PyWeakProxy { /// /// // This is fine. /// let weakref = PyWeakProxy::new_bound_with(&foo, py.None())?; - /// assert!(weakref.upgrade::()?.is_some()); + /// assert!(weakref.upgrade_as::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object() + /// weakref.upgrade() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); @@ -173,7 +173,7 @@ impl PyWeakProxy { /// /// drop(foo); /// - /// assert!(weakref.upgrade::()?.is_none()); + /// assert!(weakref.upgrade_as::()?.is_none()); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 1); /// Ok(()) /// }) @@ -240,7 +240,7 @@ impl PyWeakProxy { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { - /// if let Some(data_src) = reference.upgrade::()? { + /// if let Some(data_src) = reference.upgrade_as::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -278,11 +278,14 @@ impl PyWeakProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn upgrade(&self) -> PyResult> + pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, { - Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) + Ok(self + .as_borrowed() + .upgrade_as::()? + .map(Bound::into_gil_ref)) } /// Upgrade the weakref to an exact direct object reference. @@ -313,7 +316,7 @@ impl PyWeakProxy { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_exact::()? { + /// if let Some(data_src) = reference.upgrade_as_exact::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -351,18 +354,19 @@ impl PyWeakProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn upgrade_exact(&self) -> PyResult> + pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, { Ok(self .as_borrowed() - .upgrade_exact::()? + .upgrade_as_exact::()? .map(Bound::into_gil_ref)) } /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. /// /// This function gets the optional target of this [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). @@ -384,7 +388,7 @@ impl PyWeakProxy { /// struct Foo { /* fields omitted */ } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { - /// if let Some(object) = reference.get_object() { + /// if let Some(object) = reference.upgrade() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -420,8 +424,8 @@ impl PyWeakProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn get_object(&self) -> Option<&'_ PyAny> { - self.as_borrowed().get_object().map(Bound::into_gil_ref) + pub fn upgrade(&self) -> Option<&'_ PyAny> { + self.as_borrowed().upgrade().map(Bound::into_gil_ref) } /// Retrieve to a object pointed to by the weakref. @@ -448,7 +452,7 @@ impl PyWeakProxy { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { /// reference - /// .get_object_raw() + /// .get_object() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -481,13 +485,13 @@ impl PyWeakProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - pub fn get_object_raw(&self) -> &'_ PyAny { - self.as_borrowed().get_object_raw().into_gil_ref() + pub fn get_object(&self) -> &'_ PyAny { + self.as_borrowed().get_object().into_gil_ref() } } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { - fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny> { + fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } .expect("The 'weakref.ProxyType' instance should be valid (non-null and actually a weakref reference)") @@ -550,7 +554,7 @@ mod tests { let reference = PyWeakProxy::new_bound(&object)?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); #[cfg(not(Py_LIMITED_API))] assert_eq!( @@ -580,7 +584,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); assert!(reference .getattr("__class__") .err() @@ -606,7 +610,7 @@ mod tests { } #[test] - fn test_weakref_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_as() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; @@ -614,7 +618,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -628,7 +632,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -641,7 +645,7 @@ mod tests { } #[test] - fn test_weakref_borrowed_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; @@ -649,7 +653,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -663,7 +667,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -676,72 +680,72 @@ mod tests { } #[test] - fn test_weakref_get_object() -> PyResult<()> { + fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakProxy::new_bound(&object)?; - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); drop(object); - assert!(reference.get_object().is_none()); + assert!(reference.upgrade().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { + fn test_weakref_upgrade_borrowed() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakProxy::new_bound(&object)?; - assert!(reference.borrow_object().is_some()); + assert!(reference.upgrade_borrowed().is_some()); assert!(reference - .borrow_object() + .upgrade_borrowed() .map_or(false, |obj| obj.is(&object))); drop(object); - assert!(reference.borrow_object().is_none()); + assert!(reference.upgrade_borrowed().is_none()); Ok(()) }) } #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { + fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakProxy::new_bound(&object)?; - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { + fn test_weakref_get_object_borrowed() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakProxy::new_bound(&object)?; - assert!(reference.borrow_object_raw().is(&object)); + assert!(reference.get_object_borrowed().is(&object)); drop(object); - assert!(reference.borrow_object_raw().is_none()); + assert!(reference.get_object_borrowed().is_none()); Ok(()) }) @@ -764,7 +768,7 @@ mod tests { let reference = PyWeakProxy::new_bound(&object)?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); #[cfg(not(Py_LIMITED_API))] assert_eq!( reference.get_type().to_string(), @@ -793,7 +797,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); assert!(reference .getattr("__class__") .err() @@ -819,13 +823,13 @@ mod tests { } #[test] - fn test_weakref_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_as() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(object.bind(py))?; { - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -837,7 +841,7 @@ mod tests { drop(object); { - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -850,13 +854,13 @@ mod tests { } #[test] - fn test_weakref_borrowed_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(object.bind(py))?; { - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -868,7 +872,7 @@ mod tests { drop(object); { - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -881,68 +885,68 @@ mod tests { } #[test] - fn test_weakref_get_object() -> PyResult<()> { + fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(object.bind(py))?; - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); drop(object); - assert!(reference.get_object().is_none()); + assert!(reference.upgrade().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { + fn test_weakref_upgrade_borrowed() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(object.bind(py))?; - assert!(reference.borrow_object().is_some()); + assert!(reference.upgrade_borrowed().is_some()); assert!(reference - .borrow_object() + .upgrade_borrowed() .map_or(false, |obj| obj.is(&object))); drop(object); - assert!(reference.borrow_object().is_none()); + assert!(reference.upgrade_borrowed().is_none()); Ok(()) }) } #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { + fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(object.bind(py))?; - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { + fn test_weakref_get_object_borrowed() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(object.bind(py))?; - assert!(reference.borrow_object_raw().is(&object)); + assert!(reference.get_object_borrowed().is(&object)); drop(object); - assert!(reference.borrow_object_raw().is_none()); + assert!(reference.get_object_borrowed().is_none()); Ok(()) }) diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 81f4e64a1d7..f4ad6ad815f 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -61,7 +61,7 @@ impl PyWeakRef { /// let weakref = PyWeakRef::new_bound(&foo)?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object() + /// weakref.upgrade() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// @@ -70,7 +70,7 @@ impl PyWeakRef { /// /// drop(foo); /// - /// assert!(weakref.get_object().is_none()); + /// assert!(weakref.upgrade().is_none()); /// Ok(()) /// }) /// # } @@ -129,7 +129,7 @@ impl PyWeakRef { /// #[pyfunction] /// fn callback(wref: Bound<'_, PyWeakRef>) -> PyResult<()> { /// let py = wref.py(); - /// assert!(wref.upgrade::()?.is_none()); + /// assert!(wref.upgrade_as::()?.is_none()); /// py.run_bound("counter = 1", None, None) /// } /// @@ -141,10 +141,10 @@ impl PyWeakRef { /// /// // This is fine. /// let weakref = PyWeakRef::new_bound_with(&foo, py.None())?; - /// assert!(weakref.upgrade::()?.is_some()); + /// assert!(weakref.upgrade_as::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.get_object() + /// weakref.upgrade() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); @@ -155,7 +155,7 @@ impl PyWeakRef { /// /// drop(foo); /// - /// assert!(weakref.upgrade::()?.is_none()); + /// assert!(weakref.upgrade_as::()?.is_none()); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 1); /// Ok(()) /// }) @@ -214,7 +214,7 @@ impl PyWeakRef { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.upgrade::()? { + /// if let Some(data_src) = reference.upgrade_as::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -252,11 +252,14 @@ impl PyWeakRef { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - pub fn upgrade(&self) -> PyResult> + pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, { - Ok(self.as_borrowed().upgrade::()?.map(Bound::into_gil_ref)) + Ok(self + .as_borrowed() + .upgrade_as::()? + .map(Bound::into_gil_ref)) } /// Upgrade the weakref to an exact direct object reference. @@ -287,7 +290,7 @@ impl PyWeakRef { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_exact::()? { + /// if let Some(data_src) = reference.upgrade_as_exact::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -325,18 +328,19 @@ impl PyWeakRef { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - pub fn upgrade_exact(&self) -> PyResult> + pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, { Ok(self .as_borrowed() - .upgrade_exact::()? + .upgrade_as_exact::()? .map(Bound::into_gil_ref)) } /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). @@ -358,7 +362,7 @@ impl PyWeakRef { /// struct Foo { /* fields omitted */ } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(object) = reference.get_object() { + /// if let Some(object) = reference.upgrade() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -394,8 +398,8 @@ impl PyWeakRef { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - pub fn get_object(&self) -> Option<&'_ PyAny> { - self.as_borrowed().get_object().map(Bound::into_gil_ref) + pub fn upgrade(&self) -> Option<&'_ PyAny> { + self.as_borrowed().upgrade().map(Bound::into_gil_ref) } /// Retrieve to a object pointed to by the weakref. @@ -422,7 +426,7 @@ impl PyWeakRef { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { /// reference - /// .get_object_raw() + /// .get_object() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -455,8 +459,8 @@ impl PyWeakRef { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - pub fn get_object_raw(&self) -> &'_ PyAny { - self.as_borrowed().get_object_raw().into_gil_ref() + pub fn get_object(&self) -> &'_ PyAny { + self.as_borrowed().get_object().into_gil_ref() } } @@ -495,7 +499,7 @@ pub trait PyWeakRefMethods<'py> { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.upgrade::()? { + /// if let Some(data_src) = reference.upgrade_as::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -533,11 +537,11 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn upgrade(&self) -> PyResult>> + fn upgrade_as(&self) -> PyResult>> where T: PyTypeCheck, { - self.get_object() + self.upgrade() .map(Bound::downcast_into::) .transpose() .map_err(Into::into) @@ -545,7 +549,7 @@ pub trait PyWeakRefMethods<'py> { /// Upgrade the weakref to a Borrowed object reference. /// - /// It is named `borrowed_upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example @@ -571,7 +575,7 @@ pub trait PyWeakRefMethods<'py> { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.borrowed_upgrade::()? { + /// if let Some(data_src) = reference.upgrade_borrowed_as::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -609,13 +613,13 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? - fn borrowed_upgrade<'a, T>(&'a self) -> PyResult>> + fn upgrade_borrowed_as<'a, T>(&'a self) -> PyResult>> where T: PyTypeCheck, 'py: 'a, { // TODO: Replace when Borrowed::downcast exists - match self.borrow_object() { + match self.upgrade_borrowed() { None => Ok(None), Some(object) if T::type_check(&object) => { Ok(Some(unsafe { object.downcast_unchecked() })) @@ -652,7 +656,7 @@ pub trait PyWeakRefMethods<'py> { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_exact::()? { + /// if let Some(data_src) = reference.upgrade_as_exact::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -690,21 +694,19 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn upgrade_exact(&self) -> PyResult>> + fn upgrade_as_exact(&self) -> PyResult>> where T: PyTypeInfo, { - self.get_object() + self.upgrade() .map(Bound::downcast_into_exact) .transpose() .map_err(Into::into) } - // TODO: ADD TESTS - // TODO: ADD DIRECT METHODS TO IMPLEMENTORS /// Upgrade the weakref to a exact Borrowed object reference. /// - /// It is named `borrowed_upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// In Python it would be equivalent to [`PyWeakref_GetObject`]. /// /// # Example @@ -730,7 +732,7 @@ pub trait PyWeakRefMethods<'py> { /// } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.borrowed_upgrade_exact::()? { + /// if let Some(data_src) = reference.upgrade_borrowed_as_exact::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); /// Ok(format!("Processing '{}': score = {}", name, score)) @@ -768,13 +770,13 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? - fn borrowed_upgrade_exact<'a, T>(&'a self) -> PyResult>> + fn upgrade_borrowed_as_exact<'a, T>(&'a self) -> PyResult>> where T: PyTypeInfo, 'py: 'a, { // TODO: Replace when Borrowed::downcast_exact exists - match self.borrow_object() { + match self.upgrade_borrowed() { None => Ok(None), Some(object) if object.is_exact_instance_of::() => { Ok(Some(unsafe { object.downcast_unchecked() })) @@ -783,9 +785,9 @@ pub trait PyWeakRefMethods<'py> { } } - // TODO: NAMING-ALTERNATIVE: upgrade_any /// Upgrade the weakref to a Bound [`PyAny`] reference to the target object if possible. /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// This function returns `Some(Bound<'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). @@ -807,7 +809,7 @@ pub trait PyWeakRefMethods<'py> { /// struct Foo { /* fields omitted */ } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(object) = reference.get_object() { + /// if let Some(object) = reference.upgrade() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -843,8 +845,8 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn get_object(&self) -> Option> { - let object = self.get_object_raw(); + fn upgrade(&self) -> Option> { + let object = self.get_object(); if object.is_none() { None @@ -853,9 +855,9 @@ pub trait PyWeakRefMethods<'py> { } } - // TODO: NAMING-ALTERNATIVE: upgrade_any_borrowed /// Upgrade the weakref to a Borrowed [`PyAny`] reference to the target object if possible. /// + /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// This function returns `Some(Borrowed<'_, 'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). @@ -877,7 +879,7 @@ pub trait PyWeakRefMethods<'py> { /// struct Foo { /* fields omitted */ } /// /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(object) = reference.borrow_object() { + /// if let Some(object) = reference.upgrade_borrowed() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { /// Ok("The object, which this reference refered to, no longer exists".to_owned()) @@ -913,11 +915,11 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn borrow_object<'a>(&'a self) -> Option> + fn upgrade_borrowed<'a>(&'a self) -> Option> where 'py: 'a, { - let object = self.borrow_object_raw(); + let object = self.get_object_borrowed(); if object.is_none() { None @@ -926,7 +928,6 @@ pub trait PyWeakRefMethods<'py> { } } - // TODO: NAMING-ALTERNATIVE: get_any /// Retrieve to a Bound object pointed to by the weakref. /// /// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). @@ -951,7 +952,7 @@ pub trait PyWeakRefMethods<'py> { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { /// reference - /// .get_object_raw() + /// .get_object() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -984,12 +985,11 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn get_object_raw(&self) -> Bound<'py, PyAny> { + fn get_object(&self) -> Bound<'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. - self.borrow_object_raw().to_owned() + self.get_object_borrowed().to_owned() } - // TODO: NAMING-ALTERNATIVE: get_any_borrowed /// Retrieve to a Borrowed object pointed to by the weakref. /// /// This function returns `Borrowed<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). @@ -1014,7 +1014,7 @@ pub trait PyWeakRefMethods<'py> { /// /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { /// reference - /// .borrow_object_raw() + /// .get_object_borrowed() /// .getattr("__class__")? /// .repr()? /// .to_str() @@ -1047,11 +1047,11 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny>; + fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny>; } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { - fn borrow_object_raw(&self) -> Borrowed<'_, 'py, PyAny> { + fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } .expect("The 'weakref.ReferenceType' instance should be valid (non-null and actually a weakref reference)") @@ -1119,7 +1119,7 @@ mod tests { let reference = PyWeakRef::new_bound(&object)?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.get_type().to_string(), CLASS_NAME); @@ -1137,7 +1137,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr(&reference, None)?; @@ -1153,7 +1153,7 @@ mod tests { } #[test] - fn test_weakref_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_as() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; @@ -1161,7 +1161,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -1175,7 +1175,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -1188,7 +1188,7 @@ mod tests { } #[test] - fn test_weakref_borrowed_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; @@ -1196,7 +1196,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -1210,7 +1210,7 @@ mod tests { { // This test is a bit weird but ok. - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -1223,81 +1223,81 @@ mod tests { } #[test] - fn test_weakref_get_object() -> PyResult<()> { + fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakRef::new_bound(&object)?; assert!(reference.call0()?.is(&object)); - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.get_object().is_none()); + assert!(reference.upgrade().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { + fn test_weakref_upgrade_borrowed() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakRef::new_bound(&object)?; assert!(reference.call0()?.is(&object)); - assert!(reference.borrow_object().is_some()); + assert!(reference.upgrade_borrowed().is_some()); assert!(reference - .borrow_object() + .upgrade_borrowed() .map_or(false, |obj| obj.is(&object))); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.borrow_object().is_none()); + assert!(reference.upgrade_borrowed().is_none()); Ok(()) }) } #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { + fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakRef::new_bound(&object)?; assert!(reference.call0()?.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); drop(object); - assert!(reference.call0()?.is(&reference.get_object_raw())); + assert!(reference.call0()?.is(&reference.get_object())); assert!(reference.call0()?.is_none()); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { + fn test_weakref_get_object_borrowed() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; let reference = PyWeakRef::new_bound(&object)?; assert!(reference.call0()?.is(&object)); - assert!(reference.borrow_object_raw().is(&object)); + assert!(reference.get_object_borrowed().is(&object)); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.borrow_object_raw().is_none()); + assert!(reference.get_object_borrowed().is_none()); Ok(()) }) @@ -1320,7 +1320,7 @@ mod tests { let reference = PyWeakRef::new_bound(&object)?; assert!(!reference.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.get_type().to_string(), CLASS_NAME); @@ -1339,7 +1339,7 @@ mod tests { drop(object); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); check_repr(&reference, None)?; @@ -1355,13 +1355,13 @@ mod tests { } #[test] - fn test_weakref_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_as() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakRef::new_bound(object.bind(py))?; { - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -1373,7 +1373,7 @@ mod tests { drop(object); { - let obj = reference.upgrade::(); + let obj = reference.upgrade_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -1386,13 +1386,13 @@ mod tests { } #[test] - fn test_weakref_borrowed_upgrade() -> PyResult<()> { + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakRef::new_bound(object.bind(py))?; { - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -1404,7 +1404,7 @@ mod tests { drop(object); { - let obj = reference.borrowed_upgrade::(); + let obj = reference.upgrade_borrowed_as::(); assert!(obj.is_ok()); let obj = obj.unwrap(); @@ -1417,77 +1417,77 @@ mod tests { } #[test] - fn test_weakref_get_object() -> PyResult<()> { + fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakRef::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); - assert!(reference.get_object().is_some()); - assert!(reference.get_object().map_or(false, |obj| obj.is(&object))); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.get_object().is_none()); + assert!(reference.upgrade().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrrow_object() -> PyResult<()> { + fn test_weakref_upgrade_borrowed() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakRef::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); - assert!(reference.borrow_object().is_some()); + assert!(reference.upgrade_borrowed().is_some()); assert!(reference - .borrow_object() + .upgrade_borrowed() .map_or(false, |obj| obj.is(&object))); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.borrow_object().is_none()); + assert!(reference.upgrade_borrowed().is_none()); Ok(()) }) } #[test] - fn test_weakref_get_object_raw() -> PyResult<()> { + fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakRef::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); - assert!(reference.get_object_raw().is(&object)); + assert!(reference.get_object().is(&object)); drop(object); - assert!(reference.call0()?.is(&reference.get_object_raw())); + assert!(reference.call0()?.is(&reference.get_object())); assert!(reference.call0()?.is_none()); - assert!(reference.get_object_raw().is_none()); + assert!(reference.get_object().is_none()); Ok(()) }) } #[test] - fn test_weakref_borrow_object_raw() -> PyResult<()> { + fn test_weakref_get_object_borrowed() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; let reference = PyWeakRef::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); - assert!(reference.borrow_object_raw().is(&object)); + assert!(reference.get_object_borrowed().is(&object)); drop(object); assert!(reference.call0()?.is_none()); - assert!(reference.borrow_object_raw().is_none()); + assert!(reference.get_object_borrowed().is_none()); Ok(()) }) From 4a0a0a965e9605dca4f17d4410858b6aa2c3c915 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Tue, 12 Mar 2024 18:40:38 +0100 Subject: [PATCH 27/42] Change weakref constructors to take PyAny for main target --- src/types/weakref/callableproxy.rs | 16 ++++++++-------- src/types/weakref/proxy.rs | 16 ++++++++-------- src/types/weakref/reference.rs | 17 ++++++++--------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 3de3b9ab889..f2424cba2ee 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -36,7 +36,7 @@ impl PyWeakCallableProxy { where T: PyNativeType, { - Self::new_bound(&object.as_borrowed()).map(Bound::into_gil_ref) + Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) } /// Constructs a new Weak callable Reference (`weakref.proxy`/`weakref.CallableProxyType`) for the given object. @@ -100,7 +100,7 @@ impl PyWeakCallableProxy { /// #[inline] #[track_caller] - pub fn new_bound<'py, T>(object: &Bound<'py, T>) -> PyResult> { + pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { // TODO: Must track caller be used here? fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { assert!( @@ -117,7 +117,7 @@ impl PyWeakCallableProxy { } } - inner(object.as_any()) + inner(object) } /// Deprecated form of [`PyWeakCallableProxy::new_bound_with`]. @@ -135,7 +135,7 @@ impl PyWeakCallableProxy { T: PyNativeType, C: ToPyObject, { - Self::new_bound_with(&object.as_borrowed(), callback).map(Bound::into_gil_ref) + Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.CallableProxyType`) for the given object with a callback. @@ -205,8 +205,8 @@ impl PyWeakCallableProxy { /// # Panics /// This function panics if the provided object is not callable. #[track_caller] - pub fn new_bound_with<'py, T, C>( - object: &Bound<'py, T>, + pub fn new_bound_with<'py, C>( + object: &Bound<'py, PyAny>, callback: C, ) -> PyResult> where @@ -232,7 +232,7 @@ impl PyWeakCallableProxy { } let py = object.py(); - inner(object.as_any(), callback.to_object(py).into_bound(py)) + inner(object, callback.to_object(py).into_bound(py)) } /// Upgrade the weakref to a direct object reference. @@ -815,7 +815,7 @@ mod tests { #[test] fn test_weakref_proxy_behavior() -> PyResult<()> { Python::with_gil(|py| { - let object = Bound::new(py, WeakrefablePyClass {})?; + let object: Bound<'_, WeakrefablePyClass> = Bound::new(py, WeakrefablePyClass {})?; let reference = PyWeakCallableProxy::new_bound(&object)?; assert!(!reference.is(&object)); diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index abbccf5efbe..7d548999fb7 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -36,7 +36,7 @@ impl PyWeakProxy { where T: PyNativeType, { - Self::new_bound(&object.as_borrowed()).map(Bound::into_gil_ref) + Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`) for the given object. @@ -85,7 +85,7 @@ impl PyWeakProxy { /// #[inline] #[track_caller] - pub fn new_bound<'py, T>(object: &Bound<'py, T>) -> PyResult> { + pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { // TODO: Is this inner pattern still necessary Here? // TODO: Must track caller be used here? fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { @@ -103,7 +103,7 @@ impl PyWeakProxy { } } - inner(object.as_any()) + inner(object) } /// Deprecated form of [`PyWeakProxy::new_bound_with`]. @@ -121,7 +121,7 @@ impl PyWeakProxy { T: PyNativeType, C: ToPyObject, { - Self::new_bound_with(&object.as_borrowed(), callback).map(Bound::into_gil_ref) + Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`) for the given object with a callback. @@ -182,8 +182,8 @@ impl PyWeakProxy { /// # Panics /// This function panics if the provided object is callable. #[track_caller] - pub fn new_bound_with<'py, T, C>( - object: &Bound<'py, T>, + pub fn new_bound_with<'py, C>( + object: &Bound<'py, PyAny>, callback: C, ) -> PyResult> where @@ -209,7 +209,7 @@ impl PyWeakProxy { } let py = object.py(); - inner(object.as_any(), callback.to_object(py).into_bound(py)) + inner(object, callback.to_object(py).into_bound(py)) } /// Upgrade the weakref to a direct object reference. @@ -764,7 +764,7 @@ mod tests { #[test] fn test_weakref_proxy_behavior() -> PyResult<()> { Python::with_gil(|py| { - let object = Bound::new(py, WeakrefablePyClass {})?; + let object: Bound<'_, WeakrefablePyClass> = Bound::new(py, WeakrefablePyClass {})?; let reference = PyWeakProxy::new_bound(&object)?; assert!(!reference.is(&object)); diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index f4ad6ad815f..f18aa16b78e 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -33,7 +33,7 @@ impl PyWeakRef { where T: PyNativeType, { - Self::new_bound(&object.as_borrowed()).map(Bound::into_gil_ref) + Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object. @@ -75,7 +75,7 @@ impl PyWeakRef { /// }) /// # } /// ``` - pub fn new_bound<'py, T>(object: &Bound<'py, T>) -> PyResult> { + pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { // TODO: Is this inner pattern still necessary Here? fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { unsafe { @@ -87,7 +87,7 @@ impl PyWeakRef { } } - inner(object.as_any()) + inner(object) } /// Deprecated form of [`PyWeakRef::new_bound_with`]. @@ -104,7 +104,7 @@ impl PyWeakRef { T: PyNativeType, C: ToPyObject, { - Self::new_bound_with(&object.as_borrowed(), callback).map(Bound::into_gil_ref) + Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) } /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object with a callback. @@ -161,12 +161,11 @@ impl PyWeakRef { /// }) /// # } /// ``` - pub fn new_bound_with<'py, T, C>( - object: &Bound<'py, T>, + pub fn new_bound_with<'py, C>( + object: &Bound<'py, PyAny>, callback: C, ) -> PyResult> where - Bound<'py, T>: AsPyPointer, C: ToPyObject, { fn inner<'py>( @@ -183,7 +182,7 @@ impl PyWeakRef { } let py = object.py(); - inner(object.as_any(), callback.to_object(py).into_bound(py)) + inner(object, callback.to_object(py).into_bound(py)) } /// Upgrade the weakref to a direct object reference. @@ -1316,7 +1315,7 @@ mod tests { #[test] fn test_weakref_refence_behavior() -> PyResult<()> { Python::with_gil(|py| { - let object = Bound::new(py, WeakrefablePyClass {})?; + let object: Bound<'_, WeakrefablePyClass> = Bound::new(py, WeakrefablePyClass {})?; let reference = PyWeakRef::new_bound(&object)?; assert!(!reference.is(&object)); From f2404e0b69822943d6be8e8cb20e08e2514957e0 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 15 Mar 2024 09:48:15 +0100 Subject: [PATCH 28/42] Add track_caller to all panicing weakref methods --- src/types/weakref/callableproxy.rs | 7 +++++++ src/types/weakref/proxy.rs | 7 +++++++ src/types/weakref/reference.rs | 14 ++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index f2424cba2ee..276e1e39c05 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -102,6 +102,7 @@ impl PyWeakCallableProxy { #[track_caller] pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { // TODO: Must track caller be used here? + #[track_caller] fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { assert!( object.is_callable(), @@ -213,6 +214,7 @@ impl PyWeakCallableProxy { C: ToPyObject, { // TODO: Must track caller be used here? + #[track_caller] fn inner<'py>( object: &Bound<'py, PyAny>, callback: Bound<'py, PyAny>, @@ -307,6 +309,7 @@ impl PyWeakCallableProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, @@ -389,6 +392,7 @@ impl PyWeakCallableProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, @@ -468,6 +472,7 @@ impl PyWeakCallableProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyCallableType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] pub fn upgrade(&self) -> Option<&'_ PyAny> { self.as_borrowed().upgrade().map(Bound::into_gil_ref) } @@ -538,12 +543,14 @@ impl PyWeakCallableProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] pub fn get_object(&self) -> &'_ PyAny { self.as_borrowed().get_object().into_gil_ref() } } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakCallableProxy> { + #[track_caller] fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 7d548999fb7..87d2828b2bc 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -88,6 +88,7 @@ impl PyWeakProxy { pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { // TODO: Is this inner pattern still necessary Here? // TODO: Must track caller be used here? + #[track_caller] fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { assert!( !object.is_callable(), @@ -190,6 +191,7 @@ impl PyWeakProxy { C: ToPyObject, { // TODO: Must track caller be used here? + #[track_caller] fn inner<'py>( object: &Bound<'py, PyAny>, callback: Bound<'py, PyAny>, @@ -278,6 +280,7 @@ impl PyWeakProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, @@ -354,6 +357,7 @@ impl PyWeakProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, @@ -424,6 +428,7 @@ impl PyWeakProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] pub fn upgrade(&self) -> Option<&'_ PyAny> { self.as_borrowed().upgrade().map(Bound::into_gil_ref) } @@ -485,12 +490,14 @@ impl PyWeakProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] pub fn get_object(&self) -> &'_ PyAny { self.as_borrowed().get_object().into_gil_ref() } } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { + #[track_caller] fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index f18aa16b78e..ee693a5146f 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -251,6 +251,7 @@ impl PyWeakRef { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, @@ -327,6 +328,7 @@ impl PyWeakRef { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, @@ -397,6 +399,7 @@ impl PyWeakRef { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] pub fn upgrade(&self) -> Option<&'_ PyAny> { self.as_borrowed().upgrade().map(Bound::into_gil_ref) } @@ -458,6 +461,7 @@ impl PyWeakRef { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] pub fn get_object(&self) -> &'_ PyAny { self.as_borrowed().get_object().into_gil_ref() } @@ -536,6 +540,7 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] fn upgrade_as(&self) -> PyResult>> where T: PyTypeCheck, @@ -612,6 +617,7 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? + #[track_caller] fn upgrade_borrowed_as<'a, T>(&'a self) -> PyResult>> where T: PyTypeCheck, @@ -693,6 +699,7 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] fn upgrade_as_exact(&self) -> PyResult>> where T: PyTypeInfo, @@ -769,6 +776,7 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? + #[track_caller] fn upgrade_borrowed_as_exact<'a, T>(&'a self) -> PyResult>> where T: PyTypeInfo, @@ -844,6 +852,7 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] fn upgrade(&self) -> Option> { let object = self.get_object(); @@ -914,6 +923,7 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] fn upgrade_borrowed<'a>(&'a self) -> Option> where 'py: 'a, @@ -984,6 +994,7 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] fn get_object(&self) -> Bound<'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. self.get_object_borrowed().to_owned() @@ -1046,10 +1057,13 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + // TODO: This function is the reason every function tracks caller, however it only panics when the weakref object is not actually a weakreference type. So is it this neccessary? fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny>; } impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { + #[track_caller] fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } From 377816122e1d5ee21c72a89cf706b475d3230b58 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:29:31 +0100 Subject: [PATCH 29/42] Add PyWeakRefMethods::upgrade*_as_unchecked --- src/types/weakref/callableproxy.rs | 194 ++++++++++++++++ src/types/weakref/proxy.rs | 190 ++++++++++++++++ src/types/weakref/reference.rs | 345 ++++++++++++++++++++++++++++- 3 files changed, 728 insertions(+), 1 deletion(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 276e1e39c05..4c149871bcf 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -320,6 +320,92 @@ impl PyWeakCallableProxy { .map(Bound::into_gil_ref)) } + /// 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`. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Safety + /// Callers must ensure that the type is valid or risk type confusion. + /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakCallableProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn __call__(&self) -> &str { + /// "This class is callable" + /// } + /// + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> String { + /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// format!("Processing '{}': score = {}", name, score) + /// } else { + /// "The supplied data reference is nolonger relavent.".to_owned() + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakCallableProxy::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "Processing 'Dave': score = 10" + /// ); + /// + /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] + pub unsafe fn upgrade_as_unchecked(&self) -> Option<&T::AsRefTarget> + where + T: PyTypeCheck, + { + self.as_borrowed() + .upgrade_as_unchecked::() + .map(Bound::into_gil_ref) + } + /// Upgrade the weakref to an exact direct object reference. /// /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). @@ -730,6 +816,64 @@ mod tests { }) } + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakCallableProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { @@ -929,6 +1073,56 @@ mod tests { }) } + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; + + { + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 87d2828b2bc..cff11b6b761 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -291,6 +291,86 @@ impl PyWeakProxy { .map(Bound::into_gil_ref)) } + /// 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`. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Safety + /// Callers must ensure that the type is valid or risk type confusion. + /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakProxy; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> String { + /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// format!("Processing '{}': score = {}", name, score) + /// } else { + /// "The supplied data reference is nolonger relavent.".to_owned() + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakProxy::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType + /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy + #[track_caller] + pub unsafe fn upgrade_as_unchecked(&self) -> Option<&T::AsRefTarget> + where + T: PyTypeCheck, + { + self.as_borrowed() + .upgrade_as_unchecked::() + .map(Bound::into_gil_ref) + } + /// Upgrade the weakref to an exact direct object reference. /// /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). @@ -686,6 +766,64 @@ mod tests { }) } + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { @@ -891,6 +1029,58 @@ mod tests { }) } + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(object.bind(py))?; + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakProxy::new_bound(object.bind(py))?; + + { + let obj = + unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = + unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index ee693a5146f..470bc96719b 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -262,6 +262,86 @@ impl PyWeakRef { .map(Bound::into_gil_ref)) } + /// 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`. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`] or calling the [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// + /// # Safety + /// Callers must ensure that the type is valid or risk type confusion. + /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> String { + /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// format!("Processing '{}': score = {}", name, score) + /// } else { + /// "The supplied data reference is nolonger relavent.".to_owned() + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + pub unsafe fn upgrade_as_unchecked(&self) -> Option<&T::AsRefTarget> + where + T: PyTypeCheck, + { + self.as_borrowed() + .upgrade_as_unchecked::() + .map(Bound::into_gil_ref) + } + /// Upgrade the weakref to an exact direct object reference. /// /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). @@ -633,6 +713,159 @@ pub trait PyWeakRefMethods<'py> { } } + /// 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`. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Safety + /// Callers must ensure that the type is valid or risk type confusion. + /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> String { + /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// format!("Processing '{}': score = {}", name, score) + /// } else { + /// "The supplied data reference is nolonger relavent.".to_owned() + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + unsafe fn upgrade_as_unchecked(&self) -> Option> { + Some(self.upgrade()?.downcast_into_unchecked()) + } + + /// Upgrade the weakref to a Borrowed 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`. + /// + /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Safety + /// Callers must ensure that the type is valid or risk type confusion. + /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakRef; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> String { + /// if let Some(data_src) = unsafe { reference.upgrade_borrowed_as_unchecked::() } { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// format!("Processing '{}': score = {}", name, score) + /// } else { + /// "The supplied data reference is nolonger relavent.".to_owned() + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakRef::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? + #[track_caller] + unsafe fn upgrade_borrowed_as_unchecked<'a, T>(&'a self) -> Option> + where + 'py: 'a, + { + Some(self.upgrade_borrowed()?.downcast_unchecked()) + } + /// Upgrade the weakref to a exact direct Bound object reference. /// /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). @@ -1057,7 +1290,7 @@ pub trait PyWeakRefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] + #[track_caller] // TODO: This function is the reason every function tracks caller, however it only panics when the weakref object is not actually a weakreference type. So is it this neccessary? fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny>; } @@ -1235,6 +1468,64 @@ mod tests { }) } + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakRef::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { @@ -1429,6 +1720,58 @@ mod tests { }) } + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(object.bind(py))?; + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakRef::new_bound(object.bind(py))?; + + { + let obj = + unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = + unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { From b4007bd06785a5ea026b237ec098c7e54e870be3 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:39:23 +0100 Subject: [PATCH 30/42] Fix PyWeakProxy and PyWeakCallableProxy Documentation --- src/types/weakref/callableproxy.rs | 13 ++++++++----- src/types/weakref/proxy.rs | 4 +++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index 4c149871bcf..f633f5fa420 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -7,9 +7,11 @@ use crate::{ffi, AsPyPointer, Borrowed, Bound, PyNativeType, ToPyObject}; use super::PyWeakRefMethods; -/// Represents a Python `weakref.ProxyType`. +/// Represents a Python `weakref.CallableProxyType`. /// -/// In Python this is created by calling `weakref.proxy`. +/// In Python this is created by calling `weakref.proxy` with a callable argument as the referenced object. +/// +/// If the referenced object is non-callable [`PyWeakProxy`](super::PyWeakProxy) should be used instead. #[repr(transparent)] pub struct PyWeakCallableProxy(PyAny); @@ -1104,7 +1106,8 @@ mod tests { let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; { - let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + let obj = + unsafe { reference.upgrade_borrowed_as_unchecked::() }; assert!(obj.is_some()); assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); @@ -1113,7 +1116,8 @@ mod tests { drop(object); { - let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + let obj = + unsafe { reference.upgrade_borrowed_as_unchecked::() }; assert!(obj.is_none()); } @@ -1122,7 +1126,6 @@ mod tests { }) } - #[test] fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index cff11b6b761..3401a715bdc 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -9,7 +9,9 @@ use super::PyWeakRefMethods; /// Represents a Python `weakref.ProxyType`. /// -/// In Python this is created by calling `weakref.proxy`. +/// In Python this is created by calling `weakref.proxy` with a non-callable argument as the referenced object. +/// +/// If the referenced object is callable [`PyWeakCallableProxy`](super::PyWeakCallableProxy) should be used instead. #[repr(transparent)] pub struct PyWeakProxy(PyAny); From 6f2aaf1c3266bafdf20d3c6fcef750edb8f75471 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:47:39 +0100 Subject: [PATCH 31/42] Replace deprecated wrap_pyfunction with bound equivalent --- src/types/weakref/callableproxy.rs | 2 +- src/types/weakref/proxy.rs | 2 +- src/types/weakref/reference.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs index f633f5fa420..743328e54c7 100644 --- a/src/types/weakref/callableproxy.rs +++ b/src/types/weakref/callableproxy.rs @@ -191,7 +191,7 @@ impl PyWeakCallableProxy { /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakCallableProxy::new_bound_with(&foo, wrap_pyfunction!(callback, py)?)?; + /// let weakref2 = PyWeakCallableProxy::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 3401a715bdc..6b33ab75ed7 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -170,7 +170,7 @@ impl PyWeakProxy { /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakProxy::new_bound_with(&foo, wrap_pyfunction!(callback, py)?)?; + /// let weakref2 = PyWeakProxy::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 470bc96719b..420ef4b2a5a 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -149,7 +149,7 @@ impl PyWeakRef { /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakRef::new_bound_with(&foo, wrap_pyfunction!(callback, py)?)?; + /// let weakref2 = PyWeakRef::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// From a45c043a64a2da499f0db691d18b0a1ca1750d4f Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:04:31 +0200 Subject: [PATCH 32/42] Add (Generic) PyWeakref Type --- src/types/mod.rs | 4 +- src/types/weakref/anyref.rs | 1017 +++++++++++++++++++++++++++++++++++ src/types/weakref/mod.rs | 2 + 3 files changed, 1021 insertions(+), 2 deletions(-) create mode 100644 src/types/weakref/anyref.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index 2b62024c250..31de19434f4 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -48,7 +48,7 @@ pub use self::traceback::{PyTraceback, PyTracebackMethods}; pub use self::tuple::{PyTuple, PyTupleMethods}; pub use self::typeobject::{PyType, PyTypeMethods}; #[cfg(not(PyPy))] -pub use self::weakref::{PyWeakCallableProxy, PyWeakProxy, PyWeakRef, PyWeakRefMethods}; +pub use self::weakref::{PyWeakCallableProxy, PyWeakProxy, PyWeakRef, PyWeakRefMethods, PyWeakref}; /// Iteration over Python collections. /// @@ -359,5 +359,5 @@ pub(crate) mod string; pub(crate) mod traceback; pub(crate) mod tuple; pub(crate) mod typeobject; -#[cfg(not(PyPy))] +#[cfg(not(any(PyPy, GraalPy)))] // FIXME: Remove this soon pub(crate) mod weakref; diff --git a/src/types/weakref/anyref.rs b/src/types/weakref/anyref.rs new file mode 100644 index 00000000000..9f9f391b20a --- /dev/null +++ b/src/types/weakref/anyref.rs @@ -0,0 +1,1017 @@ +use crate::ffi_ptr_ext::FfiPtrExt; +use crate::types::PyAny; +use crate::{ffi, Borrowed, Bound, PyNativeType, PyResult, PyTypeCheck, PyTypeInfo}; + +use super::PyWeakRefMethods; + +/// Represents any Python `weakref` reference. +/// +/// In Python this is created by calling `weakref.ref` or `weakref.proxy`. +#[repr(transparent)] +pub struct PyWeakref(PyAny); + +pyobject_native_type_named!(PyWeakref); +pyobject_native_type_extract!(PyWeakref); + +// TODO: We known the layout but this cannot be implemented, due to the lack of public typeobject pointers +// #[cfg(not(Py_LIMITED_API))] +// pyobject_native_type_sized!(PyWeakref, ffi::PyWeakReference); + +impl PyTypeCheck for PyWeakref { + const NAME: &'static str = "weakref"; + + fn type_check(object: &Bound<'_, PyAny>) -> bool { + unsafe { ffi::PyWeakref_Check(object.as_ptr()) > 0 } + } +} + +impl PyWeakref { + // TODO: MAYBE ADD CREATION METHODS OR EASY CASTING?; + + /// Upgrade the weakref to a direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`] or retrieving the Object from Python. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakref>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_as::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let proxy = PyWeakProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. + /// let reference = proxy.downcast::()?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + #[track_caller] + pub fn upgrade_as(&self) -> PyResult> + where + T: PyTypeCheck, + { + Ok(self + .as_borrowed() + .upgrade_as::()? + .map(Bound::into_gil_ref)) + } + + /// 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`. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`] or retrieving the Object from Python. + /// + /// # Safety + /// Callers must ensure that the type is valid or risk type confusion. + /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakref>) -> String { + /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// format!("Processing '{}': score = {}", name, score) + /// } else { + /// "The supplied data reference is nolonger relavent.".to_owned() + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let proxy = PyWeakProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. + /// let reference = proxy.downcast::()?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + #[track_caller] + pub unsafe fn upgrade_as_unchecked(&self) -> Option<&T::AsRefTarget> + where + T: PyTypeCheck, + { + self.as_borrowed() + .upgrade_as_unchecked::() + .map(Bound::into_gil_ref) + } + + /// Upgrade the weakref to an exact direct object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`] or retrieving the Object from Python. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakref>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_as_exact::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let proxy = PyWeakProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. + /// let reference = proxy.downcast::()?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + #[track_caller] + pub fn upgrade_as_exact(&self) -> PyResult> + where + T: PyTypeInfo, + { + Ok(self + .as_borrowed() + .upgrade_as_exact::()? + .map(Bound::into_gil_ref)) + } + + /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. + /// + /// This function gets the optional target of this [`PyWeakref`] (Any Python `weakref` weakreference). + /// It produces similair results as using [`PyWeakref_GetObject`] in the C api or retrieving the Object from Python. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakref>) -> PyResult { + /// if let Some(object) = reference.upgrade() { + /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) + /// } else { + /// Ok("The object, which this reference refered to, no longer exists".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let proxy = PyWeakProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. + /// let reference = proxy.downcast::()?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object 'Foo' refered by this reference still exists." + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object, which this reference refered to, no longer exists" + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + #[track_caller] + pub fn upgrade(&self) -> Option<&'_ PyAny> { + self.as_borrowed().upgrade().map(Bound::into_gil_ref) + } + + /// Retrieve to a object pointed to by the weakref. + /// + /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// + /// This function gets the optional target of this [`PyWeakref`] (Any Python `weakref` weakreference). + /// It produces similair results as using [`PyWeakref_GetObject`] in the C api or retrieving the Object from Python. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn get_class(reference: Borrowed<'_, '_, PyWeakref>) -> PyResult { + /// reference + /// .get_object() + /// .getattr("__class__")? + /// .repr()? + /// .to_str() + /// .map(ToOwned::to_owned) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let object = Bound::new(py, Foo{})?; + /// let proxy = PyWeakProxy::new_bound(&object)?; // Retrieve this as an PyMethods argument. + /// let reference = proxy.downcast::()?; + /// + /// assert_eq!( + /// get_class(reference.as_borrowed())?, + /// "" + /// ); + /// + /// drop(object); + /// + /// assert_eq!(get_class(reference.as_borrowed())?, ""); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + #[track_caller] + pub fn get_object(&self) -> &'_ PyAny { + self.as_borrowed().get_object().into_gil_ref() + } +} + +impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakref> { + fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { + // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. + unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } + .expect("The 'weakref' weak reference instance should be valid (non-null and actually a weakref reference)") + } +} + +#[cfg(test)] +mod tests { + use crate::types::any::{PyAny, PyAnyMethods}; + use crate::types::weakref::{PyWeakProxy, PyWeakRef, PyWeakRefMethods, PyWeakref}; + use crate::{Bound, PyResult, Python}; + + fn new_reference<'py>(object: &Bound<'py, PyAny>) -> PyResult> { + let reference = PyWeakRef::new_bound(object)?; + reference.into_any().downcast_into().map_err(Into::into) + } + + fn new_proxy<'py>(object: &Bound<'py, PyAny>) -> PyResult> { + let reference = PyWeakProxy::new_bound(object)?; + reference.into_any().downcast_into().map_err(Into::into) + } + + mod python_class { + use super::*; + use crate::{py_result_ext::PyResultExt, types::PyType}; + + fn get_type(py: Python<'_>) -> PyResult> { + py.run_bound("class A:\n pass\n", None, None)?; + py.eval_bound("A", None, None).downcast_into::() + } + + #[test] + fn test_weakref_upgrade_as() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + ) -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = create_reference(&object)?; + + { + // This test is a bit weird but ok. + let obj = reference.upgrade_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = reference.upgrade_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + inner(new_reference)?; + inner(new_proxy) + } + + #[test] + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + ) -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = create_reference(&object)?; + + { + // This test is a bit weird but ok. + let obj = reference.upgrade_borrowed_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = reference.upgrade_borrowed_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + inner(new_reference)?; + inner(new_proxy) + } + + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + ) -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = create_reference(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + inner(new_reference)?; + inner(new_proxy) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + ) -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = create_reference(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + inner(new_reference)?; + inner(new_proxy) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + call_retrievable: bool, + ) -> PyResult<()> { + let not_call_retrievable = !call_retrievable; + + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = create_reference(&object)?; + + assert!(not_call_retrievable || reference.call0()?.is(&object)); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(not_call_retrievable || reference.call0()?.is_none()); + assert!(reference.upgrade().is_none()); + + Ok(()) + }) + } + + inner(new_reference, true)?; + inner(new_proxy, false) + } + + #[test] + fn test_weakref_upgrade_borrowed() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + call_retrievable: bool, + ) -> PyResult<()> { + let not_call_retrievable = !call_retrievable; + + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = create_reference(&object)?; + + assert!(not_call_retrievable || reference.call0()?.is(&object)); + assert!(reference.upgrade_borrowed().is_some()); + assert!(reference + .upgrade_borrowed() + .map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(not_call_retrievable || reference.call0()?.is_none()); + assert!(reference.upgrade_borrowed().is_none()); + + Ok(()) + }) + } + + inner(new_reference, true)?; + inner(new_proxy, false) + } + + #[test] + fn test_weakref_get_object() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + call_retrievable: bool, + ) -> PyResult<()> { + let not_call_retrievable = !call_retrievable; + + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = create_reference(&object)?; + + assert!(not_call_retrievable || reference.call0()?.is(&object)); + assert!(reference.get_object().is(&object)); + + drop(object); + + assert!(not_call_retrievable || reference.call0()?.is(&reference.get_object())); + assert!(not_call_retrievable || reference.call0()?.is_none()); + assert!(reference.get_object().is_none()); + + Ok(()) + }) + } + + inner(new_reference, true)?; + inner(new_proxy, false) + } + + #[test] + fn test_weakref_get_object_borrowed() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + call_retrievable: bool, + ) -> PyResult<()> { + let not_call_retrievable = !call_retrievable; + + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = create_reference(&object)?; + + assert!(not_call_retrievable || reference.call0()?.is(&object)); + assert!(reference.get_object_borrowed().is(&object)); + + drop(object); + + assert!(not_call_retrievable || reference.call0()?.is_none()); + assert!(reference.get_object_borrowed().is_none()); + + Ok(()) + }) + } + + inner(new_reference, true)?; + inner(new_proxy, false) + } + } + + // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable. + #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))] + mod pyo3_pyclass { + use super::*; + use crate::{pyclass, Py}; + + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} + + #[test] + fn test_weakref_upgrade_as() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + ) -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = create_reference(object.bind(py))?; + + { + let obj = reference.upgrade_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = reference.upgrade_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + inner(new_reference)?; + inner(new_proxy) + } + + #[test] + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + ) -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = create_reference(object.bind(py))?; + + { + let obj = reference.upgrade_borrowed_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = reference.upgrade_borrowed_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + inner(new_reference)?; + inner(new_proxy) + } + + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + ) -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = create_reference(object.bind(py))?; + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + inner(new_reference)?; + inner(new_proxy) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + ) -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = create_reference(object.bind(py))?; + + { + let obj = unsafe { + reference.upgrade_borrowed_as_unchecked::() + }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = unsafe { + reference.upgrade_borrowed_as_unchecked::() + }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + inner(new_reference)?; + inner(new_proxy) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + call_retrievable: bool, + ) -> PyResult<()> { + let not_call_retrievable = !call_retrievable; + + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = create_reference(object.bind(py))?; + + assert!(not_call_retrievable || reference.call0()?.is(&object)); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(not_call_retrievable || reference.call0()?.is_none()); + assert!(reference.upgrade().is_none()); + + Ok(()) + }) + } + + inner(new_reference, true)?; + inner(new_proxy, false) + } + + #[test] + fn test_weakref_upgrade_borrowed() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + call_retrievable: bool, + ) -> PyResult<()> { + let not_call_retrievable = !call_retrievable; + + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = create_reference(object.bind(py))?; + + assert!(not_call_retrievable || reference.call0()?.is(&object)); + assert!(reference.upgrade_borrowed().is_some()); + assert!(reference + .upgrade_borrowed() + .map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(not_call_retrievable || reference.call0()?.is_none()); + assert!(reference.upgrade_borrowed().is_none()); + + Ok(()) + }) + } + + inner(new_reference, true)?; + inner(new_proxy, false) + } + + #[test] + fn test_weakref_get_object() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + call_retrievable: bool, + ) -> PyResult<()> { + let not_call_retrievable = !call_retrievable; + + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = create_reference(object.bind(py))?; + + assert!(not_call_retrievable || reference.call0()?.is(&object)); + assert!(reference.get_object().is(&object)); + + drop(object); + + assert!(not_call_retrievable || reference.call0()?.is(&reference.get_object())); + assert!(not_call_retrievable || reference.call0()?.is_none()); + assert!(reference.get_object().is_none()); + + Ok(()) + }) + } + + inner(new_reference, true)?; + inner(new_proxy, false) + } + + #[test] + fn test_weakref_get_object_borrowed() -> PyResult<()> { + fn inner( + create_reference: impl for<'py> FnOnce( + &Bound<'py, PyAny>, + ) + -> PyResult>, + call_retrievable: bool, + ) -> PyResult<()> { + let not_call_retrievable = !call_retrievable; + + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = create_reference(object.bind(py))?; + + assert!(not_call_retrievable || reference.call0()?.is(&object)); + assert!(reference.get_object_borrowed().is(&object)); + + drop(object); + + assert!(not_call_retrievable || reference.call0()?.is_none()); + assert!(reference.get_object_borrowed().is_none()); + + Ok(()) + }) + } + + inner(new_reference, true)?; + inner(new_proxy, false) + } + } +} diff --git a/src/types/weakref/mod.rs b/src/types/weakref/mod.rs index 3d4f5550cab..04cc3540bb7 100644 --- a/src/types/weakref/mod.rs +++ b/src/types/weakref/mod.rs @@ -1,7 +1,9 @@ +pub use anyref::PyWeakref; pub use callableproxy::PyWeakCallableProxy; pub use proxy::PyWeakProxy; pub use reference::{PyWeakRef, PyWeakRefMethods}; +pub(crate) mod anyref; pub(crate) mod callableproxy; pub(crate) mod proxy; pub(crate) mod reference; From da776ed9d8b1abadab9d5c6e47d497ec205dd62a Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:41:38 +0200 Subject: [PATCH 33/42] Reworked Proxy types into one (PyWeakrefProxy) --- src/types/mod.rs | 2 +- src/types/weakref/anyref.rs | 30 +- src/types/weakref/callableproxy.rs | 1197 --------------------- src/types/weakref/mod.rs | 4 +- src/types/weakref/proxy.rs | 1564 +++++++++++++++++++--------- 5 files changed, 1070 insertions(+), 1727 deletions(-) delete mode 100644 src/types/weakref/callableproxy.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index 31de19434f4..97fa53a03d0 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -48,7 +48,7 @@ pub use self::traceback::{PyTraceback, PyTracebackMethods}; pub use self::tuple::{PyTuple, PyTupleMethods}; pub use self::typeobject::{PyType, PyTypeMethods}; #[cfg(not(PyPy))] -pub use self::weakref::{PyWeakCallableProxy, PyWeakProxy, PyWeakRef, PyWeakRefMethods, PyWeakref}; +pub use self::weakref::{PyWeakRef, PyWeakRefMethods, PyWeakref, PyWeakrefProxy}; /// Iteration over Python collections. /// diff --git a/src/types/weakref/anyref.rs b/src/types/weakref/anyref.rs index 9f9f391b20a..50ae59ee70f 100644 --- a/src/types/weakref/anyref.rs +++ b/src/types/weakref/anyref.rs @@ -1,6 +1,8 @@ +use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; -use crate::types::PyAny; -use crate::{ffi, Borrowed, Bound, PyNativeType, PyResult, PyTypeCheck, PyTypeInfo}; +use crate::type_object::{PyTypeCheck, PyTypeInfo}; +use crate::types::any::PyAny; +use crate::{ffi, Borrowed, Bound, PyNativeType}; use super::PyWeakRefMethods; @@ -43,7 +45,7 @@ impl PyWeakref { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// use pyo3::types::{PyWeakref, PyWeakrefProxy}; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -68,7 +70,7 @@ impl PyWeakref { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let proxy = PyWeakProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. + /// let proxy = PyWeakrefProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. /// let reference = proxy.downcast::()?; /// /// assert_eq!( @@ -123,7 +125,7 @@ impl PyWeakref { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// use pyo3::types::{PyWeakref, PyWeakrefProxy}; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -148,7 +150,7 @@ impl PyWeakref { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let proxy = PyWeakProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. + /// let proxy = PyWeakrefProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. /// let reference = proxy.downcast::()?; /// /// assert_eq!( @@ -198,7 +200,7 @@ impl PyWeakref { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// use pyo3::types::{PyWeakref, PyWeakrefProxy}; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -223,7 +225,7 @@ impl PyWeakref { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let proxy = PyWeakProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. + /// let proxy = PyWeakrefProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. /// let reference = proxy.downcast::()?; /// /// assert_eq!( @@ -277,7 +279,7 @@ impl PyWeakref { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// use pyo3::types::{PyWeakref, PyWeakrefProxy}; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -293,7 +295,7 @@ impl PyWeakref { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let proxy = PyWeakProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. + /// let proxy = PyWeakrefProxy::new_bound(&data)?; // Retrieve this as an PyMethods argument. /// let reference = proxy.downcast::()?; /// /// assert_eq!( @@ -340,7 +342,7 @@ impl PyWeakref { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::{PyWeakref, PyWeakProxy}; + /// use pyo3::types::{PyWeakref, PyWeakrefProxy}; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -357,7 +359,7 @@ impl PyWeakref { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; - /// let proxy = PyWeakProxy::new_bound(&object)?; // Retrieve this as an PyMethods argument. + /// let proxy = PyWeakrefProxy::new_bound(&object)?; // Retrieve this as an PyMethods argument. /// let reference = proxy.downcast::()?; /// /// assert_eq!( @@ -396,7 +398,7 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakref> { #[cfg(test)] mod tests { use crate::types::any::{PyAny, PyAnyMethods}; - use crate::types::weakref::{PyWeakProxy, PyWeakRef, PyWeakRefMethods, PyWeakref}; + use crate::types::weakref::{PyWeakRef, PyWeakRefMethods, PyWeakref, PyWeakrefProxy}; use crate::{Bound, PyResult, Python}; fn new_reference<'py>(object: &Bound<'py, PyAny>) -> PyResult> { @@ -405,7 +407,7 @@ mod tests { } fn new_proxy<'py>(object: &Bound<'py, PyAny>) -> PyResult> { - let reference = PyWeakProxy::new_bound(object)?; + let reference = PyWeakrefProxy::new_bound(object)?; reference.into_any().downcast_into().map_err(Into::into) } diff --git a/src/types/weakref/callableproxy.rs b/src/types/weakref/callableproxy.rs deleted file mode 100644 index 743328e54c7..00000000000 --- a/src/types/weakref/callableproxy.rs +++ /dev/null @@ -1,1197 +0,0 @@ -use crate::err::PyResult; -use crate::ffi_ptr_ext::FfiPtrExt; -use crate::py_result_ext::PyResultExt; -use crate::type_object::{PyTypeCheck, PyTypeInfo}; -use crate::types::any::{PyAny, PyAnyMethods}; -use crate::{ffi, AsPyPointer, Borrowed, Bound, PyNativeType, ToPyObject}; - -use super::PyWeakRefMethods; - -/// Represents a Python `weakref.CallableProxyType`. -/// -/// In Python this is created by calling `weakref.proxy` with a callable argument as the referenced object. -/// -/// If the referenced object is non-callable [`PyWeakProxy`](super::PyWeakProxy) should be used instead. -#[repr(transparent)] -pub struct PyWeakCallableProxy(PyAny); - -pyobject_native_type!( - PyWeakCallableProxy, - ffi::PyWeakReference, - pyobject_native_static_type_object!(ffi::_PyWeakref_CallableProxyType), - #module=Some("weakref"), - #checkfunction=ffi::PyWeakref_CheckProxy -); - -impl PyWeakCallableProxy { - /// Deprecated form of [`PyWeakCallableProxy::new_bound`]. - #[inline] - #[track_caller] - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "`PyWeakCallableProxy::new` will be replaced by `PyWeakCallableProxy::new_bound` in a future PyO3 version" - ) - )] - pub fn new(object: &T) -> PyResult<&PyWeakCallableProxy> - where - T: PyNativeType, - { - Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) - } - - /// Constructs a new Weak callable Reference (`weakref.proxy`/`weakref.CallableProxyType`) for the given object. - /// - /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag). - /// The object should also be callable. For a non-callable weakref proxy see [`PyWeakProxy`](crate::types::weakref::PyWeakProxy). - /// - /// # Examples - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakCallableProxy; - /// use pyo3::exceptions::PyReferenceError; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn __call__(&self) -> &str { - /// "This class is callable" - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let foo = Bound::new(py, Foo {})?; - /// let weakref = PyWeakCallableProxy::new_bound(&foo)?; - /// assert!( - /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.upgrade() - /// .map_or(false, |obj| obj.is(&foo)) - /// ); - /// - /// let weakref2 = PyWeakCallableProxy::new_bound(&foo)?; - /// assert!(weakref.is(&weakref2)); - /// - /// assert_eq!(weakref.call0()?.to_string(), "This class is callable"); - /// - /// drop(foo); - /// - /// assert!(weakref.upgrade().is_none()); - /// assert!(weakref.call0() - /// .err() - /// .map_or(false, |err| err.is_instance_of::(py)) - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics if the provided object is not callable. - /// - #[inline] - #[track_caller] - pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { - // TODO: Must track caller be used here? - #[track_caller] - fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { - assert!( - object.is_callable(), - "An object to be referenced by a PyWeakCallableProxy should be callable. Use PyWeakProxy instead." - ); - - unsafe { - Bound::from_owned_ptr_or_err( - object.py(), - ffi::PyWeakref_NewProxy(object.as_ptr(), ffi::Py_None()), - ) - .downcast_into_unchecked() - } - } - - inner(object) - } - - /// Deprecated form of [`PyWeakCallableProxy::new_bound_with`]. - #[inline] - #[track_caller] - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "`PyWeakCallableProxy::new_with` will be replaced by `PyWeakCallableProxy::new_bound_with` in a future PyO3 version" - ) - )] - pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakCallableProxy> - where - T: PyNativeType, - C: ToPyObject, - { - Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) - } - - /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.CallableProxyType`) for the given object with a callback. - /// - /// 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. - /// The object should also be callable. For a non-callable weakref proxy see [`PyWeakProxy`](crate::types::weakref::PyWeakProxy). - /// - /// # Examples - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakCallableProxy; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn __call__(&self) -> &str { - /// "This class is callable" - /// } - /// } - /// - /// #[pyfunction] - /// fn callback(wref: Bound<'_, PyWeakCallableProxy>) -> PyResult<()> { - /// let py = wref.py(); - /// assert!(wref.upgrade_as::()?.is_none()); - /// py.run_bound("counter = 1", None, None) - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// py.run_bound("counter = 0", None, None)?; - /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); - /// let foo = Bound::new(py, Foo{})?; - /// - /// // This is fine. - /// let weakref = PyWeakCallableProxy::new_bound_with(&foo, py.None())?; - /// assert!(weakref.upgrade_as::()?.is_some()); - /// assert!( - /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` - /// weakref.upgrade() - /// .map_or(false, |obj| obj.is(&foo)) - /// ); - /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); - /// - /// let weakref2 = PyWeakCallableProxy::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?; - /// assert!(!weakref.is(&weakref2)); // Not the same weakref - /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object - /// - /// assert_eq!(weakref.call0()?.to_string(), "This class is callable"); - /// - /// drop(foo); - /// - /// assert!(weakref.upgrade_as::()?.is_none()); - /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 1); - /// Ok(()) - /// }) - /// # } - /// ``` - /// # Panics - /// This function panics if the provided object is not callable. - #[track_caller] - pub fn new_bound_with<'py, C>( - object: &Bound<'py, PyAny>, - callback: C, - ) -> PyResult> - where - C: ToPyObject, - { - // TODO: Must track caller be used here? - #[track_caller] - fn inner<'py>( - object: &Bound<'py, PyAny>, - callback: Bound<'py, PyAny>, - ) -> PyResult> { - assert!( - object.is_callable(), - "An object to be referenced by a PyWeakCallableProxy should be callable. Use PyWeakProxy instead." - ); - - unsafe { - Bound::from_owned_ptr_or_err( - object.py(), - ffi::PyWeakref_NewProxy(object.as_ptr(), callback.as_ptr()), - ) - .downcast_into_unchecked() - } - } - - let py = object.py(); - inner(object, callback.to_object(py).into_bound(py)) - } - - /// Upgrade the weakref to a direct object reference. - /// - /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakCallableProxy; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn __call__(&self) -> &str { - /// "This class is callable" - /// } - /// - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_as::()? { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// Ok(format!("Processing '{}': score = {}", name, score)) - /// } else { - /// Ok("The supplied data reference is nolonger relavent.".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "Processing 'Dave': score = 10" - /// ); - /// - /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType - /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] - pub fn upgrade_as(&self) -> PyResult> - where - T: PyTypeCheck, - { - Ok(self - .as_borrowed() - .upgrade_as::()? - .map(Bound::into_gil_ref)) - } - - /// 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`. - /// - /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Safety - /// Callers must ensure that the type is valid or risk type confusion. - /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakCallableProxy; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn __call__(&self) -> &str { - /// "This class is callable" - /// } - /// - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> String { - /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// format!("Processing '{}': score = {}", name, score) - /// } else { - /// "The supplied data reference is nolonger relavent.".to_owned() - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed()), - /// "Processing 'Dave': score = 10" - /// ); - /// - /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed()), - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType - /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] - pub unsafe fn upgrade_as_unchecked(&self) -> Option<&T::AsRefTarget> - where - T: PyTypeCheck, - { - self.as_borrowed() - .upgrade_as_unchecked::() - .map(Bound::into_gil_ref) - } - - /// Upgrade the weakref to an exact direct object reference. - /// - /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakCallableProxy; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn __call__(&self) -> &str { - /// "This class is callable" - /// } - /// - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_as_exact::()? { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// Ok(format!("Processing '{}': score = {}", name, score)) - /// } else { - /// Ok("The supplied data reference is nolonger relavent.".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "Processing 'Dave': score = 10" - /// ); - /// - /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType - /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] - pub fn upgrade_as_exact(&self) -> PyResult> - where - T: PyTypeInfo, - { - Ok(self - .as_borrowed() - .upgrade_as_exact::()? - .map(Bound::into_gil_ref)) - } - - /// Upgrade the weakref to a [`PyAny`] reference to the target if possible. - /// - /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. - /// - /// This function gets the optional target of this [`weakref.CallableProxyType`] (result of calling [`weakref.proxy`]). - /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakCallableProxy; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn __call__(&self) -> &str { - /// "This class is callable" - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { - /// if let Some(object) = reference.upgrade() { - /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) - /// } else { - /// Ok("The object, which this reference refered to, no longer exists".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The object 'Foo' refered by this reference still exists." - /// ); - /// - /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The object, which this reference refered to, no longer exists" - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ProxyCallableType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType - /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] - pub fn upgrade(&self) -> Option<&'_ PyAny> { - self.as_borrowed().upgrade().map(Bound::into_gil_ref) - } - - /// Retrieve to a object pointed to by the weakref. - /// - /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). - /// - /// This function gets the optional target of this [`weakref.CallableProxyType`] (result of calling [`weakref.proxy`]). - /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakCallableProxy; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn __call__(&self) -> &str { - /// "This class is callable" - /// } - /// } - /// - /// fn get_class(reference: Borrowed<'_, '_, PyWeakCallableProxy>) -> PyResult { - /// reference - /// .get_object() - /// .getattr("__class__")? - /// .repr()? - /// .to_str() - /// .map(ToOwned::to_owned) - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakCallableProxy::new_bound(&object)?; - /// - /// assert_eq!( - /// get_class(reference.as_borrowed())?, - /// "" - /// ); - /// - /// assert_eq!(reference.call0()?.to_string(), "This class is callable"); - /// - /// drop(object); - /// - /// assert_eq!(get_class(reference.as_borrowed())?, ""); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType - /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] - pub fn get_object(&self) -> &'_ PyAny { - self.as_borrowed().get_object().into_gil_ref() - } -} - -impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakCallableProxy> { - #[track_caller] - fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { - // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. - unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } - .expect("The 'weakref.CallableProxyType' instance should be valid (non-null and actually a weakref reference)") - } -} - -#[cfg(test)] -mod tests { - use crate::exceptions::{PyAttributeError, PyReferenceError}; - use crate::types::any::{PyAny, PyAnyMethods}; - use crate::types::weakref::{PyWeakCallableProxy, PyWeakRefMethods}; - use crate::{Bound, PyResult, Python}; - - #[cfg(all(not(Py_LIMITED_API), Py_3_10))] - const CLASS_NAME: &str = ""; - #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] - const CLASS_NAME: &str = ""; - - fn check_repr( - reference: &Bound<'_, PyWeakCallableProxy>, - object: &Bound<'_, PyAny>, - class: &str, - ) -> PyResult<()> { - let repr = reference.repr()?.to_string(); - let (first_part, second_part) = repr.split_once(" to ").unwrap(); - - { - let (msg, addr) = first_part.split_once("0x").unwrap(); - - assert_eq!(msg, ") -> PyResult> { - py.run_bound( - "class A:\n def __call__(self):\n return 'This class is callable!'\n", - None, - None, - )?; - py.eval_bound("A", None, None).downcast_into::() - } - - #[test] - fn test_weakref_proxy_behavior() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - assert!(!reference.is(&object)); - assert!(reference.get_object().is(&object)); - #[cfg(not(Py_LIMITED_API))] - assert_eq!(reference.get_type().to_string(), CLASS_NAME); - - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); - check_repr(&reference, &object, "A")?; - - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - - assert_eq!(reference.call0()?.to_string(), "This class is callable!"); - - drop(object); - - assert!(reference.get_object().is_none()); - assert!(reference - .getattr("__class__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, py.None().bind(py), "NoneType")?; - - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - - assert!(reference - .call0() - .err() - .map_or(false, |err| err.is_instance_of::(py) - & (err.value_bound(py).to_string() - == "weakly-referenced object no longer exists"))); - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade_as() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - { - // This test is a bit weird but ok. - let obj = reference.upgrade_as::(); - - assert!(obj.is_ok()); - let obj = obj.unwrap(); - - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() - && obj.is_exact_instance(&class))); - } - - drop(object); - - { - // This test is a bit weird but ok. - let obj = reference.upgrade_as::(); - - assert!(obj.is_ok()); - let obj = obj.unwrap(); - - assert!(obj.is_none()); - } - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - { - // This test is a bit weird but ok. - let obj = reference.upgrade_borrowed_as::(); - - assert!(obj.is_ok()); - let obj = obj.unwrap(); - - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() - && obj.is_exact_instance(&class))); - } - - drop(object); - - { - // This test is a bit weird but ok. - let obj = reference.upgrade_borrowed_as::(); - - assert!(obj.is_ok()); - let obj = obj.unwrap(); - - assert!(obj.is_none()); - } - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - { - // This test is a bit weird but ok. - let obj = unsafe { reference.upgrade_as_unchecked::() }; - - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() - && obj.is_exact_instance(&class))); - } - - drop(object); - - { - // This test is a bit weird but ok. - let obj = unsafe { reference.upgrade_as_unchecked::() }; - - assert!(obj.is_none()); - } - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - { - // This test is a bit weird but ok. - let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; - - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() - && obj.is_exact_instance(&class))); - } - - drop(object); - - { - // This test is a bit weird but ok. - let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; - - assert!(obj.is_none()); - } - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - assert!(reference.upgrade().is_some()); - assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); - - drop(object); - - assert!(reference.upgrade().is_none()); - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade_borrowed() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - assert!(reference.upgrade_borrowed().is_some()); - assert!(reference - .upgrade_borrowed() - .map_or(false, |obj| obj.is(&object))); - - drop(object); - - assert!(reference.upgrade_borrowed().is_none()); - - Ok(()) - }) - } - - #[test] - fn test_weakref_get_object() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - assert!(reference.get_object().is(&object)); - - drop(object); - - assert!(reference.get_object().is_none()); - - Ok(()) - }) - } - - #[test] - fn test_weakref_get_object_borrowed() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - assert!(reference.get_object_borrowed().is(&object)); - - drop(object); - - assert!(reference.get_object_borrowed().is_none()); - - Ok(()) - }) - } - } - - // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable. - #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))] - mod pyo3_pyclass { - use super::*; - use crate::{pyclass, pymethods, Py}; - - #[pyclass(weakref, crate = "crate")] - struct WeakrefablePyClass {} - - #[pymethods(crate = "crate")] - impl WeakrefablePyClass { - fn __call__(&self) -> &str { - "This class is callable!" - } - } - - #[test] - fn test_weakref_proxy_behavior() -> PyResult<()> { - Python::with_gil(|py| { - let object: Bound<'_, WeakrefablePyClass> = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(&object)?; - - assert!(!reference.is(&object)); - assert!(reference.get_object().is(&object)); - #[cfg(not(Py_LIMITED_API))] - assert_eq!(reference.get_type().to_string(), CLASS_NAME); - - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); - check_repr(&reference, object.as_any(), "builtins.WeakrefablePyClass")?; - - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - - assert_eq!(reference.call0()?.to_string(), "This class is callable!"); - - drop(object); - - assert!(reference.get_object().is_none()); - assert!(reference - .getattr("__class__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, py.None().bind(py), "NoneType")?; - - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - - assert!(reference - .call0() - .err() - .map_or(false, |err| err.is_instance_of::(py) - & (err.value_bound(py).to_string() - == "weakly-referenced object no longer exists"))); - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade_as() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - - { - let obj = reference.upgrade_as::(); - - assert!(obj.is_ok()); - let obj = obj.unwrap(); - - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } - - drop(object); - - { - let obj = reference.upgrade_as::(); - - assert!(obj.is_ok()); - let obj = obj.unwrap(); - - assert!(obj.is_none()); - } - - Ok(()) - }) - } - #[test] - fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - - { - let obj = reference.upgrade_borrowed_as::(); - - assert!(obj.is_ok()); - let obj = obj.unwrap(); - - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } - - drop(object); - - { - let obj = reference.upgrade_borrowed_as::(); - - assert!(obj.is_ok()); - let obj = obj.unwrap(); - - assert!(obj.is_none()); - } - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - - { - let obj = unsafe { reference.upgrade_as_unchecked::() }; - - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } - - drop(object); - - { - let obj = unsafe { reference.upgrade_as_unchecked::() }; - - assert!(obj.is_none()); - } - - Ok(()) - }) - } - #[test] - fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - - { - let obj = - unsafe { reference.upgrade_borrowed_as_unchecked::() }; - - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } - - drop(object); - - { - let obj = - unsafe { reference.upgrade_borrowed_as_unchecked::() }; - - assert!(obj.is_none()); - } - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - - assert!(reference.upgrade().is_some()); - assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); - - drop(object); - - assert!(reference.upgrade().is_none()); - - Ok(()) - }) - } - - #[test] - fn test_weakref_upgrade_borrowed() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - - assert!(reference.upgrade_borrowed().is_some()); - assert!(reference - .upgrade_borrowed() - .map_or(false, |obj| obj.is(&object))); - - drop(object); - - assert!(reference.upgrade_borrowed().is_none()); - - Ok(()) - }) - } - - #[test] - fn test_weakref_get_object() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - - assert!(reference.get_object().is(&object)); - - drop(object); - - assert!(reference.get_object().is_none()); - - Ok(()) - }) - } - - #[test] - fn test_weakref_get_object_borrowed() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakCallableProxy::new_bound(object.bind(py))?; - - assert!(reference.get_object_borrowed().is(&object)); - - drop(object); - - assert!(reference.get_object_borrowed().is_none()); - - Ok(()) - }) - } - } -} diff --git a/src/types/weakref/mod.rs b/src/types/weakref/mod.rs index 04cc3540bb7..dfc91bd38c7 100644 --- a/src/types/weakref/mod.rs +++ b/src/types/weakref/mod.rs @@ -1,9 +1,7 @@ pub use anyref::PyWeakref; -pub use callableproxy::PyWeakCallableProxy; -pub use proxy::PyWeakProxy; +pub use proxy::PyWeakrefProxy; pub use reference::{PyWeakRef, PyWeakRefMethods}; pub(crate) mod anyref; -pub(crate) mod callableproxy; pub(crate) mod proxy; pub(crate) mod reference; diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 6b33ab75ed7..48a1659f8a4 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -2,49 +2,54 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; -use crate::types::any::{PyAny, PyAnyMethods}; -use crate::{ffi, AsPyPointer, Borrowed, Bound, PyNativeType, ToPyObject}; +use crate::types::any::PyAny; +use crate::{ffi, Borrowed, Bound, PyNativeType, ToPyObject}; use super::PyWeakRefMethods; -/// Represents a Python `weakref.ProxyType`. +/// Represents any Python `weakref` Proxy type. /// -/// In Python this is created by calling `weakref.proxy` with a non-callable argument as the referenced object. -/// -/// If the referenced object is callable [`PyWeakCallableProxy`](super::PyWeakCallableProxy) should be used instead. +/// In Python this is created by calling `weakref.proxy`. +/// This is either a `weakref.ProxyType` or a `weakref.CallableProxyType` (`weakref.ProxyTypes`). #[repr(transparent)] -pub struct PyWeakProxy(PyAny); - -pyobject_native_type!( - PyWeakProxy, - ffi::PyWeakReference, - pyobject_native_static_type_object!(ffi::_PyWeakref_ProxyType), - #module=Some("weakref"), - #checkfunction=ffi::PyWeakref_CheckProxy -); - -impl PyWeakProxy { - /// Deprecated form of [`PyWeakProxy::new_bound`]. +pub struct PyWeakrefProxy(PyAny); + +pyobject_native_type_named!(PyWeakrefProxy); +pyobject_native_type_extract!(PyWeakrefProxy); + +// TODO: We known the layout but this cannot be implemented, due to the lack of public typeobject pointers. And it is 2 distinct types +// #[cfg(not(Py_LIMITED_API))] +// pyobject_native_type_sized!(PyWeakrefProxy, ffi::PyWeakReference); + +impl PyTypeCheck for PyWeakrefProxy { + const NAME: &'static str = "weakref.ProxyTypes"; + + fn type_check(object: &Bound<'_, PyAny>) -> bool { + unsafe { ffi::PyWeakref_CheckProxy(object.as_ptr()) > 0 } + } +} + +/// TODO: UPDATE DOCS +impl PyWeakrefProxy { + /// Deprecated form of [`PyWeakrefProxy::new_bound`]. #[inline] - #[track_caller] #[cfg_attr( not(feature = "gil-refs"), deprecated( since = "0.21.0", - note = "`PyWeakProxy::new` will be replaced by `PyWeakProxy::new_bound` in a future PyO3 version" + note = "`PyWeakrefProxy::new` will be replaced by `PyWeakrefProxy::new_bound` in a future PyO3 version" ) )] - pub fn new(object: &T) -> PyResult<&PyWeakProxy> + pub fn new(object: &T) -> PyResult<&PyWeakrefProxy> where T: PyNativeType, { Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) } - /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`) for the given object. + /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`/`weakref.CallableProxyType`) for the given object. /// /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag). - /// The object should also not be callable. For a callable weakref proxy see [`PyWeakCallableProxy`](crate::types::weakref::PyWeakCallableProxy). /// /// # Examples #[cfg_attr( @@ -56,7 +61,7 @@ impl PyWeakProxy { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakProxy; + /// use pyo3::types::PyWeakrefProxy; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -64,14 +69,14 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let foo = Bound::new(py, Foo {})?; - /// let weakref = PyWeakProxy::new_bound(&foo)?; + /// let weakref = PyWeakrefProxy::new_bound(&foo)?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.upgrade() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// - /// let weakref2 = PyWeakProxy::new_bound(&foo)?; + /// let weakref2 = PyWeakrefProxy::new_bound(&foo)?; /// assert!(weakref.is(&weakref2)); /// /// drop(foo); @@ -81,22 +86,10 @@ impl PyWeakProxy { /// }) /// # } /// ``` - /// - /// # Panics - /// This function panics if the provided object is callable. - /// #[inline] - #[track_caller] - pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { + pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { // TODO: Is this inner pattern still necessary Here? - // TODO: Must track caller be used here? - #[track_caller] - fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { - assert!( - !object.is_callable(), - "An object to be referenced by a PyWeakProxy should not be callable. Use PyWeakCallableProxy instead." - ); - + fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { unsafe { Bound::from_owned_ptr_or_err( object.py(), @@ -109,17 +102,16 @@ impl PyWeakProxy { inner(object) } - /// Deprecated form of [`PyWeakProxy::new_bound_with`]. + /// Deprecated form of [`PyWeakrefProxy::new_bound_with`]. #[inline] - #[track_caller] #[cfg_attr( not(feature = "gil-refs"), deprecated( since = "0.21.0", - note = "`PyWeakProxy::new_with` will be replaced by `PyWeakProxy::new_bound_with` in a future PyO3 version" + note = "`PyWeakrefProxy::new_with` will be replaced by `PyWeakrefProxy::new_bound_with` in a future PyO3 version" ) )] - pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakProxy> + pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakrefProxy> where T: PyNativeType, C: ToPyObject, @@ -127,10 +119,9 @@ impl PyWeakProxy { Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) } - /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`) for the given object with a callback. + /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`/`weakref.CallableProxyType`) for the given object with a callback. /// /// 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. - /// The object should also not be callable. For a callable weakref proxy see [`PyWeakCallableProxy`](crate::types::weakref::PyWeakCallableProxy). /// /// # Examples #[cfg_attr( @@ -142,13 +133,13 @@ impl PyWeakProxy { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakProxy; + /// use pyo3::types::PyWeakrefProxy; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } /// /// #[pyfunction] - /// fn callback(wref: Bound<'_, PyWeakProxy>) -> PyResult<()> { + /// fn callback(wref: Bound<'_, PyWeakrefProxy>) -> PyResult<()> { /// let py = wref.py(); /// assert!(wref.upgrade_as::()?.is_none()); /// py.run_bound("counter = 1", None, None) @@ -161,7 +152,7 @@ impl PyWeakProxy { /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. - /// let weakref = PyWeakProxy::new_bound_with(&foo, py.None())?; + /// let weakref = PyWeakrefProxy::new_bound_with(&foo, py.None())?; /// assert!(weakref.upgrade_as::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` @@ -170,7 +161,7 @@ impl PyWeakProxy { /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakProxy::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?; + /// let weakref2 = PyWeakrefProxy::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// @@ -182,27 +173,18 @@ impl PyWeakProxy { /// }) /// # } /// ``` - /// # Panics - /// This function panics if the provided object is callable. - #[track_caller] + #[inline] pub fn new_bound_with<'py, C>( object: &Bound<'py, PyAny>, callback: C, - ) -> PyResult> + ) -> PyResult> where C: ToPyObject, { - // TODO: Must track caller be used here? - #[track_caller] fn inner<'py>( object: &Bound<'py, PyAny>, callback: Bound<'py, PyAny>, - ) -> PyResult> { - assert!( - !object.is_callable(), - "An object to be referenced by a PyWeakProxy should not be callable. Use PyWeakCallableProxy instead." - ); - + ) -> PyResult> { unsafe { Bound::from_owned_ptr_or_err( object.py(), @@ -231,7 +213,7 @@ impl PyWeakProxy { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakProxy; + /// use pyo3::types::PyWeakrefProxy; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -243,7 +225,7 @@ impl PyWeakProxy { /// } /// } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> PyResult { /// if let Some(data_src) = reference.upgrade_as::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); @@ -256,7 +238,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(&data)?; + /// let reference = PyWeakrefProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -312,7 +294,7 @@ impl PyWeakProxy { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakProxy; + /// use pyo3::types::PyWeakrefProxy; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -324,7 +306,7 @@ impl PyWeakProxy { /// } /// } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> String { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> String { /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); @@ -337,7 +319,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(&data)?; + /// let reference = PyWeakrefProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed()), @@ -388,7 +370,7 @@ impl PyWeakProxy { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakProxy; + /// use pyo3::types::PyWeakrefProxy; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -400,7 +382,7 @@ impl PyWeakProxy { /// } /// } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> PyResult { /// if let Some(data_src) = reference.upgrade_as_exact::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); @@ -413,7 +395,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(&data)?; + /// let reference = PyWeakrefProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -455,7 +437,7 @@ impl PyWeakProxy { /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). /// This function returns `Some(&'py PyAny)` if the reference still exists, otherwise `None` will be returned. /// - /// This function gets the optional target of this [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). + /// This function gets the optional target of this [`weakref.ProxyType`] (or [`weakref.CallableProxyType`], result of calling [`weakref.proxy`]). /// It produces similair results using [`PyWeakref_GetObject`] in the C api. /// /// # Example @@ -468,12 +450,12 @@ impl PyWeakProxy { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakProxy; + /// use pyo3::types::PyWeakrefProxy; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> PyResult { /// if let Some(object) = reference.upgrade() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { @@ -484,7 +466,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(&data)?; + /// let reference = PyWeakrefProxy::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -509,6 +491,7 @@ impl PyWeakProxy { /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType + /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy #[track_caller] pub fn upgrade(&self) -> Option<&'_ PyAny> { @@ -519,7 +502,7 @@ impl PyWeakProxy { /// /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). /// - /// This function gets the optional target of this [`weakref.ProxyType`] (result of calling [`weakref.proxy`]). + /// This function gets the optional target of this [`weakref.ProxyType`] (or [`weakref.CallableProxyType`], result of calling [`weakref.proxy`]). /// It produces similair results using [`PyWeakref_GetObject`] in the C api. /// /// # Example @@ -532,12 +515,12 @@ impl PyWeakProxy { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakProxy; + /// use pyo3::types::PyWeakrefProxy; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } /// - /// fn get_class(reference: Borrowed<'_, '_, PyWeakProxy>) -> PyResult { + /// fn get_class(reference: Borrowed<'_, '_, PyWeakrefProxy>) -> PyResult { /// reference /// .get_object() /// .getattr("__class__")? @@ -549,7 +532,7 @@ impl PyWeakProxy { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakProxy::new_bound(&object)?; + /// let reference = PyWeakrefProxy::new_bound(&object)?; /// /// assert_eq!( /// get_class(reference.as_borrowed())?, @@ -571,6 +554,7 @@ impl PyWeakProxy { /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType + /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy #[track_caller] pub fn get_object(&self) -> &'_ PyAny { @@ -578,12 +562,12 @@ impl PyWeakProxy { } } -impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { +impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakrefProxy> { #[track_caller] fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } - .expect("The 'weakref.ProxyType' instance should be valid (non-null and actually a weakref reference)") + .expect("The 'weakref.ProxyType' (or `weakref.CallableProxyType`) instance should be valid (non-null and actually a weakref reference)") } } @@ -591,564 +575,1120 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakProxy> { mod tests { use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError}; use crate::types::any::{PyAny, PyAnyMethods}; - use crate::types::weakref::{PyWeakProxy, PyWeakRefMethods}; + use crate::types::weakref::{PyWeakRefMethods, PyWeakrefProxy}; use crate::{Bound, PyResult, Python}; - #[cfg(all(not(Py_LIMITED_API), Py_3_10))] - const CLASS_NAME: &str = "'weakref.ProxyType'"; - #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] - const CLASS_NAME: &str = "'weakproxy'"; + mod proxy { + use super::*; - fn check_repr( - reference: &Bound<'_, PyWeakProxy>, - object: &Bound<'_, PyAny>, - class: &str, - ) -> PyResult<()> { - let repr = reference.repr()?.to_string(); - let (first_part, second_part) = repr.split_once(" to ").unwrap(); + #[cfg(all(not(Py_LIMITED_API), Py_3_10))] + const CLASS_NAME: &str = "'weakref.ProxyType'"; + #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] + const CLASS_NAME: &str = "'weakproxy'"; + + fn check_repr( + reference: &Bound<'_, PyWeakrefProxy>, + object: &Bound<'_, PyAny>, + class: &str, + ) -> PyResult<()> { + let repr = reference.repr()?.to_string(); + let (first_part, second_part) = repr.split_once(" to ").unwrap(); + + { + let (msg, addr) = first_part.split_once("0x").unwrap(); + + assert_eq!(msg, ") -> PyResult> { + py.run_bound("class A:\n pass\n", None, None)?; + py.eval_bound("A", None, None).downcast_into::() + } - Ok(()) - } + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; - mod python_class { - use super::*; - use crate::{py_result_ext::PyResultExt, types::PyType}; + assert!(!reference.is(&object)); + assert!(reference.get_object().is(&object)); + + #[cfg(not(Py_LIMITED_API))] + assert_eq!( + reference.get_type().to_string(), + format!("", CLASS_NAME) + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + check_repr(&reference, &object, "A")?; + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| { + let result = err.is_instance_of::(py); + #[cfg(not(Py_LIMITED_API))] + let result = result + & (err.value_bound(py).to_string() + == format!("{} object is not callable", CLASS_NAME)); + result + })); + + drop(object); + + assert!(reference.get_object().is_none()); + assert!(reference + .getattr("__class__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + check_repr(&reference, py.None().bind(py), "NoneType")?; + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| { + let result = err.is_instance_of::(py); + #[cfg(not(Py_LIMITED_API))] + let result = result + & (err.value_bound(py).to_string() + == format!("{} object is not callable", CLASS_NAME)); + result + })); + + Ok(()) + }) + } - fn get_type(py: Python<'_>) -> PyResult> { - py.run_bound("class A:\n pass\n", None, None)?; - py.eval_bound("A", None, None).downcast_into::() + #[test] + fn test_weakref_upgrade_as() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = reference.upgrade_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = reference.upgrade_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = reference.upgrade_borrowed_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = reference.upgrade_borrowed_as::(); + + assert!(obj.is_ok()); + let obj = obj.unwrap(); + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.upgrade().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + assert!(reference.upgrade_borrowed().is_some()); + assert!(reference + .upgrade_borrowed() + .map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.upgrade_borrowed().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + assert!(reference.get_object().is(&object)); + + drop(object); + + assert!(reference.get_object().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object_borrowed() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + assert!(reference.get_object_borrowed().is(&object)); + + drop(object); + + assert!(reference.get_object_borrowed().is_none()); + + Ok(()) + }) + } } - #[test] - fn test_weakref_proxy_behavior() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; - - assert!(!reference.is(&object)); - assert!(reference.get_object().is(&object)); - - #[cfg(not(Py_LIMITED_API))] - assert_eq!( - reference.get_type().to_string(), - format!("", CLASS_NAME) - ); - - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); - check_repr(&reference, &object, "A")?; - - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - - assert!(reference.call0().err().map_or(false, |err| { - let result = err.is_instance_of::(py); - #[cfg(not(Py_LIMITED_API))] - let result = result - & (err.value_bound(py).to_string() - == format!("{} object is not callable", CLASS_NAME)); - result - })); - - drop(object); - - assert!(reference.get_object().is_none()); - assert!(reference - .getattr("__class__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, py.None().bind(py), "NoneType")?; - - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - - assert!(reference.call0().err().map_or(false, |err| { - let result = err.is_instance_of::(py); + // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable. + #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))] + mod pyo3_pyclass { + use super::*; + use crate::{pyclass, Py}; + + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} + + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let object: Bound<'_, WeakrefablePyClass> = + Bound::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + assert!(!reference.is(&object)); + assert!(reference.get_object().is(&object)); #[cfg(not(Py_LIMITED_API))] - let result = result - & (err.value_bound(py).to_string() - == format!("{} object is not callable", CLASS_NAME)); - result - })); - - Ok(()) - }) - } + assert_eq!( + reference.get_type().to_string(), + format!("", CLASS_NAME) + ); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + check_repr(&reference, object.as_any(), "builtins.WeakrefablePyClass")?; + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| { + let result = err.is_instance_of::(py); + #[cfg(not(Py_LIMITED_API))] + let result = result + & (err.value_bound(py).to_string() + == format!("{} object is not callable", CLASS_NAME)); + result + })); + + drop(object); + + assert!(reference.get_object().is_none()); + assert!(reference + .getattr("__class__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + check_repr(&reference, py.None().bind(py), "NoneType")?; + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference.call0().err().map_or(false, |err| { + let result = err.is_instance_of::(py); + #[cfg(not(Py_LIMITED_API))] + let result = result + & (err.value_bound(py).to_string() + == format!("{} object is not callable", CLASS_NAME)); + result + })); + + Ok(()) + }) + } - #[test] - fn test_weakref_upgrade_as() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; + #[test] + fn test_weakref_upgrade_as() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - { - // This test is a bit weird but ok. - let obj = reference.upgrade_as::(); + { + let obj = reference.upgrade_as::(); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() - && obj.is_exact_instance(&class))); - } + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } - drop(object); + drop(object); - { - // This test is a bit weird but ok. - let obj = reference.upgrade_as::(); + { + let obj = reference.upgrade_as::(); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(obj.is_none()); - } + assert!(obj.is_none()); + } - Ok(()) - }) - } + Ok(()) + }) + } - #[test] - fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; + #[test] + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - { - // This test is a bit weird but ok. - let obj = reference.upgrade_borrowed_as::(); + { + let obj = reference.upgrade_borrowed_as::(); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() - && obj.is_exact_instance(&class))); - } + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } - drop(object); + drop(object); - { - // This test is a bit weird but ok. - let obj = reference.upgrade_borrowed_as::(); + { + let obj = reference.upgrade_borrowed_as::(); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(obj.is_none()); - } + assert!(obj.is_none()); + } - Ok(()) - }) - } + Ok(()) + }) + } - #[test] - fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - { - // This test is a bit weird but ok. - let obj = unsafe { reference.upgrade_as_unchecked::() }; + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() - && obj.is_exact_instance(&class))); - } + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } - drop(object); + drop(object); - { - // This test is a bit weird but ok. - let obj = unsafe { reference.upgrade_as_unchecked::() }; + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; - assert!(obj.is_none()); - } + assert!(obj.is_none()); + } - Ok(()) - }) - } + Ok(()) + }) + } - #[test] - fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - { - // This test is a bit weird but ok. - let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + { + let obj = unsafe { + reference.upgrade_borrowed_as_unchecked::() + }; - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() - && obj.is_exact_instance(&class))); - } + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } - drop(object); + drop(object); - { - // This test is a bit weird but ok. - let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + { + let obj = unsafe { + reference.upgrade_borrowed_as_unchecked::() + }; - assert!(obj.is_none()); - } + assert!(obj.is_none()); + } - Ok(()) - }) - } + Ok(()) + }) + } - #[test] - fn test_weakref_upgrade() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - assert!(reference.upgrade().is_some()); - assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); - drop(object); + drop(object); - assert!(reference.upgrade().is_none()); + assert!(reference.upgrade().is_none()); - Ok(()) - }) - } + Ok(()) + }) + } - #[test] - fn test_weakref_upgrade_borrowed() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; + #[test] + fn test_weakref_upgrade_borrowed() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - assert!(reference.upgrade_borrowed().is_some()); - assert!(reference - .upgrade_borrowed() - .map_or(false, |obj| obj.is(&object))); + assert!(reference.upgrade_borrowed().is_some()); + assert!(reference + .upgrade_borrowed() + .map_or(false, |obj| obj.is(&object))); - drop(object); + drop(object); - assert!(reference.upgrade_borrowed().is_none()); + assert!(reference.upgrade_borrowed().is_none()); - Ok(()) - }) - } + Ok(()) + }) + } - #[test] - fn test_weakref_get_object() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - assert!(reference.get_object().is(&object)); + assert!(reference.get_object().is(&object)); - drop(object); + drop(object); - assert!(reference.get_object().is_none()); + assert!(reference.get_object().is_none()); - Ok(()) - }) - } + Ok(()) + }) + } - #[test] - fn test_weakref_get_object_borrowed() -> PyResult<()> { - Python::with_gil(|py| { - let class = get_type(py)?; - let object = class.call0()?; - let reference = PyWeakProxy::new_bound(&object)?; + #[test] + fn test_weakref_get_object_borrowed() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - assert!(reference.get_object_borrowed().is(&object)); + assert!(reference.get_object_borrowed().is(&object)); - drop(object); + drop(object); - assert!(reference.get_object_borrowed().is_none()); + assert!(reference.get_object_borrowed().is_none()); - Ok(()) - }) + Ok(()) + }) + } } } - // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable. - #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))] - mod pyo3_pyclass { + mod callable_proxy { use super::*; - use crate::{pyclass, Py}; - - #[pyclass(weakref, crate = "crate")] - struct WeakrefablePyClass {} - - #[test] - fn test_weakref_proxy_behavior() -> PyResult<()> { - Python::with_gil(|py| { - let object: Bound<'_, WeakrefablePyClass> = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(&object)?; - - assert!(!reference.is(&object)); - assert!(reference.get_object().is(&object)); - #[cfg(not(Py_LIMITED_API))] - assert_eq!( - reference.get_type().to_string(), - format!("", CLASS_NAME) - ); - - assert_eq!( - reference.getattr("__class__")?.to_string(), - "" - ); - check_repr(&reference, object.as_any(), "builtins.WeakrefablePyClass")?; - - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - - assert!(reference.call0().err().map_or(false, |err| { - let result = err.is_instance_of::(py); - #[cfg(not(Py_LIMITED_API))] - let result = result - & (err.value_bound(py).to_string() - == format!("{} object is not callable", CLASS_NAME)); - result - })); - - drop(object); - - assert!(reference.get_object().is_none()); - assert!(reference - .getattr("__class__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, py.None().bind(py), "NoneType")?; - - assert!(reference - .getattr("__callback__") - .err() - .map_or(false, |err| err.is_instance_of::(py))); - - assert!(reference.call0().err().map_or(false, |err| { - let result = err.is_instance_of::(py); - #[cfg(not(Py_LIMITED_API))] - let result = result - & (err.value_bound(py).to_string() - == format!("{} object is not callable", CLASS_NAME)); - result - })); - - Ok(()) - }) + + #[cfg(all(not(Py_LIMITED_API), Py_3_10))] + const CLASS_NAME: &str = ""; + #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] + const CLASS_NAME: &str = ""; + + fn check_repr( + reference: &Bound<'_, PyWeakrefProxy>, + object: &Bound<'_, PyAny>, + class: &str, + ) -> PyResult<()> { + let repr = reference.repr()?.to_string(); + let (first_part, second_part) = repr.split_once(" to ").unwrap(); + + { + let (msg, addr) = first_part.split_once("0x").unwrap(); + + assert_eq!(msg, " PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(object.bind(py))?; + mod python_class { + use super::*; + use crate::{py_result_ext::PyResultExt, types::PyType}; + + fn get_type(py: Python<'_>) -> PyResult> { + py.run_bound( + "class A:\n def __call__(self):\n return 'This class is callable!'\n", + None, + None, + )?; + py.eval_bound("A", None, None).downcast_into::() + } - { - let obj = reference.upgrade_as::(); + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(!reference.is(&object)); + assert!(reference.get_object().is(&object)); + #[cfg(not(Py_LIMITED_API))] + assert_eq!(reference.get_type().to_string(), CLASS_NAME); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + check_repr(&reference, &object, "A")?; + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert_eq!(reference.call0()?.to_string(), "This class is callable!"); + + drop(object); + + assert!(reference.get_object().is_none()); + assert!(reference + .getattr("__class__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + check_repr(&reference, py.None().bind(py), "NoneType")?; + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference + .call0() + .err() + .map_or(false, |err| err.is_instance_of::(py) + & (err.value_bound(py).to_string() + == "weakly-referenced object no longer exists"))); + + Ok(()) + }) + } - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } + #[test] + fn test_weakref_upgrade_as() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; - drop(object); + { + // This test is a bit weird but ok. + let obj = reference.upgrade_as::(); - { - let obj = reference.upgrade_as::(); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } - assert!(obj.is_none()); - } + drop(object); - Ok(()) - }) - } + { + // This test is a bit weird but ok. + let obj = reference.upgrade_as::(); - #[test] - fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(object.bind(py))?; + assert!(obj.is_ok()); + let obj = obj.unwrap(); - { - let obj = reference.upgrade_borrowed_as::(); + assert!(obj.is_none()); + } - assert!(obj.is_ok()); - let obj = obj.unwrap(); + Ok(()) + }) + } - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } + #[test] + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; - drop(object); + { + // This test is a bit weird but ok. + let obj = reference.upgrade_borrowed_as::(); - { - let obj = reference.upgrade_borrowed_as::(); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(obj.is_ok()); - let obj = obj.unwrap(); + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } - assert!(obj.is_none()); - } + drop(object); - Ok(()) - }) - } + { + // This test is a bit weird but ok. + let obj = reference.upgrade_borrowed_as::(); - #[test] - fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(object.bind(py))?; + assert!(obj.is_ok()); + let obj = obj.unwrap(); - { - let obj = unsafe { reference.upgrade_as_unchecked::() }; + assert!(obj.is_none()); + } - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); - } + Ok(()) + }) + } - drop(object); + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; - { - let obj = unsafe { reference.upgrade_as_unchecked::() }; + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; - assert!(obj.is_none()); - } + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr() + && obj.is_exact_instance(&class))); + } + + drop(object); + + { + // This test is a bit weird but ok. + let obj = unsafe { reference.upgrade_borrowed_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.upgrade().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_borrowed() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + assert!(reference.upgrade_borrowed().is_some()); + assert!(reference + .upgrade_borrowed() + .map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.upgrade_borrowed().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; - Ok(()) - }) + assert!(reference.get_object().is(&object)); + + drop(object); + + assert!(reference.get_object().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object_borrowed() -> PyResult<()> { + Python::with_gil(|py| { + let class = get_type(py)?; + let object = class.call0()?; + let reference = PyWeakrefProxy::new_bound(&object)?; + + assert!(reference.get_object_borrowed().is(&object)); + + drop(object); + + assert!(reference.get_object_borrowed().is_none()); + + Ok(()) + }) + } } - #[test] - fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(object.bind(py))?; + // under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable. + #[cfg(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))))] + mod pyo3_pyclass { + use super::*; + use crate::{pyclass, pymethods, Py}; - { - let obj = - unsafe { reference.upgrade_borrowed_as_unchecked::() }; + #[pyclass(weakref, crate = "crate")] + struct WeakrefablePyClass {} - assert!(obj.is_some()); - assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + #[pymethods(crate = "crate")] + impl WeakrefablePyClass { + fn __call__(&self) -> &str { + "This class is callable!" } + } - drop(object); + #[test] + fn test_weakref_proxy_behavior() -> PyResult<()> { + Python::with_gil(|py| { + let object: Bound<'_, WeakrefablePyClass> = + Bound::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(&object)?; - { - let obj = - unsafe { reference.upgrade_borrowed_as_unchecked::() }; + assert!(!reference.is(&object)); + assert!(reference.get_object().is(&object)); + #[cfg(not(Py_LIMITED_API))] + assert_eq!(reference.get_type().to_string(), CLASS_NAME); + + assert_eq!( + reference.getattr("__class__")?.to_string(), + "" + ); + check_repr(&reference, object.as_any(), "builtins.WeakrefablePyClass")?; + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert_eq!(reference.call0()?.to_string(), "This class is callable!"); + + drop(object); + + assert!(reference.get_object().is_none()); + assert!(reference + .getattr("__class__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + check_repr(&reference, py.None().bind(py), "NoneType")?; + + assert!(reference + .getattr("__callback__") + .err() + .map_or(false, |err| err.is_instance_of::(py))); + + assert!(reference + .call0() + .err() + .map_or(false, |err| err.is_instance_of::(py) + & (err.value_bound(py).to_string() + == "weakly-referenced object no longer exists"))); + + Ok(()) + }) + } - assert!(obj.is_none()); - } + #[test] + fn test_weakref_upgrade_as() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - Ok(()) - }) - } + { + let obj = reference.upgrade_as::(); - #[test] - fn test_weakref_upgrade() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(object.bind(py))?; + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(reference.upgrade().is_some()); - assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } - drop(object); + drop(object); - assert!(reference.upgrade().is_none()); + { + let obj = reference.upgrade_as::(); - Ok(()) - }) - } + assert!(obj.is_ok()); + let obj = obj.unwrap(); - #[test] - fn test_weakref_upgrade_borrowed() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(object.bind(py))?; + assert!(obj.is_none()); + } - assert!(reference.upgrade_borrowed().is_some()); - assert!(reference - .upgrade_borrowed() - .map_or(false, |obj| obj.is(&object))); + Ok(()) + }) + } + #[test] + fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - drop(object); + { + let obj = reference.upgrade_borrowed_as::(); - assert!(reference.upgrade_borrowed().is_none()); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - Ok(()) - }) - } + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } - #[test] - fn test_weakref_get_object() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(object.bind(py))?; + drop(object); - assert!(reference.get_object().is(&object)); + { + let obj = reference.upgrade_borrowed_as::(); - drop(object); + assert!(obj.is_ok()); + let obj = obj.unwrap(); - assert!(reference.get_object().is_none()); + assert!(obj.is_none()); + } - Ok(()) - }) - } + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = unsafe { reference.upgrade_as_unchecked::() }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + #[test] + fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; + + { + let obj = unsafe { + reference.upgrade_borrowed_as_unchecked::() + }; + + assert!(obj.is_some()); + assert!(obj.map_or(false, |obj| obj.as_ptr() == object.as_ptr())); + } + + drop(object); + + { + let obj = unsafe { + reference.upgrade_borrowed_as_unchecked::() + }; + + assert!(obj.is_none()); + } + + Ok(()) + }) + } + + #[test] + fn test_weakref_upgrade() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; + + assert!(reference.upgrade().is_some()); + assert!(reference.upgrade().map_or(false, |obj| obj.is(&object))); + + drop(object); + + assert!(reference.upgrade().is_none()); + + Ok(()) + }) + } - #[test] - fn test_weakref_get_object_borrowed() -> PyResult<()> { - Python::with_gil(|py| { - let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakProxy::new_bound(object.bind(py))?; + #[test] + fn test_weakref_upgrade_borrowed() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; - assert!(reference.get_object_borrowed().is(&object)); + assert!(reference.upgrade_borrowed().is_some()); + assert!(reference + .upgrade_borrowed() + .map_or(false, |obj| obj.is(&object))); - drop(object); + drop(object); - assert!(reference.get_object_borrowed().is_none()); + assert!(reference.upgrade_borrowed().is_none()); - Ok(()) - }) + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; + + assert!(reference.get_object().is(&object)); + + drop(object); + + assert!(reference.get_object().is_none()); + + Ok(()) + }) + } + + #[test] + fn test_weakref_get_object_borrowed() -> PyResult<()> { + Python::with_gil(|py| { + let object = Py::new(py, WeakrefablePyClass {})?; + let reference = PyWeakrefProxy::new_bound(object.bind(py))?; + + assert!(reference.get_object_borrowed().is(&object)); + + drop(object); + + assert!(reference.get_object_borrowed().is_none()); + + Ok(()) + }) + } } } } From d1bf16cf2811344468dec8064040a6580b14e672 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:59:13 +0200 Subject: [PATCH 34/42] Rename PyWeakRef to PyWeakrefReference --- src/prelude.rs | 2 +- src/types/mod.rs | 2 +- src/types/weakref/anyref.rs | 760 ++++++++++++++++++++++++++++- src/types/weakref/mod.rs | 4 +- src/types/weakref/proxy.rs | 6 +- src/types/weakref/reference.rs | 866 +++------------------------------ 6 files changed, 820 insertions(+), 820 deletions(-) diff --git a/src/prelude.rs b/src/prelude.rs index ea405d3d527..6fcd911e973 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -47,4 +47,4 @@ pub use crate::types::traceback::PyTracebackMethods; pub use crate::types::tuple::PyTupleMethods; pub use crate::types::typeobject::PyTypeMethods; #[cfg(not(PyPy))] -pub use crate::types::weakref::PyWeakRefMethods; +pub use crate::types::weakref::PyWeakrefMethods; diff --git a/src/types/mod.rs b/src/types/mod.rs index 97fa53a03d0..322a82e191d 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -48,7 +48,7 @@ pub use self::traceback::{PyTraceback, PyTracebackMethods}; pub use self::tuple::{PyTuple, PyTupleMethods}; pub use self::typeobject::{PyType, PyTypeMethods}; #[cfg(not(PyPy))] -pub use self::weakref::{PyWeakRef, PyWeakRefMethods, PyWeakref, PyWeakrefProxy}; +pub use self::weakref::{PyWeakref, PyWeakrefMethods, PyWeakrefProxy, PyWeakrefReference}; /// Iteration over Python collections. /// diff --git a/src/types/weakref/anyref.rs b/src/types/weakref/anyref.rs index 50ae59ee70f..863e8c2f537 100644 --- a/src/types/weakref/anyref.rs +++ b/src/types/weakref/anyref.rs @@ -1,11 +1,9 @@ -use crate::err::PyResult; +use crate::err::{DowncastError, PyResult}; use crate::ffi_ptr_ext::FfiPtrExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; -use crate::types::any::PyAny; +use crate::types::any::{PyAny, PyAnyMethods}; use crate::{ffi, Borrowed, Bound, PyNativeType}; -use super::PyWeakRefMethods; - /// Represents any Python `weakref` reference. /// /// In Python this is created by calling `weakref.ref` or `weakref.proxy`. @@ -387,7 +385,755 @@ impl PyWeakref { } } -impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakref> { +/// Implementation of functionality for [`PyWeakref`]. +/// +/// These methods are defined for the `Bound<'py, PyWeakref>` smart pointer, so to use method call +/// syntax these methods are separated into a trait, because stable Rust does not yet support +/// `arbitrary_self_types`. +#[doc(alias = "PyWeakref")] +pub trait PyWeakrefMethods<'py> { + /// Upgrade the weakref to a direct Bound object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_as::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + fn upgrade_as(&self) -> PyResult>> + where + T: PyTypeCheck, + { + self.upgrade() + .map(Bound::downcast_into::) + .transpose() + .map_err(Into::into) + } + + /// Upgrade the weakref to a Borrowed object reference. + /// + /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_borrowed_as::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? + #[track_caller] + fn upgrade_borrowed_as<'a, T>(&'a self) -> PyResult>> + where + T: PyTypeCheck, + 'py: 'a, + { + // TODO: Replace when Borrowed::downcast exists + match self.upgrade_borrowed() { + None => Ok(None), + Some(object) if T::type_check(&object) => { + Ok(Some(unsafe { object.downcast_unchecked() })) + } + Some(object) => Err(DowncastError::new(&object, T::NAME).into()), + } + } + + /// 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`. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Safety + /// Callers must ensure that the type is valid or risk type confusion. + /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> String { + /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// format!("Processing '{}': score = {}", name, score) + /// } else { + /// "The supplied data reference is nolonger relavent.".to_owned() + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + unsafe fn upgrade_as_unchecked(&self) -> Option> { + Some(self.upgrade()?.downcast_into_unchecked()) + } + + /// Upgrade the weakref to a Borrowed 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`. + /// + /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Safety + /// Callers must ensure that the type is valid or risk type confusion. + /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> String { + /// if let Some(data_src) = unsafe { reference.upgrade_borrowed_as_unchecked::() } { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// format!("Processing '{}': score = {}", name, score) + /// } else { + /// "The supplied data reference is nolonger relavent.".to_owned() + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed()), + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? + #[track_caller] + unsafe fn upgrade_borrowed_as_unchecked<'a, T>(&'a self) -> Option> + where + 'py: 'a, + { + Some(self.upgrade_borrowed()?.downcast_unchecked()) + } + + /// Upgrade the weakref to a exact direct Bound object reference. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_as_exact::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + fn upgrade_as_exact(&self) -> PyResult>> + where + T: PyTypeInfo, + { + self.upgrade() + .map(Bound::downcast_into_exact) + .transpose() + .map_err(Into::into) + } + + /// Upgrade the weakref to a exact Borrowed object reference. + /// + /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// In Python it would be equivalent to [`PyWeakref_GetObject`]. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// #[pymethods] + /// impl Foo { + /// fn get_data(&self) -> (&str, u32) { + /// ("Dave", 10) + /// } + /// } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { + /// if let Some(data_src) = reference.upgrade_borrowed_as_exact::()? { + /// let data = data_src.borrow(); + /// let (name, score) = data.get_data(); + /// Ok(format!("Processing '{}': score = {}", name, score)) + /// } else { + /// Ok("The supplied data reference is nolonger relavent.".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "Processing 'Dave': score = 10" + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The supplied data reference is nolonger relavent." + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? + #[track_caller] + fn upgrade_borrowed_as_exact<'a, T>(&'a self) -> PyResult>> + where + T: PyTypeInfo, + 'py: 'a, + { + // TODO: Replace when Borrowed::downcast_exact exists + match self.upgrade_borrowed() { + None => Ok(None), + Some(object) if object.is_exact_instance_of::() => { + Ok(Some(unsafe { object.downcast_unchecked() })) + } + Some(object) => Err(DowncastError::new(&object, T::NAME).into()), + } + } + + /// Upgrade the weakref to a Bound [`PyAny`] reference to the target object if possible. + /// + /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// This function returns `Some(Bound<'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { + /// if let Some(object) = reference.upgrade() { + /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) + /// } else { + /// Ok("The object, which this reference refered to, no longer exists".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object 'Foo' refered by this reference still exists." + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object, which this reference refered to, no longer exists" + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + fn upgrade(&self) -> Option> { + let object = self.get_object(); + + if object.is_none() { + None + } else { + Some(object) + } + } + + /// Upgrade the weakref to a Borrowed [`PyAny`] reference to the target object if possible. + /// + /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). + /// This function returns `Some(Borrowed<'_, 'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { + /// if let Some(object) = reference.upgrade_borrowed() { + /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) + /// } else { + /// Ok("The object, which this reference refered to, no longer exists".to_owned()) + /// } + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let data = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&data)?; + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object 'Foo' refered by this reference still exists." + /// ); + /// + /// drop(data); + /// + /// assert_eq!( + /// parse_data(reference.as_borrowed())?, + /// "The object, which this reference refered to, no longer exists" + /// ); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + fn upgrade_borrowed<'a>(&'a self) -> Option> + where + 'py: 'a, + { + let object = self.get_object_borrowed(); + + if object.is_none() { + None + } else { + Some(object) + } + } + + /// Retrieve to a Bound object pointed to by the weakref. + /// + /// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn get_class(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { + /// reference + /// .get_object() + /// .getattr("__class__")? + /// .repr()? + /// .to_str() + /// .map(ToOwned::to_owned) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let object = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&object)?; + /// + /// assert_eq!( + /// get_class(reference.as_borrowed())?, + /// "" + /// ); + /// + /// drop(object); + /// + /// assert_eq!(get_class(reference.as_borrowed())?, ""); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + fn get_object(&self) -> Bound<'py, PyAny> { + // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. + self.get_object_borrowed().to_owned() + } + + /// Retrieve to a Borrowed object pointed to by the weakref. + /// + /// This function returns `Borrowed<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// + /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). + /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. + /// + /// # Example + #[cfg_attr( + not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), + doc = "```rust,ignore" + )] + #[cfg_attr( + all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), + doc = "```rust" + )] + /// use pyo3::prelude::*; + /// use pyo3::types::PyWeakrefReference; + /// + /// #[pyclass(weakref)] + /// struct Foo { /* fields omitted */ } + /// + /// fn get_class(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { + /// reference + /// .get_object_borrowed() + /// .getattr("__class__")? + /// .repr()? + /// .to_str() + /// .map(ToOwned::to_owned) + /// } + /// + /// # fn main() -> PyResult<()> { + /// Python::with_gil(|py| { + /// let object = Bound::new(py, Foo{})?; + /// let reference = PyWeakrefReference::new_bound(&object)?; + /// + /// assert_eq!( + /// get_class(reference.as_borrowed())?, + /// "" + /// ); + /// + /// drop(object); + /// + /// assert_eq!(get_class(reference.as_borrowed())?, ""); + /// + /// Ok(()) + /// }) + /// # } + /// ``` + /// + /// # Panics + /// This function panics is the current object is invalid. + /// If used propperly this is never the case. (NonNull and actually a weakref type) + /// + /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject + /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType + /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref + #[track_caller] + // TODO: This function is the reason every function tracks caller, however it only panics when the weakref object is not actually a weakreference type. So is it this neccessary? + fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny>; +} + +impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakref> { fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } @@ -398,11 +1144,11 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakref> { #[cfg(test)] mod tests { use crate::types::any::{PyAny, PyAnyMethods}; - use crate::types::weakref::{PyWeakRef, PyWeakRefMethods, PyWeakref, PyWeakrefProxy}; + use crate::types::weakref::{PyWeakref, PyWeakrefMethods, PyWeakrefProxy, PyWeakrefReference}; use crate::{Bound, PyResult, Python}; fn new_reference<'py>(object: &Bound<'py, PyAny>) -> PyResult> { - let reference = PyWeakRef::new_bound(object)?; + let reference = PyWeakrefReference::new_bound(object)?; reference.into_any().downcast_into().map_err(Into::into) } diff --git a/src/types/weakref/mod.rs b/src/types/weakref/mod.rs index dfc91bd38c7..49d4e515b12 100644 --- a/src/types/weakref/mod.rs +++ b/src/types/weakref/mod.rs @@ -1,6 +1,6 @@ -pub use anyref::PyWeakref; +pub use anyref::{PyWeakref, PyWeakrefMethods}; pub use proxy::PyWeakrefProxy; -pub use reference::{PyWeakRef, PyWeakRefMethods}; +pub use reference::PyWeakrefReference; pub(crate) mod anyref; pub(crate) mod proxy; diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 48a1659f8a4..803d9a02f1d 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -5,7 +5,7 @@ use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAny; use crate::{ffi, Borrowed, Bound, PyNativeType, ToPyObject}; -use super::PyWeakRefMethods; +use super::PyWeakrefMethods; /// Represents any Python `weakref` Proxy type. /// @@ -562,7 +562,7 @@ impl PyWeakrefProxy { } } -impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakrefProxy> { +impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakrefProxy> { #[track_caller] fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. @@ -575,7 +575,7 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakrefProxy> { mod tests { use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError}; use crate::types::any::{PyAny, PyAnyMethods}; - use crate::types::weakref::{PyWeakRefMethods, PyWeakrefProxy}; + use crate::types::weakref::{PyWeakrefMethods, PyWeakrefProxy}; use crate::{Bound, PyResult, Python}; mod proxy { diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 420ef4b2a5a..0fbd7e7c75c 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -1,35 +1,37 @@ -use crate::err::{DowncastError, PyResult}; +use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; -use crate::types::any::{PyAny, PyAnyMethods}; +use crate::types::any::PyAny; use crate::{ffi, AsPyPointer, Borrowed, Bound, PyNativeType, ToPyObject}; +use super::PyWeakrefMethods; + /// Represents a Python `weakref.ReferenceType`. /// /// In Python this is created by calling `weakref.ref`. #[repr(transparent)] -pub struct PyWeakRef(PyAny); +pub struct PyWeakrefReference(PyAny); pyobject_native_type!( - PyWeakRef, + PyWeakrefReference, ffi::PyWeakReference, pyobject_native_static_type_object!(ffi::_PyWeakref_RefType), #module=Some("weakref"), #checkfunction=ffi::PyWeakref_CheckRefExact ); -impl PyWeakRef { - /// Deprecated form of [`PyWeakRef::new_bound`]. +impl PyWeakrefReference { + /// Deprecated form of [`PyWeakrefReference::new_bound`]. #[inline] #[cfg_attr( not(feature = "gil-refs"), deprecated( since = "0.21.0", - note = "`PyWeakRef::new` will be replaced by `PyWeakRef::new_bound` in a future PyO3 version" + note = "`PyWeakrefReference::new` will be replaced by `PyWeakrefReference::new_bound` in a future PyO3 version" ) )] - pub fn new(object: &T) -> PyResult<&PyWeakRef> + pub fn new(object: &T) -> PyResult<&PyWeakrefReference> where T: PyNativeType, { @@ -50,7 +52,7 @@ impl PyWeakRef { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; + /// use pyo3::types::PyWeakrefReference; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -58,14 +60,14 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let foo = Bound::new(py, Foo {})?; - /// let weakref = PyWeakRef::new_bound(&foo)?; + /// let weakref = PyWeakrefReference::new_bound(&foo)?; /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` /// weakref.upgrade() /// .map_or(false, |obj| obj.is(&foo)) /// ); /// - /// let weakref2 = PyWeakRef::new_bound(&foo)?; + /// let weakref2 = PyWeakrefReference::new_bound(&foo)?; /// assert!(weakref.is(&weakref2)); /// /// drop(foo); @@ -75,9 +77,9 @@ impl PyWeakRef { /// }) /// # } /// ``` - pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { + pub fn new_bound<'py>(object: &Bound<'py, PyAny>) -> PyResult> { // TODO: Is this inner pattern still necessary Here? - fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { + fn inner<'py>(object: &Bound<'py, PyAny>) -> PyResult> { unsafe { Bound::from_owned_ptr_or_err( object.py(), @@ -90,16 +92,16 @@ impl PyWeakRef { inner(object) } - /// Deprecated form of [`PyWeakRef::new_bound_with`]. + /// Deprecated form of [`PyWeakrefReference::new_bound_with`]. #[inline] #[cfg_attr( not(feature = "gil-refs"), deprecated( since = "0.21.0", - note = "`PyWeakRef::new_with` will be replaced by `PyWeakRef::new_bound_with` in a future PyO3 version" + note = "`PyWeakrefReference::new_with` will be replaced by `PyWeakrefReference::new_bound_with` in a future PyO3 version" ) )] - pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakRef> + pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakrefReference> where T: PyNativeType, C: ToPyObject, @@ -121,13 +123,13 @@ impl PyWeakRef { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; + /// use pyo3::types::PyWeakrefReference; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } /// /// #[pyfunction] - /// fn callback(wref: Bound<'_, PyWeakRef>) -> PyResult<()> { + /// fn callback(wref: Bound<'_, PyWeakrefReference>) -> PyResult<()> { /// let py = wref.py(); /// assert!(wref.upgrade_as::()?.is_none()); /// py.run_bound("counter = 1", None, None) @@ -140,7 +142,7 @@ impl PyWeakRef { /// let foo = Bound::new(py, Foo{})?; /// /// // This is fine. - /// let weakref = PyWeakRef::new_bound_with(&foo, py.None())?; + /// let weakref = PyWeakrefReference::new_bound_with(&foo, py.None())?; /// assert!(weakref.upgrade_as::()?.is_some()); /// assert!( /// // In normal situations where a direct `Bound<'py, Foo>` is required use `upgrade::` @@ -149,7 +151,7 @@ impl PyWeakRef { /// ); /// assert_eq!(py.eval_bound("counter", None, None)?.extract::()?, 0); /// - /// let weakref2 = PyWeakRef::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?; + /// let weakref2 = PyWeakrefReference::new_bound_with(&foo, wrap_pyfunction_bound!(callback, py)?)?; /// assert!(!weakref.is(&weakref2)); // Not the same weakref /// assert!(weakref.eq(&weakref2)?); // But Equal, since they point to the same object /// @@ -164,14 +166,14 @@ impl PyWeakRef { pub fn new_bound_with<'py, C>( object: &Bound<'py, PyAny>, callback: C, - ) -> PyResult> + ) -> PyResult> where C: ToPyObject, { fn inner<'py>( object: &Bound<'py, PyAny>, callback: Bound<'py, PyAny>, - ) -> PyResult> { + ) -> PyResult> { unsafe { Bound::from_owned_ptr_or_err( object.py(), @@ -200,7 +202,7 @@ impl PyWeakRef { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; + /// use pyo3::types::PyWeakrefReference; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -212,7 +214,7 @@ impl PyWeakRef { /// } /// } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { /// if let Some(data_src) = reference.upgrade_as::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); @@ -225,7 +227,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; + /// let reference = PyWeakrefReference::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -281,7 +283,7 @@ impl PyWeakRef { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; + /// use pyo3::types::PyWeakrefReference; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -293,7 +295,7 @@ impl PyWeakRef { /// } /// } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> String { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> String { /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); @@ -306,7 +308,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; + /// let reference = PyWeakrefReference::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed()), @@ -357,7 +359,7 @@ impl PyWeakRef { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; + /// use pyo3::types::PyWeakrefReference; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } @@ -369,7 +371,7 @@ impl PyWeakRef { /// } /// } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { /// if let Some(data_src) = reference.upgrade_as_exact::()? { /// let data = data_src.borrow(); /// let (name, score) = data.get_data(); @@ -382,7 +384,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; + /// let reference = PyWeakrefReference::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -437,12 +439,12 @@ impl PyWeakRef { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; + /// use pyo3::types::PyWeakrefReference; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// fn parse_data(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { /// if let Some(object) = reference.upgrade() { /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) /// } else { @@ -453,7 +455,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; + /// let reference = PyWeakrefReference::new_bound(&data)?; /// /// assert_eq!( /// parse_data(reference.as_borrowed())?, @@ -501,12 +503,12 @@ impl PyWeakRef { doc = "```rust" )] /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; + /// use pyo3::types::PyWeakrefReference; /// /// #[pyclass(weakref)] /// struct Foo { /* fields omitted */ } /// - /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { + /// fn get_class(reference: Borrowed<'_, '_, PyWeakrefReference>) -> PyResult { /// reference /// .get_object() /// .getattr("__class__")? @@ -518,7 +520,7 @@ impl PyWeakRef { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| { /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&object)?; + /// let reference = PyWeakrefReference::new_bound(&object)?; /// /// assert_eq!( /// get_class(reference.as_borrowed())?, @@ -547,755 +549,7 @@ impl PyWeakRef { } } -/// Implementation of functionality for [`PyWeakRef`]. -/// -/// These methods are defined for the `Bound<'py, PyWeakRef>` smart pointer, so to use method call -/// syntax these methods are separated into a trait, because stable Rust does not yet support -/// `arbitrary_self_types`. -#[doc(alias = "PyWeakRef")] -pub trait PyWeakRefMethods<'py> { - /// Upgrade the weakref to a direct Bound object reference. - /// - /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_as::()? { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// Ok(format!("Processing '{}': score = {}", name, score)) - /// } else { - /// Ok("The supplied data reference is nolonger relavent.".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "Processing 'Dave': score = 10" - /// ); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] - fn upgrade_as(&self) -> PyResult>> - where - T: PyTypeCheck, - { - self.upgrade() - .map(Bound::downcast_into::) - .transpose() - .map_err(Into::into) - } - - /// Upgrade the weakref to a Borrowed object reference. - /// - /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_borrowed_as::()? { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// Ok(format!("Processing '{}': score = {}", name, score)) - /// } else { - /// Ok("The supplied data reference is nolonger relavent.".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "Processing 'Dave': score = 10" - /// ); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? - #[track_caller] - fn upgrade_borrowed_as<'a, T>(&'a self) -> PyResult>> - where - T: PyTypeCheck, - 'py: 'a, - { - // TODO: Replace when Borrowed::downcast exists - match self.upgrade_borrowed() { - None => Ok(None), - Some(object) if T::type_check(&object) => { - Ok(Some(unsafe { object.downcast_unchecked() })) - } - Some(object) => Err(DowncastError::new(&object, T::NAME).into()), - } - } - - /// 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`. - /// - /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Safety - /// Callers must ensure that the type is valid or risk type confusion. - /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> String { - /// if let Some(data_src) = unsafe { reference.upgrade_as_unchecked::() } { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// format!("Processing '{}': score = {}", name, score) - /// } else { - /// "The supplied data reference is nolonger relavent.".to_owned() - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed()), - /// "Processing 'Dave': score = 10" - /// ); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed()), - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] - unsafe fn upgrade_as_unchecked(&self) -> Option> { - Some(self.upgrade()?.downcast_into_unchecked()) - } - - /// Upgrade the weakref to a Borrowed 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`. - /// - /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Safety - /// Callers must ensure that the type is valid or risk type confusion. - /// The `weakref` is still allowed to be `None`, if the referenced object has been cleaned up. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> String { - /// if let Some(data_src) = unsafe { reference.upgrade_borrowed_as_unchecked::() } { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// format!("Processing '{}': score = {}", name, score) - /// } else { - /// "The supplied data reference is nolonger relavent.".to_owned() - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed()), - /// "Processing 'Dave': score = 10" - /// ); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed()), - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? - #[track_caller] - unsafe fn upgrade_borrowed_as_unchecked<'a, T>(&'a self) -> Option> - where - 'py: 'a, - { - Some(self.upgrade_borrowed()?.downcast_unchecked()) - } - - /// Upgrade the weakref to a exact direct Bound object reference. - /// - /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_as_exact::()? { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// Ok(format!("Processing '{}': score = {}", name, score)) - /// } else { - /// Ok("The supplied data reference is nolonger relavent.".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "Processing 'Dave': score = 10" - /// ); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] - fn upgrade_as_exact(&self) -> PyResult>> - where - T: PyTypeInfo, - { - self.upgrade() - .map(Bound::downcast_into_exact) - .transpose() - .map_err(Into::into) - } - - /// Upgrade the weakref to a exact Borrowed object reference. - /// - /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// In Python it would be equivalent to [`PyWeakref_GetObject`]. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// #[pymethods] - /// impl Foo { - /// fn get_data(&self) -> (&str, u32) { - /// ("Dave", 10) - /// } - /// } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(data_src) = reference.upgrade_borrowed_as_exact::()? { - /// let data = data_src.borrow(); - /// let (name, score) = data.get_data(); - /// Ok(format!("Processing '{}': score = {}", name, score)) - /// } else { - /// Ok("The supplied data reference is nolonger relavent.".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "Processing 'Dave': score = 10" - /// ); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The supplied data reference is nolonger relavent." - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? - #[track_caller] - fn upgrade_borrowed_as_exact<'a, T>(&'a self) -> PyResult>> - where - T: PyTypeInfo, - 'py: 'a, - { - // TODO: Replace when Borrowed::downcast_exact exists - match self.upgrade_borrowed() { - None => Ok(None), - Some(object) if object.is_exact_instance_of::() => { - Ok(Some(unsafe { object.downcast_unchecked() })) - } - Some(object) => Err(DowncastError::new(&object, T::NAME).into()), - } - } - - /// Upgrade the weakref to a Bound [`PyAny`] reference to the target object if possible. - /// - /// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// This function returns `Some(Bound<'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. - /// - /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). - /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(object) = reference.upgrade() { - /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) - /// } else { - /// Ok("The object, which this reference refered to, no longer exists".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The object 'Foo' refered by this reference still exists." - /// ); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The object, which this reference refered to, no longer exists" - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] - fn upgrade(&self) -> Option> { - let object = self.get_object(); - - if object.is_none() { - None - } else { - Some(object) - } - } - - /// Upgrade the weakref to a Borrowed [`PyAny`] reference to the target object if possible. - /// - /// It is named `upgrade_borrowed` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). - /// This function returns `Some(Borrowed<'_, 'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. - /// - /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). - /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// fn parse_data(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// if let Some(object) = reference.upgrade_borrowed() { - /// Ok(format!("The object '{}' refered by this reference still exists.", object.getattr("__class__")?.getattr("__qualname__")?)) - /// } else { - /// Ok("The object, which this reference refered to, no longer exists".to_owned()) - /// } - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let data = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&data)?; - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The object 'Foo' refered by this reference still exists." - /// ); - /// - /// drop(data); - /// - /// assert_eq!( - /// parse_data(reference.as_borrowed())?, - /// "The object, which this reference refered to, no longer exists" - /// ); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] - fn upgrade_borrowed<'a>(&'a self) -> Option> - where - 'py: 'a, - { - let object = self.get_object_borrowed(); - - if object.is_none() { - None - } else { - Some(object) - } - } - - /// Retrieve to a Bound object pointed to by the weakref. - /// - /// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). - /// - /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). - /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// reference - /// .get_object() - /// .getattr("__class__")? - /// .repr()? - /// .to_str() - /// .map(ToOwned::to_owned) - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&object)?; - /// - /// assert_eq!( - /// get_class(reference.as_borrowed())?, - /// "" - /// ); - /// - /// drop(object); - /// - /// assert_eq!(get_class(reference.as_borrowed())?, ""); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] - fn get_object(&self) -> Bound<'py, PyAny> { - // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. - self.get_object_borrowed().to_owned() - } - - /// Retrieve to a Borrowed object pointed to by the weakref. - /// - /// This function returns `Borrowed<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). - /// - /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). - /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. - /// - /// # Example - #[cfg_attr( - not(all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9))))), - doc = "```rust,ignore" - )] - #[cfg_attr( - all(feature = "macros", not(all(Py_LIMITED_API, not(Py_3_9)))), - doc = "```rust" - )] - /// use pyo3::prelude::*; - /// use pyo3::types::PyWeakRef; - /// - /// #[pyclass(weakref)] - /// struct Foo { /* fields omitted */ } - /// - /// fn get_class(reference: Borrowed<'_, '_, PyWeakRef>) -> PyResult { - /// reference - /// .get_object_borrowed() - /// .getattr("__class__")? - /// .repr()? - /// .to_str() - /// .map(ToOwned::to_owned) - /// } - /// - /// # fn main() -> PyResult<()> { - /// Python::with_gil(|py| { - /// let object = Bound::new(py, Foo{})?; - /// let reference = PyWeakRef::new_bound(&object)?; - /// - /// assert_eq!( - /// get_class(reference.as_borrowed())?, - /// "" - /// ); - /// - /// drop(object); - /// - /// assert_eq!(get_class(reference.as_borrowed())?, ""); - /// - /// Ok(()) - /// }) - /// # } - /// ``` - /// - /// # Panics - /// This function panics is the current object is invalid. - /// If used propperly this is never the case. (NonNull and actually a weakref type) - /// - /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType - /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] - // TODO: This function is the reason every function tracks caller, however it only panics when the weakref object is not actually a weakreference type. So is it this neccessary? - fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny>; -} - -impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { +impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakrefReference> { #[track_caller] fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. @@ -1307,7 +561,7 @@ impl<'py> PyWeakRefMethods<'py> for Bound<'py, PyWeakRef> { #[cfg(test)] mod tests { use crate::types::any::{PyAny, PyAnyMethods}; - use crate::types::weakref::{PyWeakRef, PyWeakRefMethods}; + use crate::types::weakref::{PyWeakrefMethods, PyWeakrefReference}; use crate::{Bound, PyResult, Python}; #[cfg(all(not(Py_LIMITED_API), Py_3_10))] @@ -1316,7 +570,7 @@ mod tests { const CLASS_NAME: &str = ""; fn check_repr( - reference: &Bound<'_, PyWeakRef>, + reference: &Bound<'_, PyWeakrefReference>, object: Option<(&Bound<'_, PyAny>, &str)>, ) -> PyResult<()> { let repr = reference.repr()?.to_string(); @@ -1362,7 +616,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; assert!(!reference.is(&object)); assert!(reference.get_object().is(&object)); @@ -1403,7 +657,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; { // This test is a bit weird but ok. @@ -1438,7 +692,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; { // This test is a bit weird but ok. @@ -1473,7 +727,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; { // This test is a bit weird but ok. @@ -1502,7 +756,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; { // This test is a bit weird but ok. @@ -1531,7 +785,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; assert!(reference.call0()?.is(&object)); assert!(reference.upgrade().is_some()); @@ -1551,7 +805,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; assert!(reference.call0()?.is(&object)); assert!(reference.upgrade_borrowed().is_some()); @@ -1573,7 +827,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; assert!(reference.call0()?.is(&object)); assert!(reference.get_object().is(&object)); @@ -1593,7 +847,7 @@ mod tests { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; assert!(reference.call0()?.is(&object)); assert!(reference.get_object_borrowed().is(&object)); @@ -1621,7 +875,7 @@ mod tests { fn test_weakref_refence_behavior() -> PyResult<()> { Python::with_gil(|py| { let object: Bound<'_, WeakrefablePyClass> = Bound::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(&object)?; + let reference = PyWeakrefReference::new_bound(&object)?; assert!(!reference.is(&object)); assert!(reference.get_object().is(&object)); @@ -1662,7 +916,7 @@ mod tests { fn test_weakref_upgrade_as() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(object.bind(py))?; + let reference = PyWeakrefReference::new_bound(object.bind(py))?; { let obj = reference.upgrade_as::(); @@ -1693,7 +947,7 @@ mod tests { fn test_weakref_upgrade_borrowed_as() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(object.bind(py))?; + let reference = PyWeakrefReference::new_bound(object.bind(py))?; { let obj = reference.upgrade_borrowed_as::(); @@ -1724,7 +978,7 @@ mod tests { fn test_weakref_upgrade_as_unchecked() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(object.bind(py))?; + let reference = PyWeakrefReference::new_bound(object.bind(py))?; { let obj = unsafe { reference.upgrade_as_unchecked::() }; @@ -1749,7 +1003,7 @@ mod tests { fn test_weakref_upgrade_borrowed_as_unchecked() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(object.bind(py))?; + let reference = PyWeakrefReference::new_bound(object.bind(py))?; { let obj = @@ -1776,7 +1030,7 @@ mod tests { fn test_weakref_upgrade() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(object.bind(py))?; + let reference = PyWeakrefReference::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); assert!(reference.upgrade().is_some()); @@ -1795,7 +1049,7 @@ mod tests { fn test_weakref_upgrade_borrowed() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(object.bind(py))?; + let reference = PyWeakrefReference::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); assert!(reference.upgrade_borrowed().is_some()); @@ -1816,7 +1070,7 @@ mod tests { fn test_weakref_get_object() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(object.bind(py))?; + let reference = PyWeakrefReference::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); assert!(reference.get_object().is(&object)); @@ -1835,7 +1089,7 @@ mod tests { fn test_weakref_get_object_borrowed() -> PyResult<()> { Python::with_gil(|py| { let object = Py::new(py, WeakrefablePyClass {})?; - let reference = PyWeakRef::new_bound(object.bind(py))?; + let reference = PyWeakrefReference::new_bound(object.bind(py))?; assert!(reference.call0()?.is(&object)); assert!(reference.get_object_borrowed().is(&object)); From c7ab37541a2804f47567fec860c0b2b20939f7a9 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Thu, 18 Apr 2024 17:21:08 +0200 Subject: [PATCH 35/42] Change PyWeakrefReference to only use type pointer when it exists --- newsfragments/3835.added.md | 2 +- src/prelude.rs | 1 - src/types/mod.rs | 2 -- src/types/weakref/reference.rs | 16 ++++++++++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/newsfragments/3835.added.md b/newsfragments/3835.added.md index 55871f4cdf7..2970a4c8db4 100644 --- a/newsfragments/3835.added.md +++ b/newsfragments/3835.added.md @@ -1 +1 @@ -Add `PyWeakRef`, `PyWeakProxy` and `PyWeakCallableProxy`. +Add `PyWeakref`, `PyWeakrefReference` and `PyWeakrefProxy`. diff --git a/src/prelude.rs b/src/prelude.rs index 6fcd911e973..f9c2e69f8a5 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -46,5 +46,4 @@ pub use crate::types::string::PyStringMethods; pub use crate::types::traceback::PyTracebackMethods; pub use crate::types::tuple::PyTupleMethods; pub use crate::types::typeobject::PyTypeMethods; -#[cfg(not(PyPy))] pub use crate::types::weakref::PyWeakrefMethods; diff --git a/src/types/mod.rs b/src/types/mod.rs index aed1f12f08c..d8f79c134dc 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -47,7 +47,6 @@ pub use self::string::{PyString, PyString as PyUnicode, PyStringMethods}; pub use self::traceback::{PyTraceback, PyTracebackMethods}; pub use self::tuple::{PyTuple, PyTupleMethods}; pub use self::typeobject::{PyType, PyTypeMethods}; -#[cfg(not(PyPy))] pub use self::weakref::{PyWeakref, PyWeakrefMethods, PyWeakrefProxy, PyWeakrefReference}; /// Iteration over Python collections. @@ -352,5 +351,4 @@ pub(crate) mod string; pub(crate) mod traceback; pub(crate) mod tuple; pub(crate) mod typeobject; -#[cfg(not(any(PyPy, GraalPy)))] // FIXME: Remove this soon pub(crate) mod weakref; diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 0fbd7e7c75c..904e129b25b 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -13,6 +13,7 @@ use super::PyWeakrefMethods; #[repr(transparent)] pub struct PyWeakrefReference(PyAny); +#[cfg(not(any(PyPy, GraalPy, Py_LIMITED_API)))] pyobject_native_type!( PyWeakrefReference, ffi::PyWeakReference, @@ -21,6 +22,21 @@ pyobject_native_type!( #checkfunction=ffi::PyWeakref_CheckRefExact ); +// When targetting alternative or multiple interpreters, it is better to not use the internal API. +#[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] +pyobject_native_type_named!(PyWeakrefReference); +#[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] +pyobject_native_type_extract!(PyWeakrefReference); + +#[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] +impl PyTypeCheck for PyWeakrefReference { + const NAME: &'static str = "weakref.ReferenceType"; + + fn type_check(object: &Bound<'_, PyAny>) -> bool { + unsafe { ffi::PyWeakref_CheckRef(object.as_ptr()) > 0 } + } +} + impl PyWeakrefReference { /// Deprecated form of [`PyWeakrefReference::new_bound`]. #[inline] From 618a714a61115b0c6dd66b726f9406457fb383d0 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sun, 26 May 2024 09:20:42 +0100 Subject: [PATCH 36/42] Remove `#[track_caller]` annotations for now --- src/types/weakref/anyref.rs | 14 -------------- src/types/weakref/proxy.rs | 6 ------ src/types/weakref/reference.rs | 6 ------ 3 files changed, 26 deletions(-) diff --git a/src/types/weakref/anyref.rs b/src/types/weakref/anyref.rs index 863e8c2f537..597e9eec909 100644 --- a/src/types/weakref/anyref.rs +++ b/src/types/weakref/anyref.rs @@ -93,7 +93,6 @@ impl PyWeakref { /// If used propperly this is never the case. (NonNull and actually a weakref type) /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - #[track_caller] pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, @@ -173,7 +172,6 @@ impl PyWeakref { /// If used propperly this is never the case. (NonNull and actually a weakref type) /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - #[track_caller] pub unsafe fn upgrade_as_unchecked(&self) -> Option<&T::AsRefTarget> where T: PyTypeCheck, @@ -248,7 +246,6 @@ impl PyWeakref { /// If used propperly this is never the case. (NonNull and actually a weakref type) /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - #[track_caller] pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, @@ -318,7 +315,6 @@ impl PyWeakref { /// If used propperly this is never the case. (NonNull and actually a weakref type) /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - #[track_caller] pub fn upgrade(&self) -> Option<&'_ PyAny> { self.as_borrowed().upgrade().map(Bound::into_gil_ref) } @@ -379,7 +375,6 @@ impl PyWeakref { /// If used propperly this is never the case. (NonNull and actually a weakref type) /// /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject - #[track_caller] pub fn get_object(&self) -> &'_ PyAny { self.as_borrowed().get_object().into_gil_ref() } @@ -458,7 +453,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] fn upgrade_as(&self) -> PyResult>> where T: PyTypeCheck, @@ -535,7 +529,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? - #[track_caller] fn upgrade_borrowed_as<'a, T>(&'a self) -> PyResult>> where T: PyTypeCheck, @@ -621,7 +614,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] unsafe fn upgrade_as_unchecked(&self) -> Option> { Some(self.upgrade()?.downcast_into_unchecked()) } @@ -696,7 +688,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? - #[track_caller] unsafe fn upgrade_borrowed_as_unchecked<'a, T>(&'a self) -> Option> where 'py: 'a, @@ -770,7 +761,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] fn upgrade_as_exact(&self) -> PyResult>> where T: PyTypeInfo, @@ -847,7 +837,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref? - #[track_caller] fn upgrade_borrowed_as_exact<'a, T>(&'a self) -> PyResult>> where T: PyTypeInfo, @@ -923,7 +912,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] fn upgrade(&self) -> Option> { let object = self.get_object(); @@ -994,7 +982,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] fn upgrade_borrowed<'a>(&'a self) -> Option> where 'py: 'a, @@ -1065,7 +1052,6 @@ pub trait PyWeakrefMethods<'py> { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] fn get_object(&self) -> Bound<'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. self.get_object_borrowed().to_owned() diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 803d9a02f1d..fd144f14141 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -264,7 +264,6 @@ impl PyWeakrefProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, @@ -345,7 +344,6 @@ impl PyWeakrefProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] pub unsafe fn upgrade_as_unchecked(&self) -> Option<&T::AsRefTarget> where T: PyTypeCheck, @@ -421,7 +419,6 @@ impl PyWeakrefProxy { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, @@ -493,7 +490,6 @@ impl PyWeakrefProxy { /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] pub fn upgrade(&self) -> Option<&'_ PyAny> { self.as_borrowed().upgrade().map(Bound::into_gil_ref) } @@ -556,14 +552,12 @@ impl PyWeakrefProxy { /// [`weakref.ProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.ProxyType /// [`weakref.CallableProxyType`]: https://docs.python.org/3/library/weakref.html#weakref.CallableProxyType /// [`weakref.proxy`]: https://docs.python.org/3/library/weakref.html#weakref.proxy - #[track_caller] pub fn get_object(&self) -> &'_ PyAny { self.as_borrowed().get_object().into_gil_ref() } } impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakrefProxy> { - #[track_caller] fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 904e129b25b..23b6e10246a 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -269,7 +269,6 @@ impl PyWeakrefReference { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] pub fn upgrade_as(&self) -> PyResult> where T: PyTypeCheck, @@ -350,7 +349,6 @@ impl PyWeakrefReference { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] pub unsafe fn upgrade_as_unchecked(&self) -> Option<&T::AsRefTarget> where T: PyTypeCheck, @@ -426,7 +424,6 @@ impl PyWeakrefReference { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] pub fn upgrade_as_exact(&self) -> PyResult> where T: PyTypeInfo, @@ -497,7 +494,6 @@ impl PyWeakrefReference { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] pub fn upgrade(&self) -> Option<&'_ PyAny> { self.as_borrowed().upgrade().map(Bound::into_gil_ref) } @@ -559,14 +555,12 @@ impl PyWeakrefReference { /// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject /// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType /// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref - #[track_caller] pub fn get_object(&self) -> &'_ PyAny { self.as_borrowed().get_object().into_gil_ref() } } impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakrefReference> { - #[track_caller] fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { // PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } From 3cebed41f41b8ebb52cd76ce4a9d824701ba5975 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Sun, 26 May 2024 13:15:17 +0200 Subject: [PATCH 37/42] Make the gil-refs function feature dependent --- src/types/weakref/anyref.rs | 6 ++- src/types/weakref/proxy.rs | 71 +++++++++++++++++----------------- src/types/weakref/reference.rs | 71 +++++++++++++++++----------------- 3 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/types/weakref/anyref.rs b/src/types/weakref/anyref.rs index 597e9eec909..194a7118064 100644 --- a/src/types/weakref/anyref.rs +++ b/src/types/weakref/anyref.rs @@ -2,7 +2,10 @@ use crate::err::{DowncastError, PyResult}; use crate::ffi_ptr_ext::FfiPtrExt; use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::{PyAny, PyAnyMethods}; -use crate::{ffi, Borrowed, Bound, PyNativeType}; +use crate::{ffi, Borrowed, Bound}; + +#[cfg(feature = "gil-refs")] +use crate::PyNativeType; /// Represents any Python `weakref` reference. /// @@ -25,6 +28,7 @@ impl PyTypeCheck for PyWeakref { } } +#[cfg(feature = "gil-refs")] impl PyWeakref { // TODO: MAYBE ADD CREATION METHODS OR EASY CASTING?; diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index fd144f14141..438f30a0990 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -1,9 +1,12 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; -use crate::type_object::{PyTypeCheck, PyTypeInfo}; +use crate::type_object::PyTypeCheck; use crate::types::any::PyAny; -use crate::{ffi, Borrowed, Bound, PyNativeType, ToPyObject}; +use crate::{ffi, Borrowed, Bound, ToPyObject}; + +#[cfg(feature = "gil-refs")] +use crate::{type_object::PyTypeInfo, PyNativeType}; use super::PyWeakrefMethods; @@ -31,22 +34,6 @@ impl PyTypeCheck for PyWeakrefProxy { /// TODO: UPDATE DOCS impl PyWeakrefProxy { - /// Deprecated form of [`PyWeakrefProxy::new_bound`]. - #[inline] - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "`PyWeakrefProxy::new` will be replaced by `PyWeakrefProxy::new_bound` in a future PyO3 version" - ) - )] - pub fn new(object: &T) -> PyResult<&PyWeakrefProxy> - where - T: PyNativeType, - { - Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) - } - /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`/`weakref.CallableProxyType`) for the given object. /// /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag). @@ -102,23 +89,6 @@ impl PyWeakrefProxy { inner(object) } - /// Deprecated form of [`PyWeakrefProxy::new_bound_with`]. - #[inline] - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "`PyWeakrefProxy::new_with` will be replaced by `PyWeakrefProxy::new_bound_with` in a future PyO3 version" - ) - )] - pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakrefProxy> - where - T: PyNativeType, - C: ToPyObject, - { - Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) - } - /// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`/`weakref.CallableProxyType`) for the given object with a callback. /// /// 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. @@ -197,6 +167,37 @@ impl PyWeakrefProxy { let py = object.py(); inner(object, callback.to_object(py).into_bound(py)) } +} + +/// TODO: UPDATE DOCS +#[cfg(feature = "gil-refs")] +impl PyWeakrefProxy { + /// Deprecated form of [`PyWeakrefProxy::new_bound`]. + #[inline] + #[deprecated( + since = "0.21.0", + note = "`PyWeakrefProxy::new` will be replaced by `PyWeakrefProxy::new_bound` in a future PyO3 version" + )] + pub fn new(object: &T) -> PyResult<&PyWeakrefProxy> + where + T: PyNativeType, + { + Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) + } + + /// Deprecated form of [`PyWeakrefProxy::new_bound_with`]. + #[inline] + #[deprecated( + since = "0.21.0", + note = "`PyWeakrefProxy::new_with` will be replaced by `PyWeakrefProxy::new_bound_with` in a future PyO3 version" + )] + pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakrefProxy> + where + T: PyNativeType, + C: ToPyObject, + { + Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) + } /// Upgrade the weakref to a direct object reference. /// diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 23b6e10246a..d36b98990a9 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -1,9 +1,13 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; -use crate::type_object::{PyTypeCheck, PyTypeInfo}; use crate::types::any::PyAny; -use crate::{ffi, AsPyPointer, Borrowed, Bound, PyNativeType, ToPyObject}; +use crate::{ffi, AsPyPointer, Borrowed, Bound, ToPyObject}; + +#[cfg(any(any(PyPy, GraalPy, Py_LIMITED_API), feature = "gil-refs"))] +use crate::type_object::PyTypeCheck; +#[cfg(feature = "gil-refs")] +use crate::{type_object::PyTypeInfo, PyNativeType}; use super::PyWeakrefMethods; @@ -38,22 +42,6 @@ impl PyTypeCheck for PyWeakrefReference { } impl PyWeakrefReference { - /// Deprecated form of [`PyWeakrefReference::new_bound`]. - #[inline] - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "`PyWeakrefReference::new` will be replaced by `PyWeakrefReference::new_bound` in a future PyO3 version" - ) - )] - pub fn new(object: &T) -> PyResult<&PyWeakrefReference> - where - T: PyNativeType, - { - Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) - } - /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object. /// /// Returns a `TypeError` if `object` is not weak referenceable (Most native types and PyClasses without `weakref` flag). @@ -108,23 +96,6 @@ impl PyWeakrefReference { inner(object) } - /// Deprecated form of [`PyWeakrefReference::new_bound_with`]. - #[inline] - #[cfg_attr( - not(feature = "gil-refs"), - deprecated( - since = "0.21.0", - note = "`PyWeakrefReference::new_with` will be replaced by `PyWeakrefReference::new_bound_with` in a future PyO3 version" - ) - )] - pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakrefReference> - where - T: PyNativeType, - C: ToPyObject, - { - Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) - } - /// Constructs a new Weak Reference (`weakref.ref`/`weakref.ReferenceType`) for the given object with a callback. /// /// 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. @@ -202,6 +173,36 @@ impl PyWeakrefReference { let py = object.py(); inner(object, callback.to_object(py).into_bound(py)) } +} + +#[cfg(feature = "gil-refs")] +impl PyWeakrefReference { + /// Deprecated form of [`PyWeakrefReference::new_bound`]. + #[inline] + #[deprecated( + since = "0.21.0", + note = "`PyWeakrefReference::new` will be replaced by `PyWeakrefReference::new_bound` in a future PyO3 version" + )] + pub fn new(object: &T) -> PyResult<&PyWeakrefReference> + where + T: PyNativeType, + { + Self::new_bound(object.as_borrowed().as_any()).map(Bound::into_gil_ref) + } + + /// Deprecated form of [`PyWeakrefReference::new_bound_with`]. + #[inline] + #[deprecated( + since = "0.21.0", + note = "`PyWeakrefReference::new_with` will be replaced by `PyWeakrefReference::new_bound_with` in a future PyO3 version" + )] + pub fn new_with(object: &T, callback: C) -> PyResult<&PyWeakrefReference> + where + T: PyNativeType, + C: ToPyObject, + { + Self::new_bound_with(object.as_borrowed().as_any(), callback).map(Bound::into_gil_ref) + } /// Upgrade the weakref to a direct object reference. /// From b9511e18f80cdb24e783b2c74c68c7e4bdf954d0 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Sun, 26 May 2024 13:40:29 +0200 Subject: [PATCH 38/42] Remove unused AsPyPointer import --- src/types/weakref/reference.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index d36b98990a9..7ea0f108696 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -2,7 +2,7 @@ use crate::err::PyResult; use crate::ffi_ptr_ext::FfiPtrExt; use crate::py_result_ext::PyResultExt; use crate::types::any::PyAny; -use crate::{ffi, AsPyPointer, Borrowed, Bound, ToPyObject}; +use crate::{ffi, Borrowed, Bound, ToPyObject}; #[cfg(any(any(PyPy, GraalPy, Py_LIMITED_API), feature = "gil-refs"))] use crate::type_object::PyTypeCheck; From 4f9bfb095845f96bf25462192956fb43e79fd6e7 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Sun, 26 May 2024 16:21:17 +0200 Subject: [PATCH 39/42] Change docs links to PyNone to not include private module --- src/types/weakref/anyref.rs | 6 +++--- src/types/weakref/proxy.rs | 2 +- src/types/weakref/reference.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/types/weakref/anyref.rs b/src/types/weakref/anyref.rs index 194a7118064..6ed2619a775 100644 --- a/src/types/weakref/anyref.rs +++ b/src/types/weakref/anyref.rs @@ -325,7 +325,7 @@ impl PyWeakref { /// Retrieve to a object pointed to by the weakref. /// - /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone). /// /// This function gets the optional target of this [`PyWeakref`] (Any Python `weakref` weakreference). /// It produces similair results as using [`PyWeakref_GetObject`] in the C api or retrieving the Object from Python. @@ -1001,7 +1001,7 @@ pub trait PyWeakrefMethods<'py> { /// Retrieve to a Bound object pointed to by the weakref. /// - /// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone). /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. @@ -1063,7 +1063,7 @@ pub trait PyWeakrefMethods<'py> { /// Retrieve to a Borrowed object pointed to by the weakref. /// - /// This function returns `Borrowed<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// This function returns `Borrowed<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone). /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). /// It produces similair results to using [`PyWeakref_GetObject`] in the C api. diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 438f30a0990..9ac74db31af 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -497,7 +497,7 @@ impl PyWeakrefProxy { /// Retrieve to a object pointed to by the weakref. /// - /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone). /// /// This function gets the optional target of this [`weakref.ProxyType`] (or [`weakref.CallableProxyType`], result of calling [`weakref.proxy`]). /// It produces similair results using [`PyWeakref_GetObject`] in the C api. diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 7ea0f108696..c89c5a6ca24 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -501,7 +501,7 @@ impl PyWeakrefReference { /// Retrieve to a object pointed to by the weakref. /// - /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::none::PyNone). + /// This function returns `&'py PyAny`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone). /// /// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). /// It produces similair results to calling the `weakref.ReferenceType` or using [`PyWeakref_GetObject`] in the C api. From dc9991856b4bdc96de15672636af95c4315d67b6 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Sun, 26 May 2024 19:52:43 +0200 Subject: [PATCH 40/42] Fix string based examples --- src/types/weakref/anyref.rs | 15 ++++++--------- src/types/weakref/proxy.rs | 15 ++++++++++----- src/types/weakref/reference.rs | 5 ++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/types/weakref/anyref.rs b/src/types/weakref/anyref.rs index 6ed2619a775..82e16293e62 100644 --- a/src/types/weakref/anyref.rs +++ b/src/types/weakref/anyref.rs @@ -349,9 +349,8 @@ impl PyWeakref { /// reference /// .get_object() /// .getattr("__class__")? - /// .repr()? - /// .to_str() - /// .map(ToOwned::to_owned) + /// .repr() + /// .map(|repr| repr.to_string()) /// } /// /// # fn main() -> PyResult<()> { @@ -1025,9 +1024,8 @@ pub trait PyWeakrefMethods<'py> { /// reference /// .get_object() /// .getattr("__class__")? - /// .repr()? - /// .to_str() - /// .map(ToOwned::to_owned) + /// .repr() + /// .map(|repr| repr.to_string()) /// } /// /// # fn main() -> PyResult<()> { @@ -1087,9 +1085,8 @@ pub trait PyWeakrefMethods<'py> { /// reference /// .get_object_borrowed() /// .getattr("__class__")? - /// .repr()? - /// .to_str() - /// .map(ToOwned::to_owned) + /// .repr() + /// .map(|repr| repr.to_string()) /// } /// /// # fn main() -> PyResult<()> { diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 9ac74db31af..561d24b3605 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -521,9 +521,8 @@ impl PyWeakrefProxy { /// reference /// .get_object() /// .getattr("__class__")? - /// .repr()? - /// .to_str() - /// .map(ToOwned::to_owned) + /// .repr() + /// .map(|repr| repr.to_string()) /// } /// /// # fn main() -> PyResult<()> { @@ -600,7 +599,10 @@ mod tests { let (msg, addr) = second_part.split_once("0x").unwrap(); - assert_eq!(msg, format!("{} at ", class)); + // Avoids not succeeding at unreliable quotation (Python 3.13-dev adds ' around classname without documenting) + assert!(msg.ends_with(" at ")); + assert!(msg.contains(class)); + assert!(addr .to_lowercase() .contains(format!("{:x?}", object.as_ptr()).split_at(2).1)); @@ -1165,7 +1167,10 @@ mod tests { let (msg, addr) = second_part.split_once("0x").unwrap(); - assert_eq!(msg, format!("{} at ", class)); + // Avoids not succeeding at unreliable quotation (Python 3.13-dev adds ' around classname without documenting) + assert!(msg.ends_with(" at ")); + assert!(msg.contains(class)); + assert!(addr .to_lowercase() .contains(format!("{:x?}", object.as_ptr()).split_at(2).1)); diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index c89c5a6ca24..adb77c11b9e 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -525,9 +525,8 @@ impl PyWeakrefReference { /// reference /// .get_object() /// .getattr("__class__")? - /// .repr()? - /// .to_str() - /// .map(ToOwned::to_owned) + /// .repr() + /// .map(|repr| repr.to_string()) /// } /// /// # fn main() -> PyResult<()> { From ada2c3a6eac055c8e073c82e3c532f01c9b3c2d9 Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Sun, 26 May 2024 21:20:29 +0200 Subject: [PATCH 41/42] Change tests to work for Python 3.13 --- src/types/weakref/proxy.rs | 124 ++++++++++++++++----------------- src/types/weakref/reference.rs | 17 +++-- 2 files changed, 69 insertions(+), 72 deletions(-) diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index 561d24b3605..a249d1b2a9b 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -572,44 +572,60 @@ mod tests { use crate::types::weakref::{PyWeakrefMethods, PyWeakrefProxy}; use crate::{Bound, PyResult, Python}; - mod proxy { - use super::*; - - #[cfg(all(not(Py_LIMITED_API), Py_3_10))] - const CLASS_NAME: &str = "'weakref.ProxyType'"; - #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] - const CLASS_NAME: &str = "'weakproxy'"; - - fn check_repr( - reference: &Bound<'_, PyWeakrefProxy>, - object: &Bound<'_, PyAny>, - class: &str, - ) -> PyResult<()> { - let repr = reference.repr()?.to_string(); - let (first_part, second_part) = repr.split_once(" to ").unwrap(); - - { - let (msg, addr) = first_part.split_once("0x").unwrap(); - - assert_eq!(msg, " = None; + #[cfg(all(not(Py_3_13), not(Py_LIMITED_API)))] + const DEADREF_FIX: Option<&str> = Some("NoneType"); + + #[cfg(not(Py_LIMITED_API))] + fn check_repr( + reference: &Bound<'_, PyWeakrefProxy>, + object: &Bound<'_, PyAny>, + class: Option<&str>, + ) -> PyResult<()> { + let repr = reference.repr()?.to_string(); + + #[cfg(Py_3_13)] + let (first_part, second_part) = repr.split_once(";").unwrap(); + #[cfg(not(Py_3_13))] + let (first_part, second_part) = repr.split_once(" to ").unwrap(); + + { + let (msg, addr) = first_part.split_once("0x").unwrap(); + + assert_eq!(msg, "" ); - check_repr(&reference, &object, "A")?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, &object, Some("A"))?; assert!(reference .getattr("__callback__") @@ -662,7 +679,8 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, py.None().bind(py), "NoneType")?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, py.None().bind(py), None)?; assert!(reference .getattr("__callback__") @@ -911,7 +929,8 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - check_repr(&reference, object.as_any(), "builtins.WeakrefablePyClass")?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, object.as_any(), Some("WeakrefablePyClass"))?; assert!(reference .getattr("__callback__") @@ -934,7 +953,8 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, py.None().bind(py), "NoneType")?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, py.None().bind(py), None)?; assert!(reference .getattr("__callback__") @@ -1148,36 +1168,6 @@ mod tests { #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] const CLASS_NAME: &str = ""; - fn check_repr( - reference: &Bound<'_, PyWeakrefProxy>, - object: &Bound<'_, PyAny>, - class: &str, - ) -> PyResult<()> { - let repr = reference.repr()?.to_string(); - let (first_part, second_part) = repr.split_once(" to ").unwrap(); - - { - let (msg, addr) = first_part.split_once("0x").unwrap(); - - assert_eq!(msg, "" ); - check_repr(&reference, &object, "A")?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, &object, Some("A"))?; assert!(reference .getattr("__callback__") @@ -1223,7 +1214,8 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, py.None().bind(py), "NoneType")?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, py.None().bind(py), None)?; assert!(reference .getattr("__callback__") @@ -1474,7 +1466,8 @@ mod tests { reference.getattr("__class__")?.to_string(), "" ); - check_repr(&reference, object.as_any(), "builtins.WeakrefablePyClass")?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, object.as_any(), Some("WeakrefablePyClass"))?; assert!(reference .getattr("__callback__") @@ -1490,7 +1483,8 @@ mod tests { .getattr("__class__") .err() .map_or(false, |err| err.is_instance_of::(py))); - check_repr(&reference, py.None().bind(py), "NoneType")?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, py.None().bind(py), None)?; assert!(reference .getattr("__callback__") diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index adb77c11b9e..6cdcde3a7f7 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -599,7 +599,11 @@ mod tests { Some((object, class)) => { let (msg, addr) = second_part.split_once("0x").unwrap(); - assert_eq!(msg, format!("to '{}' at ", class)); + // Avoid testing on reprs directly since they the quoting and full path vs class name tends to be changedi undocumented. + assert!(msg.starts_with("to '")); + assert!(msg.contains(class)); + assert!(msg.ends_with("' at ")); + assert!(addr .to_lowercase() .contains(format!("{:x?}", object.as_ptr()).split_at(2).1)); @@ -622,7 +626,7 @@ mod tests { } #[test] - fn test_weakref_refence_behavior() -> PyResult<()> { + fn test_weakref_reference_behavior() -> PyResult<()> { Python::with_gil(|py| { let class = get_type(py)?; let object = class.call0()?; @@ -637,6 +641,7 @@ mod tests { #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); + #[cfg(not(Py_LIMITED_API))] check_repr(&reference, Some((object.as_any(), "A")))?; assert!(reference @@ -882,7 +887,7 @@ mod tests { struct WeakrefablePyClass {} #[test] - fn test_weakref_refence_behavior() -> PyResult<()> { + fn test_weakref_reference_behavior() -> PyResult<()> { Python::with_gil(|py| { let object: Bound<'_, WeakrefablePyClass> = Bound::new(py, WeakrefablePyClass {})?; let reference = PyWeakrefReference::new_bound(&object)?; @@ -894,10 +899,8 @@ mod tests { #[cfg(not(Py_LIMITED_API))] assert_eq!(reference.getattr("__class__")?.to_string(), CLASS_NAME); - check_repr( - &reference, - Some((object.as_any(), "builtins.WeakrefablePyClass")), - )?; + #[cfg(not(Py_LIMITED_API))] + check_repr(&reference, Some((object.as_any(), "WeakrefablePyClass")))?; assert!(reference .getattr("__callback__") From 7e9aad8725e8a679cb4f9998a476683fbf44295e Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Sun, 26 May 2024 21:32:48 +0200 Subject: [PATCH 42/42] Fix cargo clippy for Python 3.13 --- src/types/weakref/proxy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/weakref/proxy.rs b/src/types/weakref/proxy.rs index a249d1b2a9b..71334488b54 100644 --- a/src/types/weakref/proxy.rs +++ b/src/types/weakref/proxy.rs @@ -586,7 +586,7 @@ mod tests { let repr = reference.repr()?.to_string(); #[cfg(Py_3_13)] - let (first_part, second_part) = repr.split_once(";").unwrap(); + let (first_part, second_part) = repr.split_once(';').unwrap(); #[cfg(not(Py_3_13))] let (first_part, second_part) = repr.split_once(" to ").unwrap();