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