pyo3/
type_object.rs

1//! Python type object information
2
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::types::any::PyAnyMethods;
5use crate::types::{PyAny, PyType};
6use crate::{ffi, Bound, Python};
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.
38pub unsafe trait PyTypeInfo: Sized {
39    /// Class name.
40    const NAME: &'static str;
41
42    /// Module name, if any.
43    const MODULE: Option<&'static str>;
44
45    /// Returns the PyTypeObject instance for this type.
46    fn type_object_raw(py: Python<'_>) -> *mut ffi::PyTypeObject;
47
48    /// Returns the safe abstraction over the type object.
49    #[inline]
50    fn type_object(py: Python<'_>) -> Bound<'_, PyType> {
51        // Making the borrowed object `Bound` is necessary for soundness reasons. It's an extreme
52        // edge case, but arbitrary Python code _could_ change the __class__ of an object and cause
53        // the type object to be freed.
54        //
55        // By making `Bound` we assume ownership which is then safe against races.
56        unsafe {
57            Self::type_object_raw(py)
58                .cast::<ffi::PyObject>()
59                .assume_borrowed_unchecked(py)
60                .to_owned()
61                .downcast_into_unchecked()
62        }
63    }
64
65    /// Deprecated name for [`PyTypeInfo::type_object`].
66    #[deprecated(since = "0.23.0", note = "renamed to `PyTypeInfo::type_object`")]
67    #[inline]
68    fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType> {
69        Self::type_object(py)
70    }
71
72    /// Checks if `object` is an instance of this type or a subclass of this type.
73    #[inline]
74    fn is_type_of(object: &Bound<'_, PyAny>) -> bool {
75        unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 }
76    }
77
78    /// Deprecated name for [`PyTypeInfo::is_type_of`].
79    #[deprecated(since = "0.23.0", note = "renamed to `PyTypeInfo::is_type_of`")]
80    #[inline]
81    fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
82        Self::is_type_of(object)
83    }
84
85    /// Checks if `object` is an instance of this type.
86    #[inline]
87    fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool {
88        unsafe { ffi::Py_TYPE(object.as_ptr()) == Self::type_object_raw(object.py()) }
89    }
90
91    /// Deprecated name for [`PyTypeInfo::is_exact_type_of`].
92    #[deprecated(since = "0.23.0", note = "renamed to `PyTypeInfo::is_exact_type_of`")]
93    #[inline]
94    fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
95        Self::is_exact_type_of(object)
96    }
97}
98
99/// Implemented by types which can be used as a concrete Python type inside `Py<T>` smart pointers.
100pub trait PyTypeCheck {
101    /// Name of self. This is used in error messages, for example.
102    const NAME: &'static str;
103
104    /// Checks if `object` is an instance of `Self`, which may include a subtype.
105    ///
106    /// This should be equivalent to the Python expression `isinstance(object, Self)`.
107    fn type_check(object: &Bound<'_, PyAny>) -> bool;
108}
109
110impl<T> PyTypeCheck for T
111where
112    T: PyTypeInfo,
113{
114    const NAME: &'static str = <T as PyTypeInfo>::NAME;
115
116    #[inline]
117    fn type_check(object: &Bound<'_, PyAny>) -> bool {
118        T::is_type_of(object)
119    }
120}