1use crate::err::{self, PyResult};
2use crate::{ffi, PyAny, PyTypeInfo, Python};
3
4/// Represents a reference to a Python `type object`.
5#[repr(transparent)]
6pub struct PyType(PyAny);
7
8pyobject_native_type_core!(PyType, pyobject_native_static_type_object!(ffi::PyType_Type), #checkfunction=ffi::PyType_Check);
9
10impl PyType {
11 /// Creates a new type object.
12 #[inline]
13 pub fn new<T: PyTypeInfo>(py: Python<'_>) -> &PyType {
14 T::type_object(py)
15 }
16
17 /// Retrieves the underlying FFI pointer associated with this Python object.
18 #[inline]
19 pub fn as_type_ptr(&self) -> *mut ffi::PyTypeObject {
20 self.as_ptr() as *mut ffi::PyTypeObject
21 }
22
23 /// Retrieves the `PyType` instance for the given FFI pointer.
24 ///
25 /// # Safety
26 /// - The pointer must be non-null.
27 /// - The pointer must be valid for the entire of the lifetime for which the reference is used.
28 #[inline]
29 pub unsafe fn from_type_ptr(py: Python<'_>, p: *mut ffi::PyTypeObject) -> &PyType {
30 py.from_borrowed_ptr(p as *mut ffi::PyObject)
31 }
32
33 /// Gets the name of the `PyType`.
34 pub fn name(&self) -> PyResult<&str> {
35 self.getattr(intern!(self.py(), "__qualname__"))?.extract()
36 }
37
38 /// Checks whether `self` is a subclass of `other`.
39 ///
40 /// Equivalent to the Python expression `issubclass(self, other)`.
41 pub fn is_subclass(&self, other: &PyAny) -> PyResult<bool> {
42 let result = unsafe { ffi::PyObject_IsSubclass(self.as_ptr(), other.as_ptr()) };
43 err::error_on_minusone(self.py(), result)?;
44 Ok(result == 1)
45 }
46
47 /// Checks whether `self` is a subclass of type `T`.
48 ///
49 /// Equivalent to the Python expression `issubclass(self, T)`, if the type
50 /// `T` is known at compile time.
51 pub fn is_subclass_of<T>(&self) -> PyResult<bool>
52 where
53 T: PyTypeInfo,
54 {
55 self.is_subclass(T::type_object(self.py()))
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use crate::types::{PyBool, PyLong};
62 use crate::Python;
63
64 #[test]
65 fn test_type_is_subclass() {
66 Python::with_gil(|py| {
67 let bool_type = py.get_type::<PyBool>();
68 let long_type = py.get_type::<PyLong>();
69 assert!(bool_type.is_subclass(long_type).unwrap());
70 });
71 }
72
73 #[test]
74 fn test_type_is_subclass_of() {
75 Python::with_gil(|py| {
76 assert!(py.get_type::<PyBool>().is_subclass_of::<PyLong>().unwrap());
77 });
78 }
79}
80