From 291145fd20e47e1e3aa9ed84a303f690ab386298 Mon Sep 17 00:00:00 2001 From: Lily Foote Date: Sat, 17 Feb 2024 17:41:32 +0000 Subject: [PATCH] Add PyTypeMethods trait with qualname method This allows calling `Bound<'py, PyType>::qualname()`. --- src/err/mod.rs | 3 ++- src/prelude.rs | 1 + src/types/mod.rs | 2 +- src/types/typeobject.rs | 37 ++++++++++++++++++++++++++++++++++++- tests/test_methods.rs | 2 +- 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/err/mod.rs b/src/err/mod.rs index b5cd02c0954..f891c3cd8e9 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -946,10 +946,11 @@ struct PyDowncastErrorArguments { impl PyErrArguments for PyDowncastErrorArguments { fn arguments(self, py: Python<'_>) -> PyObject { + use crate::types::typeobject::PyTypeMethods; format!( "'{}' object cannot be converted to '{}'", self.from - .as_ref(py) + .bind(py) .qualname() .as_deref() .unwrap_or(""), diff --git a/src/prelude.rs b/src/prelude.rs index 47951aedcce..3ac239f49e1 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::typeobject::PyTypeMethods; diff --git a/src/types/mod.rs b/src/types/mod.rs index da22b30729f..46909c880bb 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -309,4 +309,4 @@ mod slice; pub(crate) mod string; pub(crate) mod traceback; pub(crate) mod tuple; -mod typeobject; +pub(crate) mod typeobject; diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index 50eeaa7153d..8528bb8af7f 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -1,5 +1,5 @@ use crate::err::{self, PyResult}; -use crate::{ffi, PyAny, PyTypeInfo, Python}; +use crate::{ffi, Bound, PyAny, PyTypeInfo, Python}; use std::borrow::Cow; #[cfg(not(any(Py_LIMITED_API, PyPy)))] use std::ffi::CStr; @@ -108,6 +108,41 @@ impl PyType { } } +/// Implementation of functionality for [`PyType`]. +/// +/// These methods are defined for the `Bound<'py, PyType>` 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`. +pub trait PyTypeMethods<'py> { + /// Gets the [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name) of the `PyType`. + fn qualname(&self) -> PyResult; +} + +impl<'py> PyTypeMethods<'py> for Bound<'py, PyType> { + fn qualname(&self) -> PyResult { + use crate::types::any::PyAnyMethods; + #[cfg(any(Py_LIMITED_API, PyPy, not(Py_3_11)))] + let name = self + .as_any() + .getattr(intern!(self.py(), "__qualname__"))? + .extract(); + + #[cfg(not(any(Py_LIMITED_API, PyPy, not(Py_3_11))))] + let name = { + use crate::ffi_ptr_ext::FfiPtrExt; + + let obj = unsafe { + ffi::PyType_GetQualName(self.as_ptr() as *mut ffi::PyTypeObject) + .assume_owned_or_err(self.py())? + }; + + obj.extract() + }; + + name + } +} + #[cfg(test)] mod tests { use crate::types::{PyBool, PyLong}; diff --git a/tests/test_methods.rs b/tests/test_methods.rs index 2114ead25c4..114ae5b8db3 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -87,7 +87,7 @@ impl ClassMethod { fn method_owned(cls: Py) -> PyResult { Ok(format!( "{}.method_owned()!", - Python::with_gil(|gil| cls.as_ref(gil).qualname())? + Python::with_gil(|gil| cls.bind(gil).qualname())? )) } }