Skip to main content

pyo3/
pyclass.rs

1//! `PyClass` and related traits.
2use crate::{ffi, impl_::pyclass::PyClassImpl, PyTypeInfo};
3use core::{cmp::Ordering, ffi::c_int};
4
5mod create_type_object;
6pub(crate) mod gc;
7mod guard;
8
9pub(crate) use self::create_type_object::{create_type_object, PyClassTypeObject};
10
11pub use self::gc::{PyTraverseError, PyVisit};
12pub use self::guard::{
13    PyClassGuard, PyClassGuardError, PyClassGuardMap, PyClassGuardMut, PyClassGuardMutError,
14    PyClassGuardMutSuper,
15};
16
17/// Types that can be used as Python classes.
18///
19/// The `#[pyclass]` attribute implements this trait for your Rust struct -
20/// you shouldn't implement this trait directly.
21pub trait PyClass: PyTypeInfo + PyClassImpl {
22    /// Name of the class.
23    ///
24    /// This can be set via `#[pyclass(name = "...")]`, otherwise it defaults to the Rust type name.
25    const NAME: &'static str;
26
27    /// Whether the pyclass is frozen.
28    ///
29    /// This can be enabled via `#[pyclass(frozen)]`.
30    type Frozen: Frozen;
31}
32
33/// Operators for the `__richcmp__` method
34#[derive(Debug, Clone, Copy)]
35pub enum CompareOp {
36    /// The *less than* operator.
37    Lt = ffi::Py_LT as isize,
38    /// The *less than or equal to* operator.
39    Le = ffi::Py_LE as isize,
40    /// The equality operator.
41    Eq = ffi::Py_EQ as isize,
42    /// The *not equal to* operator.
43    Ne = ffi::Py_NE as isize,
44    /// The *greater than* operator.
45    Gt = ffi::Py_GT as isize,
46    /// The *greater than or equal to* operator.
47    Ge = ffi::Py_GE as isize,
48}
49
50impl CompareOp {
51    /// Conversion from the C enum.
52    pub fn from_raw(op: c_int) -> Option<Self> {
53        match op {
54            ffi::Py_LT => Some(CompareOp::Lt),
55            ffi::Py_LE => Some(CompareOp::Le),
56            ffi::Py_EQ => Some(CompareOp::Eq),
57            ffi::Py_NE => Some(CompareOp::Ne),
58            ffi::Py_GT => Some(CompareOp::Gt),
59            ffi::Py_GE => Some(CompareOp::Ge),
60            _ => None,
61        }
62    }
63
64    /// Returns if a Rust [`core::cmp::Ordering`] matches this ordering query.
65    ///
66    /// Usage example:
67    ///
68    /// ```rust,no_run
69    /// # use pyo3::prelude::*;
70    /// # use pyo3::class::basic::CompareOp;
71    ///
72    /// #[pyclass]
73    /// struct Size {
74    ///     size: usize,
75    /// }
76    ///
77    /// #[pymethods]
78    /// impl Size {
79    ///     fn __richcmp__(&self, other: &Size, op: CompareOp) -> bool {
80    ///         op.matches(self.size.cmp(&other.size))
81    ///     }
82    /// }
83    /// ```
84    pub fn matches(&self, result: Ordering) -> bool {
85        match self {
86            CompareOp::Eq => result == Ordering::Equal,
87            CompareOp::Ne => result != Ordering::Equal,
88            CompareOp::Lt => result == Ordering::Less,
89            CompareOp::Le => result != Ordering::Greater,
90            CompareOp::Gt => result == Ordering::Greater,
91            CompareOp::Ge => result != Ordering::Less,
92        }
93    }
94}
95
96/// A workaround for [associated const equality](https://github.com/rust-lang/rust/issues/92827).
97///
98/// This serves to have True / False values in the [`PyClass`] trait's `Frozen` type.
99#[doc(hidden)]
100pub mod boolean_struct {
101    pub(crate) mod private {
102        use super::*;
103
104        /// A way to "seal" the boolean traits.
105        pub trait Boolean {
106            const VALUE: bool;
107        }
108
109        impl Boolean for True {
110            const VALUE: bool = true;
111        }
112        impl Boolean for False {
113            const VALUE: bool = false;
114        }
115    }
116
117    pub struct True(());
118    pub struct False(());
119}
120
121/// A trait which is used to describe whether a `#[pyclass]` is frozen.
122#[doc(hidden)]
123pub trait Frozen: boolean_struct::private::Boolean {}
124
125impl Frozen for boolean_struct::True {}
126impl Frozen for boolean_struct::False {}
127
128mod tests {
129    #[test]
130    fn test_compare_op_matches() {
131        use super::CompareOp;
132        use core::cmp::Ordering;
133
134        assert!(CompareOp::Eq.matches(Ordering::Equal));
135        assert!(CompareOp::Ne.matches(Ordering::Less));
136        assert!(CompareOp::Ge.matches(Ordering::Greater));
137        assert!(CompareOp::Gt.matches(Ordering::Greater));
138        assert!(CompareOp::Le.matches(Ordering::Equal));
139        assert!(CompareOp::Lt.matches(Ordering::Less));
140    }
141}