Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding new getter for type obj #4197

Merged
merged 14 commits into from
May 25, 2024
1 change: 1 addition & 0 deletions newsfragments/4197.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `PyTypeMethods::mro` and `PyTypeMethods::bases`.
97 changes: 95 additions & 2 deletions src/types/typeobject.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::err::{self, PyResult};
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
use crate::ffi_ptr_ext::FfiPtrExt;
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};
Expand Down Expand Up @@ -127,6 +130,16 @@ pub trait PyTypeMethods<'py>: crate::sealed::Sealed {
fn is_subclass_of<T>(&self) -> PyResult<bool>
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> {
Expand Down Expand Up @@ -177,6 +190,46 @@ 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("Cannot convert to Rust object.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very minor: The expect message here is a little awkward IMO. There is no broad "Rust object" that we're trying to convert to, instead we're converting from the PyObject down into PyTuple.

My suggestion would be something like this instead

Unexpected type in __mro__ attribute.

Cheukting marked this conversation as resolved.
Show resolved Hide resolved

#[cfg(not(any(Py_LIMITED_API, PyPy)))]
let mro = unsafe {
(*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("Cannot convert to Rust object.");
Cheukting marked this conversation as resolved.
Show resolved Hide resolved

#[cfg(not(any(Py_LIMITED_API, PyPy)))]
let bases = unsafe {
(*self.as_type_ptr())
.tp_bases
.assume_borrowed(self.py())
.to_owned()
.downcast_into_unchecked()
};

bases
}
}

impl<'a> Borrowed<'a, '_, PyType> {
Expand Down Expand Up @@ -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]
Expand All @@ -237,4 +290,44 @@ mod tests {
.unwrap());
});
}

#[test]
fn test_mro() {
Python::with_gil(|py| {
assert!(py
.get_type_bound::<PyBool>()
.mro()
.eq(PyTuple::new_bound(
py,
[
py.get_type_bound::<PyBool>(),
py.get_type_bound::<PyInt>(),
py.get_type_bound::<PyAny>()
]
))
.unwrap());
});
}

#[test]
fn test_bases_bool() {
Python::with_gil(|py| {
assert!(py
.get_type_bound::<PyBool>()
.bases()
.eq(PyTuple::new_bound(py, [py.get_type_bound::<PyInt>()]))
.unwrap());
});
}

#[test]
fn test_bases_object() {
Python::with_gil(|py| {
assert!(py
.get_type_bound::<PyAny>()
.bases()
.eq(PyTuple::empty_bound(py))
.unwrap());
});
}
}
Loading