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