1 | //! Python type object information |
2 | |
3 | use crate::ffi_ptr_ext::FfiPtrExt; |
4 | use crate::types::any::PyAnyMethods; |
5 | use crate::types::{PyAny, PyType}; |
6 | use crate::{ffi, Bound, Python}; |
7 | use std::ptr; |
8 | |
9 | /// `T: PyLayout<U>` represents that `T` is a concrete representation of `U` in the Python heap. |
10 | /// E.g., `PyClassObject` is a concrete representation of all `pyclass`es, and `ffi::PyObject` |
11 | /// is of `PyAny`. |
12 | /// |
13 | /// This trait is intended to be used internally. |
14 | /// |
15 | /// # Safety |
16 | /// |
17 | /// This trait must only be implemented for types which represent valid layouts of Python objects. |
18 | pub unsafe trait PyLayout<T> {} |
19 | |
20 | /// `T: PySizedLayout<U>` represents that `T` is not a instance of |
21 | /// [`PyVarObject`](https://docs.python.org/3/c-api/structures.html#c.PyVarObject). |
22 | /// |
23 | /// In addition, that `T` is a concrete representation of `U`. |
24 | pub trait PySizedLayout<T>: PyLayout<T> + Sized {} |
25 | |
26 | /// Python type information. |
27 | /// All Python native types (e.g., `PyDict`) and `#[pyclass]` structs implement this trait. |
28 | /// |
29 | /// This trait is marked unsafe because: |
30 | /// - specifying the incorrect layout can lead to memory errors |
31 | /// - the return value of type_object must always point to the same PyTypeObject instance |
32 | /// |
33 | /// It is safely implemented by the `pyclass` macro. |
34 | /// |
35 | /// # Safety |
36 | /// |
37 | /// Implementations must provide an implementation for `type_object_raw` which infallibly produces a |
38 | /// non-null pointer to the corresponding Python type object. |
39 | pub unsafe trait PyTypeInfo: Sized { |
40 | /// Class name. |
41 | const NAME: &'static str; |
42 | |
43 | /// Module name, if any. |
44 | const MODULE: Option<&'static str>; |
45 | |
46 | /// Returns the PyTypeObject instance for this type. |
47 | fn type_object_raw(py: Python<'_>) -> *mut ffi::PyTypeObject; |
48 | |
49 | /// Returns the safe abstraction over the type object. |
50 | #[inline ] |
51 | fn type_object(py: Python<'_>) -> Bound<'_, PyType> { |
52 | // Making the borrowed object `Bound` is necessary for soundness reasons. It's an extreme |
53 | // edge case, but arbitrary Python code _could_ change the __class__ of an object and cause |
54 | // the type object to be freed. |
55 | // |
56 | // By making `Bound` we assume ownership which is then safe against races. |
57 | unsafe { |
58 | Self::type_object_raw(py) |
59 | .cast::<ffi::PyObject>() |
60 | .assume_borrowed_unchecked(py) |
61 | .to_owned() |
62 | .downcast_into_unchecked() |
63 | } |
64 | } |
65 | |
66 | /// Deprecated name for [`PyTypeInfo::type_object`]. |
67 | #[deprecated (since = "0.23.0" , note = "renamed to `PyTypeInfo::type_object`" )] |
68 | #[inline ] |
69 | fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType> { |
70 | Self::type_object(py) |
71 | } |
72 | |
73 | /// Checks if `object` is an instance of this type or a subclass of this type. |
74 | #[inline ] |
75 | fn is_type_of(object: &Bound<'_, PyAny>) -> bool { |
76 | unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 } |
77 | } |
78 | |
79 | /// Deprecated name for [`PyTypeInfo::is_type_of`]. |
80 | #[deprecated (since = "0.23.0" , note = "renamed to `PyTypeInfo::is_type_of`" )] |
81 | #[inline ] |
82 | fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool { |
83 | Self::is_type_of(object) |
84 | } |
85 | |
86 | /// Checks if `object` is an instance of this type. |
87 | #[inline ] |
88 | fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool { |
89 | unsafe { |
90 | ptr::eq( |
91 | ffi::Py_TYPE(object.as_ptr()), |
92 | Self::type_object_raw(object.py()), |
93 | ) |
94 | } |
95 | } |
96 | |
97 | /// Deprecated name for [`PyTypeInfo::is_exact_type_of`]. |
98 | #[deprecated (since = "0.23.0" , note = "renamed to `PyTypeInfo::is_exact_type_of`" )] |
99 | #[inline ] |
100 | fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool { |
101 | Self::is_exact_type_of(object) |
102 | } |
103 | } |
104 | |
105 | /// Implemented by types which can be used as a concrete Python type inside `Py<T>` smart pointers. |
106 | pub trait PyTypeCheck { |
107 | /// Name of self. This is used in error messages, for example. |
108 | const NAME: &'static str; |
109 | |
110 | /// Checks if `object` is an instance of `Self`, which may include a subtype. |
111 | /// |
112 | /// This should be equivalent to the Python expression `isinstance(object, Self)`. |
113 | fn type_check(object: &Bound<'_, PyAny>) -> bool; |
114 | } |
115 | |
116 | impl<T> PyTypeCheck for T |
117 | where |
118 | T: PyTypeInfo, |
119 | { |
120 | const NAME: &'static str = <T as PyTypeInfo>::NAME; |
121 | |
122 | #[inline ] |
123 | fn type_check(object: &Bound<'_, PyAny>) -> bool { |
124 | T::is_type_of(object) |
125 | } |
126 | } |
127 | |