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