Skip to content

Commit

Permalink
adding new getter for type obj (#4197)
Browse files Browse the repository at this point in the history
* 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 <mail@davidhewitt.dev>

* Update src/types/typeobject.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Update src/types/typeobject.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Update src/types/typeobject.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Update src/types/typeobject.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* using uncheck downcast

* fix formating

* move import

* Update src/types/typeobject.rs

Co-authored-by: Matt Hooks <me@matthooks.com>

* Update src/types/typeobject.rs

Co-authored-by: Matt Hooks <me@matthooks.com>

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Co-authored-by: Matt Hooks <me@matthooks.com>
  • Loading branch information
3 people committed May 25, 2024
1 parent 2c654b2 commit d21045c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 2 deletions.
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,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};
Expand Down Expand Up @@ -127,6 +128,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 +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> {
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());
});
}
}

0 comments on commit d21045c

Please sign in to comment.