From d21045cbc1a2df7edff12bc7d7911457ce3d408d Mon Sep 17 00:00:00 2001 From: Cheuk Ting Ho Date: Sat, 25 May 2024 23:39:48 +0100 Subject: [PATCH] adding new getter for type obj (#4197) * adding new getter for type obj * fixing limited api build * fix formating ssues from clippy * add changelog info * Update newsfragments/4197.added.md Co-authored-by: David Hewitt * Update src/types/typeobject.rs Co-authored-by: David Hewitt * Update src/types/typeobject.rs Co-authored-by: David Hewitt * Update src/types/typeobject.rs Co-authored-by: David Hewitt * Update src/types/typeobject.rs Co-authored-by: David Hewitt * using uncheck downcast * fix formating * move import * Update src/types/typeobject.rs Co-authored-by: Matt Hooks * Update src/types/typeobject.rs Co-authored-by: Matt Hooks --------- Co-authored-by: David Hewitt Co-authored-by: Matt Hooks --- newsfragments/4197.added.md | 1 + src/types/typeobject.rs | 97 ++++++++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 newsfragments/4197.added.md diff --git a/newsfragments/4197.added.md b/newsfragments/4197.added.md new file mode 100644 index 00000000..5652028c --- /dev/null +++ b/newsfragments/4197.added.md @@ -0,0 +1 @@ +Add `PyTypeMethods::mro` and `PyTypeMethods::bases`. diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index e6c4a218..9c2d5c5f 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -1,6 +1,7 @@ use crate::err::{self, PyResult}; use crate::instance::Borrowed; use crate::types::any::PyAnyMethods; +use crate::types::PyTuple; #[cfg(feature = "gil-refs")] use crate::PyNativeType; use crate::{ffi, Bound, PyAny, PyTypeInfo, Python}; @@ -127,6 +128,16 @@ pub trait PyTypeMethods<'py>: crate::sealed::Sealed { fn is_subclass_of(&self) -> PyResult where T: PyTypeInfo; + + /// Return the method resolution order for this type. + /// + /// Equivalent to the Python expression `self.__mro__`. + fn mro(&self) -> Bound<'py, PyTuple>; + + /// Return Python bases + /// + /// Equivalent to the Python expression `self.__bases__`. + fn bases(&self) -> Bound<'py, PyTuple>; } impl<'py> PyTypeMethods<'py> for Bound<'py, PyType> { @@ -177,6 +188,48 @@ impl<'py> PyTypeMethods<'py> for Bound<'py, PyType> { { self.is_subclass(&T::type_object_bound(self.py())) } + + fn mro(&self) -> Bound<'py, PyTuple> { + #[cfg(any(Py_LIMITED_API, PyPy))] + let mro = self + .getattr(intern!(self.py(), "__mro__")) + .expect("Cannot get `__mro__` from object.") + .extract() + .expect("Unexpected type in `__mro__` attribute."); + + #[cfg(not(any(Py_LIMITED_API, PyPy)))] + let mro = unsafe { + use crate::ffi_ptr_ext::FfiPtrExt; + (*self.as_type_ptr()) + .tp_mro + .assume_borrowed(self.py()) + .to_owned() + .downcast_into_unchecked() + }; + + mro + } + + fn bases(&self) -> Bound<'py, PyTuple> { + #[cfg(any(Py_LIMITED_API, PyPy))] + let bases = self + .getattr(intern!(self.py(), "__bases__")) + .expect("Cannot get `__bases__` from object.") + .extract() + .expect("Unexpected type in `__bases__` attribute."); + + #[cfg(not(any(Py_LIMITED_API, PyPy)))] + let bases = unsafe { + use crate::ffi_ptr_ext::FfiPtrExt; + (*self.as_type_ptr()) + .tp_bases + .assume_borrowed(self.py()) + .to_owned() + .downcast_into_unchecked() + }; + + bases + } } impl<'a> Borrowed<'a, '_, PyType> { @@ -215,8 +268,8 @@ impl<'a> Borrowed<'a, '_, PyType> { #[cfg(test)] mod tests { - use crate::types::typeobject::PyTypeMethods; - use crate::types::{PyBool, PyLong}; + use crate::types::{PyAnyMethods, PyBool, PyInt, PyLong, PyTuple, PyTypeMethods}; + use crate::PyAny; use crate::Python; #[test] @@ -237,4 +290,44 @@ mod tests { .unwrap()); }); } + + #[test] + fn test_mro() { + Python::with_gil(|py| { + assert!(py + .get_type_bound::() + .mro() + .eq(PyTuple::new_bound( + py, + [ + py.get_type_bound::(), + py.get_type_bound::(), + py.get_type_bound::() + ] + )) + .unwrap()); + }); + } + + #[test] + fn test_bases_bool() { + Python::with_gil(|py| { + assert!(py + .get_type_bound::() + .bases() + .eq(PyTuple::new_bound(py, [py.get_type_bound::()])) + .unwrap()); + }); + } + + #[test] + fn test_bases_object() { + Python::with_gil(|py| { + assert!(py + .get_type_bound::() + .bases() + .eq(PyTuple::empty_bound(py)) + .unwrap()); + }); + } }