pyo3/type_object.rs
1//! Python type object information
2
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::types::{PyAny, PyType};
5use crate::{ffi, Bound, Python};
6use std::ptr;
7
8/// `T: PyLayout<U>` represents that `T` is a concrete representation of `U` in the Python heap.
9/// E.g., `PyClassObject` is a concrete representation of all `pyclass`es, and `ffi::PyObject`
10/// is of `PyAny`.
11///
12/// This trait is intended to be used internally.
13///
14/// # Safety
15///
16/// This trait must only be implemented for types which represent valid layouts of Python objects.
17pub unsafe trait PyLayout<T> {}
18
19/// `T: PySizedLayout<U>` represents that `T` is not a instance of
20/// [`PyVarObject`](https://docs.python.org/3/c-api/structures.html#c.PyVarObject).
21///
22/// In addition, that `T` is a concrete representation of `U`.
23pub trait PySizedLayout<T>: PyLayout<T> + Sized {}
24
25/// Python type information.
26/// All Python native types (e.g., `PyDict`) and `#[pyclass]` structs implement this trait.
27///
28/// This trait is marked unsafe because:
29/// - specifying the incorrect layout can lead to memory errors
30/// - the return value of type_object must always point to the same PyTypeObject instance
31///
32/// It is safely implemented by the `pyclass` macro.
33///
34/// # Safety
35///
36/// Implementations must provide an implementation for `type_object_raw` which infallibly produces a
37/// non-null pointer to the corresponding Python type object.
38///
39/// `is_type_of` must only return true for objects which can safely be treated as instances of `Self`.
40///
41/// `is_exact_type_of` must only return true for objects whose type is exactly `Self`.
42pub unsafe trait PyTypeInfo: Sized {
43 /// Class name.
44 const NAME: &'static str;
45
46 /// Module name, if any.
47 const MODULE: Option<&'static str>;
48
49 /// Provides the full python type paths.
50 #[cfg(feature = "experimental-inspect")]
51 const PYTHON_TYPE: &'static str = "typing.Any";
52
53 /// Returns the PyTypeObject instance for this type.
54 fn type_object_raw(py: Python<'_>) -> *mut ffi::PyTypeObject;
55
56 /// Returns the safe abstraction over the type object.
57 #[inline]
58 fn type_object(py: Python<'_>) -> Bound<'_, PyType> {
59 // Making the borrowed object `Bound` is necessary for soundness reasons. It's an extreme
60 // edge case, but arbitrary Python code _could_ change the __class__ of an object and cause
61 // the type object to be freed.
62 //
63 // By making `Bound` we assume ownership which is then safe against races.
64 unsafe {
65 Self::type_object_raw(py)
66 .cast::<ffi::PyObject>()
67 .assume_borrowed_unchecked(py)
68 .to_owned()
69 .cast_into_unchecked()
70 }
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 /// Checks if `object` is an instance of this type.
80 #[inline]
81 fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool {
82 unsafe {
83 ptr::eq(
84 ffi::Py_TYPE(object.as_ptr()),
85 Self::type_object_raw(object.py()),
86 )
87 }
88 }
89}
90
91/// Implemented by types which can be used as a concrete Python type inside `Py<T>` smart pointers.
92///
93/// # Safety
94///
95/// This trait is used to determine whether [`Bound::cast`] and similar functions can safely cast
96/// to a concrete type. The implementor is responsible for ensuring that `type_check` only returns
97/// true for objects which can safely be treated as Python instances of `Self`.
98pub unsafe trait PyTypeCheck {
99 /// Name of self. This is used in error messages, for example.
100 #[deprecated(
101 since = "0.27.0",
102 note = "Use ::classinfo_object() instead and format the type name at runtime. Note that using built-in cast features is often better than manual PyTypeCheck usage."
103 )]
104 const NAME: &'static str;
105
106 /// Provides the full python type of the allowed values.
107 #[cfg(feature = "experimental-inspect")]
108 const PYTHON_TYPE: &'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 /// Returns the expected type as a possible argument for the `isinstance` and `issubclass` function.
116 ///
117 /// It may be a single type or a tuple of types.
118 fn classinfo_object(py: Python<'_>) -> Bound<'_, PyAny>;
119}
120
121unsafe impl<T> PyTypeCheck for T
122where
123 T: PyTypeInfo,
124{
125 const NAME: &'static str = T::NAME;
126
127 #[cfg(feature = "experimental-inspect")]
128 const PYTHON_TYPE: &'static str = <T as PyTypeInfo>::PYTHON_TYPE;
129
130 #[inline]
131 fn type_check(object: &Bound<'_, PyAny>) -> bool {
132 T::is_type_of(object)
133 }
134
135 #[inline]
136 fn classinfo_object(py: Python<'_>) -> Bound<'_, PyAny> {
137 T::type_object(py).into_any()
138 }
139}