Skip to main content

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