pyo3/
pyclass.rs

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