1use super::any::PyAnyMethods;
2use crate::conversion::IntoPyObject;
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::PyStaticExpr;
5#[cfg(feature = "experimental-inspect")]
6use crate::type_object::PyTypeInfo;
7use crate::PyErr;
8use crate::{
9 exceptions::PyTypeError, ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound,
10 types::typeobject::PyTypeMethods, Borrowed, FromPyObject, PyAny, Python,
11};
12use std::convert::Infallible;
13use std::ptr;
14
15#[repr(transparent)]
23pub struct PyBool(PyAny);
24
25pyobject_native_type!(PyBool, ffi::PyObject, pyobject_native_static_type_object!(ffi::PyBool_Type), "builtins", "bool", #checkfunction=ffi::PyBool_Check);
26
27impl PyBool {
28 #[inline]
34 pub fn new(py: Python<'_>, val: bool) -> Borrowed<'_, '_, Self> {
35 unsafe {
37 if val { ffi::Py_True() } else { ffi::Py_False() }
38 .assume_borrowed_unchecked(py)
39 .cast_unchecked()
40 }
41 }
42}
43
44#[doc(alias = "PyBool")]
50pub trait PyBoolMethods<'py>: crate::sealed::Sealed {
51 fn is_true(&self) -> bool;
53}
54
55impl<'py> PyBoolMethods<'py> for Bound<'py, PyBool> {
56 #[inline]
57 fn is_true(&self) -> bool {
58 unsafe { ptr::eq(self.as_ptr(), ffi::Py_True()) }
59 }
60}
61
62impl PartialEq<bool> for Bound<'_, PyBool> {
64 #[inline]
65 fn eq(&self, other: &bool) -> bool {
66 self.as_borrowed() == *other
67 }
68}
69
70impl PartialEq<bool> for &'_ Bound<'_, PyBool> {
72 #[inline]
73 fn eq(&self, other: &bool) -> bool {
74 self.as_borrowed() == *other
75 }
76}
77
78impl PartialEq<&'_ bool> for Bound<'_, PyBool> {
80 #[inline]
81 fn eq(&self, other: &&bool) -> bool {
82 self.as_borrowed() == **other
83 }
84}
85
86impl PartialEq<Bound<'_, PyBool>> for bool {
88 #[inline]
89 fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
90 *self == other.as_borrowed()
91 }
92}
93
94impl PartialEq<&'_ Bound<'_, PyBool>> for bool {
96 #[inline]
97 fn eq(&self, other: &&'_ Bound<'_, PyBool>) -> bool {
98 *self == other.as_borrowed()
99 }
100}
101
102impl PartialEq<Bound<'_, PyBool>> for &'_ bool {
104 #[inline]
105 fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
106 **self == other.as_borrowed()
107 }
108}
109
110impl PartialEq<bool> for Borrowed<'_, '_, PyBool> {
112 #[inline]
113 fn eq(&self, other: &bool) -> bool {
114 self.is_true() == *other
115 }
116}
117
118impl PartialEq<&bool> for Borrowed<'_, '_, PyBool> {
120 #[inline]
121 fn eq(&self, other: &&bool) -> bool {
122 self.is_true() == **other
123 }
124}
125
126impl PartialEq<Borrowed<'_, '_, PyBool>> for bool {
128 #[inline]
129 fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
130 *self == other.is_true()
131 }
132}
133
134impl PartialEq<Borrowed<'_, '_, PyBool>> for &'_ bool {
136 #[inline]
137 fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
138 **self == other.is_true()
139 }
140}
141
142impl<'py> IntoPyObject<'py> for bool {
143 type Target = PyBool;
144 type Output = Borrowed<'py, 'py, Self::Target>;
145 type Error = Infallible;
146
147 #[cfg(feature = "experimental-inspect")]
148 const OUTPUT_TYPE: PyStaticExpr = PyBool::TYPE_HINT;
149
150 #[inline]
151 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
152 Ok(PyBool::new(py, self))
153 }
154}
155
156impl<'py> IntoPyObject<'py> for &bool {
157 type Target = PyBool;
158 type Output = Borrowed<'py, 'py, Self::Target>;
159 type Error = Infallible;
160
161 #[cfg(feature = "experimental-inspect")]
162 const OUTPUT_TYPE: PyStaticExpr = bool::OUTPUT_TYPE;
163
164 #[inline]
165 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
166 (*self).into_pyobject(py)
167 }
168}
169
170impl FromPyObject<'_, '_> for bool {
174 type Error = PyErr;
175
176 #[cfg(feature = "experimental-inspect")]
177 const INPUT_TYPE: PyStaticExpr = PyBool::TYPE_HINT;
178
179 fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
180 let err = match obj.cast::<PyBool>() {
181 Ok(obj) => return Ok(obj.is_true()),
182 Err(err) => err,
183 };
184
185 let is_numpy_bool = {
186 let ty = obj.get_type();
187 ty.module().is_ok_and(|module| module == "numpy")
188 && ty
189 .name()
190 .is_ok_and(|name| name == "bool_" || name == "bool")
191 };
192
193 if is_numpy_bool {
194 let missing_conversion = |obj: Borrowed<'_, '_, PyAny>| {
195 PyTypeError::new_err(format!(
196 "object of type '{}' does not define a '__bool__' conversion",
197 obj.get_type()
198 ))
199 };
200
201 #[cfg(not(any(Py_LIMITED_API, PyPy)))]
202 unsafe {
203 let ptr = obj.as_ptr();
204
205 if let Some(tp_as_number) = (*(*ptr).ob_type).tp_as_number.as_ref() {
206 if let Some(nb_bool) = tp_as_number.nb_bool {
207 match (nb_bool)(ptr) {
208 0 => return Ok(false),
209 1 => return Ok(true),
210 _ => return Err(crate::PyErr::fetch(obj.py())),
211 }
212 }
213 }
214
215 return Err(missing_conversion(obj));
216 }
217
218 #[cfg(any(Py_LIMITED_API, PyPy))]
219 {
220 let meth = obj
221 .lookup_special(crate::intern!(obj.py(), "__bool__"))?
222 .ok_or_else(|| missing_conversion(obj))?;
223
224 let obj = meth.call0()?.cast_into::<PyBool>()?;
225 return Ok(obj.is_true());
226 }
227 }
228
229 Err(err.into())
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use crate::types::{PyAnyMethods, PyBool, PyBoolMethods};
236 use crate::IntoPyObject;
237 use crate::Python;
238
239 #[test]
240 fn test_true() {
241 Python::attach(|py| {
242 assert!(PyBool::new(py, true).is_true());
243 let t = PyBool::new(py, true);
244 assert!(t.extract::<bool>().unwrap());
245 assert!(true.into_pyobject(py).unwrap().is(&*PyBool::new(py, true)));
246 });
247 }
248
249 #[test]
250 fn test_false() {
251 Python::attach(|py| {
252 assert!(!PyBool::new(py, false).is_true());
253 let t = PyBool::new(py, false);
254 assert!(!t.extract::<bool>().unwrap());
255 assert!(false
256 .into_pyobject(py)
257 .unwrap()
258 .is(&*PyBool::new(py, false)));
259 });
260 }
261
262 #[test]
263 fn test_pybool_comparisons() {
264 Python::attach(|py| {
265 let py_bool = PyBool::new(py, true);
266 let py_bool_false = PyBool::new(py, false);
267 let rust_bool = true;
268
269 assert_eq!(*py_bool, rust_bool);
271 assert_ne!(*py_bool_false, rust_bool);
272
273 assert_eq!(*py_bool, &rust_bool);
275 assert_ne!(*py_bool_false, &rust_bool);
276
277 assert_eq!(&*py_bool, rust_bool);
279 assert_ne!(&*py_bool_false, rust_bool);
280
281 assert_eq!(&*py_bool, &rust_bool);
283 assert_ne!(&*py_bool_false, &rust_bool);
284
285 assert_eq!(rust_bool, *py_bool);
287 assert_ne!(rust_bool, *py_bool_false);
288
289 assert_eq!(rust_bool, &*py_bool);
291 assert_ne!(rust_bool, &*py_bool_false);
292
293 assert_eq!(&rust_bool, *py_bool);
295 assert_ne!(&rust_bool, *py_bool_false);
296
297 assert_eq!(&rust_bool, &*py_bool);
299 assert_ne!(&rust_bool, &*py_bool_false);
300
301 assert_eq!(py_bool, rust_bool);
303 assert_ne!(py_bool_false, rust_bool);
304
305 assert_eq!(py_bool, &rust_bool);
307 assert_ne!(py_bool_false, &rust_bool);
308
309 assert_eq!(rust_bool, py_bool);
311 assert_ne!(rust_bool, py_bool_false);
312
313 assert_eq!(&rust_bool, py_bool);
315 assert_ne!(&rust_bool, py_bool_false);
316 assert_eq!(py_bool, rust_bool);
317 assert_ne!(py_bool_false, rust_bool);
318 })
319 }
320}