From a1c7fc826f593f28d0005689948e3048d4879d2b Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sun, 14 Jul 2024 14:59:21 -0500 Subject: [PATCH 1/8] Added `Bound::as_super` and `Bound::into_super` methods. --- src/instance.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/instance.rs b/src/instance.rs index 6967306a73f..52cea0022fc 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -359,6 +359,102 @@ where self.1.get() } + /// Upcast this `Bound` to its base type by reference. + /// + /// If this type defined an explicit base class in its`pyclass` declaration + /// (e.g. `#[pyclass(extends = BaseType)]`), the returned type will be + /// `&Bound`. If an explicit base class was _not_ declared, the + /// return value will be `&Bound` (making this method equivalent + /// to as [`as_any`]). + /// + /// This method is particularly useful for calling methods defined in an + /// extension trait that has been implemented for `Bound`. + /// + /// See also the [`into_super`] method to upcast by value, and the + /// [`PyRef::as_super`]/[`PyRefMut::as_super`] methods for upcasting a pyclass + /// that has already been [`borrow`]ed. + /// + /// # Example: Calling a method defined on the `Bound` base type + /// + /// ```rust + /// # fn main() { + /// use pyo3::prelude::*; + /// + /// #[pyclass(subclass)] + /// struct BaseClass; + /// + /// trait MyClassMethods<'py> { + /// fn pyrepr(&self) -> PyResult; + /// } + /// impl<'py> MyClassMethods<'py> for Bound<'py, BaseClass> { + /// fn pyrepr(&self) -> PyResult { + /// self.call_method0("__repr__")?.extract() + /// } + /// } + /// + /// #[pyclass(extends = BaseClass)] + /// struct SubClass; + /// + /// Python::with_gil(|py| { + /// let obj = Bound::new(py, (SubClass, BaseClass)).unwrap(); + /// assert!(obj.as_super().pyrepr().is_ok()); + /// }) + /// # } + /// ``` + #[inline] + pub fn as_super(&self) -> &Bound<'py, T::BaseType> { + // a pyclass can always be safely "downcast" to its base type + unsafe { self.as_any().downcast_unchecked() } + } + + /// Upcast this `Bound` to its base type by value. + /// + /// If this type defined an explicit base class in its`pyclass` declaration + /// (e.g. `#[pyclass(extends = BaseType)]`), the returned type will be + /// `&Bound`. If an explicit base class was _not_ declared, the + /// return value will be `&Bound` (making this method equivalent + /// to as [`as_any`]). + /// + /// This method is particularly useful for calling methods defined in an + /// extension trait that has been implemented for `Bound`. + /// + /// See also the [`as_super`] method to upcast by value, and the + /// [`PyRef::into_super`]/[`PyRefMut::into_super`] methods for upcasting a pyclass + /// that has already been [`borrow`]ed. + /// + /// # Example: Calling a method defined on the `Bound` base type + /// + /// ```rust + /// # fn main() { + /// use pyo3::prelude::*; + /// + /// #[pyclass(subclass)] + /// struct BaseClass; + /// + /// trait MyClassMethods<'py> { + /// fn pyrepr(self) -> PyResult; + /// } + /// impl<'py> MyClassMethods<'py> for Bound<'py, BaseClass> { + /// fn pyrepr(self) -> PyResult { + /// self.call_method0("__repr__")?.extract() + /// } + /// } + /// + /// #[pyclass(extends = BaseClass)] + /// struct SubClass; + /// + /// Python::with_gil(|py| { + /// let obj = Bound::new(py, (SubClass, BaseClass)).unwrap(); + /// assert!(obj.into_super().pyrepr().is_ok()); + /// }) + /// # } + /// ``` + #[inline] + pub fn into_super(self) -> Bound<'py, T::BaseType> { + // a pyclass can always be safely "downcast" to its base type + unsafe { self.into_any().downcast_into_unchecked() } + } + #[inline] pub(crate) fn get_class_object(&self) -> &PyClassObject { self.1.get_class_object() From feb42c2f5535ddd3c8e4bceba9d7ceedda458b2e Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sun, 14 Jul 2024 14:59:38 -0500 Subject: [PATCH 2/8] Added tests for the `Bound::as_super` and `Bound::into_super` methods. --- src/instance.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/instance.rs b/src/instance.rs index 52cea0022fc..cceb7d1dd70 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -2420,5 +2420,43 @@ a = A() } }) } + + #[crate::pyclass(crate = "crate", subclass)] + struct BaseClass; + + trait MyClassMethods<'py>: Sized { + fn pyrepr_by_ref(&self) -> PyResult; + fn pyrepr_by_val(self) -> PyResult { + self.pyrepr_by_ref() + } + } + impl<'py> MyClassMethods<'py> for Bound<'py, BaseClass> { + fn pyrepr_by_ref(&self) -> PyResult { + self.call_method0("__repr__")?.extract() + } + } + + #[crate::pyclass(crate = "crate", extends = BaseClass)] + struct SubClass; + + #[test] + fn test_as_super() { + Python::with_gil(|py| { + let obj = Bound::new(py, (SubClass, BaseClass)).unwrap(); + let _: &Bound<'_, BaseClass> = obj.as_super(); + let _: &Bound<'_, PyAny> = obj.as_super().as_super(); + assert!(obj.as_super().pyrepr_by_ref().is_ok()); + }) + } + + #[test] + fn test_into_super() { + Python::with_gil(|py| { + let obj = Bound::new(py, (SubClass, BaseClass)).unwrap(); + let _: Bound<'_, BaseClass> = obj.clone().into_super(); + let _: Bound<'_, PyAny> = obj.clone().into_super().into_super(); + assert!(obj.into_super().pyrepr_by_val().is_ok()); + }) + } } } From aeece288b08f1b60adc91f4df9f9a032166551d6 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sun, 14 Jul 2024 15:01:55 -0500 Subject: [PATCH 3/8] Added newsfragment entry. --- newsfragments/4350.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4350.added.md diff --git a/newsfragments/4350.added.md b/newsfragments/4350.added.md new file mode 100644 index 00000000000..f9276ae0d4f --- /dev/null +++ b/newsfragments/4350.added.md @@ -0,0 +1 @@ +Added `as_super` and `into_super` methods for `Bound`. \ No newline at end of file From 33541af5aae2856a1a0afe5c95997c56657e84d2 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sun, 14 Jul 2024 15:22:48 -0500 Subject: [PATCH 4/8] Fixed newsfragment PR number. --- newsfragments/{4350.added.md => 4351.added.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename newsfragments/{4350.added.md => 4351.added.md} (100%) diff --git a/newsfragments/4350.added.md b/newsfragments/4351.added.md similarity index 100% rename from newsfragments/4350.added.md rename to newsfragments/4351.added.md From e2c4516099bb6886e4e00886de87371757fce8c6 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sun, 14 Jul 2024 15:34:16 -0500 Subject: [PATCH 5/8] Fixed doc links. --- src/instance.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/instance.rs b/src/instance.rs index cceb7d1dd70..08a295c813d 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -365,7 +365,7 @@ where /// (e.g. `#[pyclass(extends = BaseType)]`), the returned type will be /// `&Bound`. If an explicit base class was _not_ declared, the /// return value will be `&Bound` (making this method equivalent - /// to as [`as_any`]). + /// to [`as_any`]). /// /// This method is particularly useful for calling methods defined in an /// extension trait that has been implemented for `Bound`. @@ -401,6 +401,10 @@ where /// }) /// # } /// ``` + /// + /// [`as_any`]: Bound::as_any + /// [`into_super`]: Bound::into_super + /// [`borrow`]: Bound::borrow #[inline] pub fn as_super(&self) -> &Bound<'py, T::BaseType> { // a pyclass can always be safely "downcast" to its base type @@ -411,9 +415,9 @@ where /// /// If this type defined an explicit base class in its`pyclass` declaration /// (e.g. `#[pyclass(extends = BaseType)]`), the returned type will be - /// `&Bound`. If an explicit base class was _not_ declared, the - /// return value will be `&Bound` (making this method equivalent - /// to as [`as_any`]). + /// `Bound`. If an explicit base class was _not_ declared, the + /// return value will be `Bound` (making this method equivalent + /// to [`into_any`]). /// /// This method is particularly useful for calling methods defined in an /// extension trait that has been implemented for `Bound`. @@ -449,6 +453,10 @@ where /// }) /// # } /// ``` + /// + /// [`into_any`]: Bound::as_any + /// [`as_super`]: Bound::as_super + /// [`borrow`]: Bound::borrow #[inline] pub fn into_super(self) -> Bound<'py, T::BaseType> { // a pyclass can always be safely "downcast" to its base type From bc660c175e2f1e308bd36474cf53809e1b7d462d Mon Sep 17 00:00:00 2001 From: Joshua Rudolph Date: Sun, 14 Jul 2024 16:42:27 -0500 Subject: [PATCH 6/8] Fixed missing spaces in method docstrings. Co-authored-by: Lily Foote --- src/instance.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/instance.rs b/src/instance.rs index 08a295c813d..9ee4b8f4123 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -361,7 +361,7 @@ where /// Upcast this `Bound` to its base type by reference. /// - /// If this type defined an explicit base class in its`pyclass` declaration + /// If this type defined an explicit base class in its `pyclass` declaration /// (e.g. `#[pyclass(extends = BaseType)]`), the returned type will be /// `&Bound`. If an explicit base class was _not_ declared, the /// return value will be `&Bound` (making this method equivalent @@ -413,7 +413,7 @@ where /// Upcast this `Bound` to its base type by value. /// - /// If this type defined an explicit base class in its`pyclass` declaration + /// If this type defined an explicit base class in its `pyclass` declaration /// (e.g. `#[pyclass(extends = BaseType)]`), the returned type will be /// `Bound`. If an explicit base class was _not_ declared, the /// return value will be `Bound` (making this method equivalent From 752632ce3f707193feaf1bd425b23c4499159663 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sun, 14 Jul 2024 23:51:34 +0200 Subject: [PATCH 7/8] fixup docs --- src/instance.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/instance.rs b/src/instance.rs index 9ee4b8f4123..9bc184d55ed 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -422,7 +422,7 @@ where /// This method is particularly useful for calling methods defined in an /// extension trait that has been implemented for `Bound`. /// - /// See also the [`as_super`] method to upcast by value, and the + /// See also the [`as_super`] method to upcast by reference, and the /// [`PyRef::into_super`]/[`PyRefMut::into_super`] methods for upcasting a pyclass /// that has already been [`borrow`]ed. /// From 86b9e3a33a93043efd26548271559517ee1a7565 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Sun, 14 Jul 2024 23:51:44 +0200 Subject: [PATCH 8/8] fixup docs --- src/instance.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/instance.rs b/src/instance.rs index 9bc184d55ed..3b8e2529a6e 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -454,7 +454,7 @@ where /// # } /// ``` /// - /// [`into_any`]: Bound::as_any + /// [`into_any`]: Bound::into_any /// [`as_super`]: Bound::as_super /// [`borrow`]: Bound::borrow #[inline]