1#[cfg(feature = "experimental-inspect")]
2use crate::inspect::types::TypeInfo;
3#[cfg(feature = "gil-refs")]
4use crate::PyNativeType;
5use crate::{
6 exceptions::PyTypeError, ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound,
7 types::typeobject::PyTypeMethods, Borrowed, FromPyObject, IntoPy, PyAny, PyObject, PyResult,
8 Python, ToPyObject,
9};
10
11use super::any::PyAnyMethods;
12
13#[repr(transparent)]
21pub struct PyBool(PyAny);
22
23pyobject_native_type!(PyBool, ffi::PyObject, pyobject_native_static_type_object!(ffi::PyBool_Type), #checkfunction=ffi::PyBool_Check);
24
25impl PyBool {
26 #[inline]
32 pub fn new_bound(py: Python<'_>, val: bool) -> Borrowed<'_, '_, Self> {
33 unsafe {
34 if val { ffi::Py_True() } else { ffi::Py_False() }
35 .assume_borrowed(py)
36 .downcast_unchecked()
37 }
38 }
39}
40
41#[cfg(feature = "gil-refs")]
42impl PyBool {
43 #[deprecated(
45 since = "0.21.0",
46 note = "`PyBool::new` will be replaced by `PyBool::new_bound` in a future PyO3 version"
47 )]
48 #[inline]
49 pub fn new(py: Python<'_>, val: bool) -> &PyBool {
50 #[allow(deprecated)]
51 unsafe {
52 py.from_borrowed_ptr(if val { ffi::Py_True() } else { ffi::Py_False() })
53 }
54 }
55
56 #[inline]
58 pub fn is_true(&self) -> bool {
59 self.as_borrowed().is_true()
60 }
61}
62
63#[doc(alias = "PyBool")]
69pub trait PyBoolMethods<'py>: crate::sealed::Sealed {
70 fn is_true(&self) -> bool;
72}
73
74impl<'py> PyBoolMethods<'py> for Bound<'py, PyBool> {
75 #[inline]
76 fn is_true(&self) -> bool {
77 self.as_ptr() == unsafe { crate::ffi::Py_True() }
78 }
79}
80
81impl PartialEq<bool> for Bound<'_, PyBool> {
83 #[inline]
84 fn eq(&self, other: &bool) -> bool {
85 self.as_borrowed() == *other
86 }
87}
88
89impl PartialEq<bool> for &'_ Bound<'_, PyBool> {
91 #[inline]
92 fn eq(&self, other: &bool) -> bool {
93 self.as_borrowed() == *other
94 }
95}
96
97impl PartialEq<&'_ bool> for Bound<'_, PyBool> {
99 #[inline]
100 fn eq(&self, other: &&bool) -> bool {
101 self.as_borrowed() == **other
102 }
103}
104
105impl PartialEq<Bound<'_, PyBool>> for bool {
107 #[inline]
108 fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
109 *self == other.as_borrowed()
110 }
111}
112
113impl PartialEq<&'_ Bound<'_, PyBool>> for bool {
115 #[inline]
116 fn eq(&self, other: &&'_ Bound<'_, PyBool>) -> bool {
117 *self == other.as_borrowed()
118 }
119}
120
121impl PartialEq<Bound<'_, PyBool>> for &'_ bool {
123 #[inline]
124 fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
125 **self == other.as_borrowed()
126 }
127}
128
129impl PartialEq<bool> for Borrowed<'_, '_, PyBool> {
131 #[inline]
132 fn eq(&self, other: &bool) -> bool {
133 self.is_true() == *other
134 }
135}
136
137impl PartialEq<&bool> for Borrowed<'_, '_, PyBool> {
139 #[inline]
140 fn eq(&self, other: &&bool) -> bool {
141 self.is_true() == **other
142 }
143}
144
145impl PartialEq<Borrowed<'_, '_, PyBool>> for bool {
147 #[inline]
148 fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
149 *self == other.is_true()
150 }
151}
152
153impl PartialEq<Borrowed<'_, '_, PyBool>> for &'_ bool {
155 #[inline]
156 fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
157 **self == other.is_true()
158 }
159}
160
161impl ToPyObject for bool {
163 #[inline]
164 fn to_object(&self, py: Python<'_>) -> PyObject {
165 unsafe {
166 PyObject::from_borrowed_ptr(
167 py,
168 if *self {
169 ffi::Py_True()
170 } else {
171 ffi::Py_False()
172 },
173 )
174 }
175 }
176}
177
178impl IntoPy<PyObject> for bool {
179 #[inline]
180 fn into_py(self, py: Python<'_>) -> PyObject {
181 PyBool::new_bound(py, self).into_py(py)
182 }
183
184 #[cfg(feature = "experimental-inspect")]
185 fn type_output() -> TypeInfo {
186 TypeInfo::builtin("bool")
187 }
188}
189
190impl FromPyObject<'_> for bool {
194 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
195 let err = match obj.downcast::<PyBool>() {
196 Ok(obj) => return Ok(obj.is_true()),
197 Err(err) => err,
198 };
199
200 let is_numpy_bool = {
201 let ty = obj.get_type();
202 ty.module().map_or(false, |module| module == "numpy")
203 && ty
204 .name()
205 .map_or(false, |name| name == "bool_" || name == "bool")
206 };
207
208 if is_numpy_bool {
209 let missing_conversion = |obj: &Bound<'_, PyAny>| {
210 PyTypeError::new_err(format!(
211 "object of type '{}' does not define a '__bool__' conversion",
212 obj.get_type()
213 ))
214 };
215
216 #[cfg(not(any(Py_LIMITED_API, PyPy)))]
217 unsafe {
218 let ptr = obj.as_ptr();
219
220 if let Some(tp_as_number) = (*(*ptr).ob_type).tp_as_number.as_ref() {
221 if let Some(nb_bool) = tp_as_number.nb_bool {
222 match (nb_bool)(ptr) {
223 0 => return Ok(false),
224 1 => return Ok(true),
225 _ => return Err(crate::PyErr::fetch(obj.py())),
226 }
227 }
228 }
229
230 return Err(missing_conversion(obj));
231 }
232
233 #[cfg(any(Py_LIMITED_API, PyPy))]
234 {
235 let meth = obj
236 .lookup_special(crate::intern!(obj.py(), "__bool__"))?
237 .ok_or_else(|| missing_conversion(obj))?;
238
239 let obj = meth.call0()?.downcast_into::<PyBool>()?;
240 return Ok(obj.is_true());
241 }
242 }
243
244 Err(err.into())
245 }
246
247 #[cfg(feature = "experimental-inspect")]
248 fn type_input() -> TypeInfo {
249 Self::type_output()
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use crate::types::any::PyAnyMethods;
256 use crate::types::boolobject::PyBoolMethods;
257 use crate::types::PyBool;
258 use crate::Python;
259 use crate::ToPyObject;
260
261 #[test]
262 fn test_true() {
263 Python::with_gil(|py| {
264 assert!(PyBool::new_bound(py, true).is_true());
265 let t = PyBool::new_bound(py, true);
266 assert!(t.extract::<bool>().unwrap());
267 assert!(true.to_object(py).is(&*PyBool::new_bound(py, true)));
268 });
269 }
270
271 #[test]
272 fn test_false() {
273 Python::with_gil(|py| {
274 assert!(!PyBool::new_bound(py, false).is_true());
275 let t = PyBool::new_bound(py, false);
276 assert!(!t.extract::<bool>().unwrap());
277 assert!(false.to_object(py).is(&*PyBool::new_bound(py, false)));
278 });
279 }
280
281 #[test]
282 fn test_pybool_comparisons() {
283 Python::with_gil(|py| {
284 let py_bool = PyBool::new_bound(py, true);
285 let py_bool_false = PyBool::new_bound(py, false);
286 let rust_bool = true;
287
288 assert_eq!(*py_bool, rust_bool);
290 assert_ne!(*py_bool_false, rust_bool);
291
292 assert_eq!(*py_bool, &rust_bool);
294 assert_ne!(*py_bool_false, &rust_bool);
295
296 assert_eq!(&*py_bool, rust_bool);
298 assert_ne!(&*py_bool_false, rust_bool);
299
300 assert_eq!(&*py_bool, &rust_bool);
302 assert_ne!(&*py_bool_false, &rust_bool);
303
304 assert_eq!(rust_bool, *py_bool);
306 assert_ne!(rust_bool, *py_bool_false);
307
308 assert_eq!(rust_bool, &*py_bool);
310 assert_ne!(rust_bool, &*py_bool_false);
311
312 assert_eq!(&rust_bool, *py_bool);
314 assert_ne!(&rust_bool, *py_bool_false);
315
316 assert_eq!(&rust_bool, &*py_bool);
318 assert_ne!(&rust_bool, &*py_bool_false);
319
320 assert_eq!(py_bool, rust_bool);
322 assert_ne!(py_bool_false, rust_bool);
323
324 assert_eq!(py_bool, &rust_bool);
326 assert_ne!(py_bool_false, &rust_bool);
327
328 assert_eq!(rust_bool, py_bool);
330 assert_ne!(rust_bool, py_bool_false);
331
332 assert_eq!(&rust_bool, py_bool);
334 assert_ne!(&rust_bool, py_bool_false);
335 assert_eq!(py_bool, rust_bool);
336 assert_ne!(py_bool_false, rust_bool);
337 })
338 }
339}