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