1 | use crate::err::{self, PyResult}; |
2 | use crate::{ffi, PyAny, PyTypeInfo, Python}; |
3 | |
4 | /// Represents a reference to a Python `type object`. |
5 | #[repr (transparent)] |
6 | pub struct PyType(PyAny); |
7 | |
8 | pyobject_native_type_core!(PyType, pyobject_native_static_type_object!(ffi::PyType_Type), #checkfunction=ffi::PyType_Check); |
9 | |
10 | impl 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)] |
60 | mod 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 | |