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;
14use std::ptr;
15
16/// Represents a Python `bool`.
17///
18/// Values of this type are accessed via PyO3's smart pointers, e.g. as
19/// [`Py<PyBool>`][crate::Py] or [`Bound<'py, PyBool>`][Bound].
20///
21/// For APIs available on `bool` objects, see the [`PyBoolMethods`] trait which is implemented for
22/// [`Bound<'py, PyBool>`][Bound].
23#[repr(transparent)]
24pub struct PyBool(PyAny);
25
26pyobject_native_type!(PyBool, ffi::PyObject, pyobject_native_static_type_object!(ffi::PyBool_Type), #checkfunction=ffi::PyBool_Check);
27
28impl PyBool {
29 /// Depending on `val`, returns `true` or `false`.
30 ///
31 /// # Note
32 /// This returns a [`Borrowed`] reference to one of Pythons `True` or
33 /// `False` singletons
34 #[inline]
35 pub fn new(py: Python<'_>, val: bool) -> Borrowed<'_, '_, Self> {
36 unsafe {
37 ifBorrowed<'_, '_, PyAny> val { ffi::Py_True() } else { ffi::Py_False() }
38 .assume_borrowed(py)
39 .downcast_unchecked()
40 }
41 }
42
43 /// Deprecated name for [`PyBool::new`].
44 #[deprecated(since = "0.23.0", note = "renamed to `PyBool::new`")]
45 #[inline]
46 pub fn new_bound(py: Python<'_>, val: bool) -> Borrowed<'_, '_, Self> {
47 Self::new(py, val)
48 }
49}
50
51/// Implementation of functionality for [`PyBool`].
52///
53/// These methods are defined for the `Bound<'py, PyBool>` smart pointer, so to use method call
54/// syntax these methods are separated into a trait, because stable Rust does not yet support
55/// `arbitrary_self_types`.
56#[doc(alias = "PyBool")]
57pub trait PyBoolMethods<'py>: crate::sealed::Sealed {
58 /// Gets whether this boolean is `true`.
59 fn is_true(&self) -> bool;
60}
61
62impl<'py> PyBoolMethods<'py> for Bound<'py, PyBool> {
63 #[inline]
64 fn is_true(&self) -> bool {
65 unsafe { ptr::eq(self.as_ptr(), b:ffi::Py_True()) }
66 }
67}
68
69/// Compare `Bound<PyBool>` with `bool`.
70impl PartialEq<bool> for Bound<'_, PyBool> {
71 #[inline]
72 fn eq(&self, other: &bool) -> bool {
73 self.as_borrowed() == *other
74 }
75}
76
77/// Compare `&Bound<PyBool>` with `bool`.
78impl PartialEq<bool> for &'_ Bound<'_, PyBool> {
79 #[inline]
80 fn eq(&self, other: &bool) -> bool {
81 self.as_borrowed() == *other
82 }
83}
84
85/// Compare `Bound<PyBool>` with `&bool`.
86impl PartialEq<&'_ bool> for Bound<'_, PyBool> {
87 #[inline]
88 fn eq(&self, other: &&bool) -> bool {
89 self.as_borrowed() == **other
90 }
91}
92
93/// Compare `bool` with `Bound<PyBool>`
94impl PartialEq<Bound<'_, PyBool>> for bool {
95 #[inline]
96 fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
97 *self == other.as_borrowed()
98 }
99}
100
101/// Compare `bool` with `&Bound<PyBool>`
102impl PartialEq<&'_ Bound<'_, PyBool>> for bool {
103 #[inline]
104 fn eq(&self, other: &&'_ Bound<'_, PyBool>) -> bool {
105 *self == other.as_borrowed()
106 }
107}
108
109/// Compare `&bool` with `Bound<PyBool>`
110impl PartialEq<Bound<'_, PyBool>> for &'_ bool {
111 #[inline]
112 fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
113 **self == other.as_borrowed()
114 }
115}
116
117/// Compare `Borrowed<PyBool>` with `bool`
118impl PartialEq<bool> for Borrowed<'_, '_, PyBool> {
119 #[inline]
120 fn eq(&self, other: &bool) -> bool {
121 self.is_true() == *other
122 }
123}
124
125/// Compare `Borrowed<PyBool>` with `&bool`
126impl PartialEq<&bool> for Borrowed<'_, '_, PyBool> {
127 #[inline]
128 fn eq(&self, other: &&bool) -> bool {
129 self.is_true() == **other
130 }
131}
132
133/// Compare `bool` with `Borrowed<PyBool>`
134impl PartialEq<Borrowed<'_, '_, PyBool>> for bool {
135 #[inline]
136 fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
137 *self == other.is_true()
138 }
139}
140
141/// Compare `&bool` with `Borrowed<PyBool>`
142impl PartialEq<Borrowed<'_, '_, PyBool>> for &'_ bool {
143 #[inline]
144 fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
145 **self == other.is_true()
146 }
147}
148
149/// Converts a Rust `bool` to a Python `bool`.
150#[allow(deprecated)]
151impl ToPyObject for bool {
152 #[inline]
153 fn to_object(&self, py: Python<'_>) -> PyObject {
154 self.into_pyobject(py).unwrap().into_any().unbind()
155 }
156}
157
158#[allow(deprecated)]
159impl IntoPy<PyObject> for bool {
160 #[inline]
161 fn into_py(self, py: Python<'_>) -> PyObject {
162 self.into_pyobject(py).unwrap().into_any().unbind()
163 }
164}
165
166impl<'py> IntoPyObject<'py> for bool {
167 type Target = PyBool;
168 type Output = Borrowed<'py, 'py, Self::Target>;
169 type Error = Infallible;
170
171 #[inline]
172 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
173 Ok(PyBool::new(py, self))
174 }
175
176 #[cfg(feature = "experimental-inspect")]
177 fn type_output() -> TypeInfo {
178 TypeInfo::builtin("bool")
179 }
180}
181
182impl<'py> IntoPyObject<'py> for &bool {
183 type Target = PyBool;
184 type Output = Borrowed<'py, 'py, Self::Target>;
185 type Error = Infallible;
186
187 #[inline]
188 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
189 (*self).into_pyobject(py)
190 }
191
192 #[cfg(feature = "experimental-inspect")]
193 fn type_output() -> TypeInfo {
194 TypeInfo::builtin("bool")
195 }
196}
197
198/// Converts a Python `bool` to a Rust `bool`.
199///
200/// Fails with `TypeError` if the input is not a Python `bool`.
201impl FromPyObject<'_> for bool {
202 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
203 let err = match obj.downcast::<PyBool>() {
204 Ok(obj) => return Ok(obj.is_true()),
205 Err(err) => err,
206 };
207
208 let is_numpy_bool = {
209 let ty = obj.get_type();
210 ty.module().map_or(false, |module| module == "numpy")
211 && ty
212 .name()
213 .map_or(false, |name| name == "bool_" || name == "bool")
214 };
215
216 if is_numpy_bool {
217 let missing_conversion = |obj: &Bound<'_, PyAny>| {
218 PyTypeError::new_err(format!(
219 "object of type '{}' does not define a '__bool__' conversion",
220 obj.get_type()
221 ))
222 };
223
224 #[cfg(not(any(Py_LIMITED_API, PyPy)))]
225 unsafe {
226 let ptr = obj.as_ptr();
227
228 if let Some(tp_as_number) = (*(*ptr).ob_type).tp_as_number.as_ref() {
229 if let Some(nb_bool) = tp_as_number.nb_bool {
230 match (nb_bool)(ptr) {
231 0 => return Ok(false),
232 1 => return Ok(true),
233 _ => return Err(crate::PyErr::fetch(obj.py())),
234 }
235 }
236 }
237
238 return Err(missing_conversion(obj));
239 }
240
241 #[cfg(any(Py_LIMITED_API, PyPy))]
242 {
243 let meth = obj
244 .lookup_special(crate::intern!(obj.py(), "__bool__"))?
245 .ok_or_else(|| missing_conversion(obj))?;
246
247 let obj = meth.call0()?.downcast_into::<PyBool>()?;
248 return Ok(obj.is_true());
249 }
250 }
251
252 Err(err.into())
253 }
254
255 #[cfg(feature = "experimental-inspect")]
256 fn type_input() -> TypeInfo {
257 Self::type_output()
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use crate::types::any::PyAnyMethods;
264 use crate::types::boolobject::PyBoolMethods;
265 use crate::types::PyBool;
266 use crate::IntoPyObject;
267 use crate::Python;
268
269 #[test]
270 fn test_true() {
271 Python::with_gil(|py| {
272 assert!(PyBool::new(py, true).is_true());
273 let t = PyBool::new(py, true);
274 assert!(t.extract::<bool>().unwrap());
275 assert!(true.into_pyobject(py).unwrap().is(&*PyBool::new(py, true)));
276 });
277 }
278
279 #[test]
280 fn test_false() {
281 Python::with_gil(|py| {
282 assert!(!PyBool::new(py, false).is_true());
283 let t = PyBool::new(py, false);
284 assert!(!t.extract::<bool>().unwrap());
285 assert!(false
286 .into_pyobject(py)
287 .unwrap()
288 .is(&*PyBool::new(py, false)));
289 });
290 }
291
292 #[test]
293 fn test_pybool_comparisons() {
294 Python::with_gil(|py| {
295 let py_bool = PyBool::new(py, true);
296 let py_bool_false = PyBool::new(py, false);
297 let rust_bool = true;
298
299 // Bound<'_, PyBool> == bool
300 assert_eq!(*py_bool, rust_bool);
301 assert_ne!(*py_bool_false, rust_bool);
302
303 // Bound<'_, PyBool> == &bool
304 assert_eq!(*py_bool, &rust_bool);
305 assert_ne!(*py_bool_false, &rust_bool);
306
307 // &Bound<'_, PyBool> == bool
308 assert_eq!(&*py_bool, rust_bool);
309 assert_ne!(&*py_bool_false, rust_bool);
310
311 // &Bound<'_, PyBool> == &bool
312 assert_eq!(&*py_bool, &rust_bool);
313 assert_ne!(&*py_bool_false, &rust_bool);
314
315 // bool == Bound<'_, PyBool>
316 assert_eq!(rust_bool, *py_bool);
317 assert_ne!(rust_bool, *py_bool_false);
318
319 // bool == &Bound<'_, PyBool>
320 assert_eq!(rust_bool, &*py_bool);
321 assert_ne!(rust_bool, &*py_bool_false);
322
323 // &bool == Bound<'_, PyBool>
324 assert_eq!(&rust_bool, *py_bool);
325 assert_ne!(&rust_bool, *py_bool_false);
326
327 // &bool == &Bound<'_, PyBool>
328 assert_eq!(&rust_bool, &*py_bool);
329 assert_ne!(&rust_bool, &*py_bool_false);
330
331 // Borrowed<'_, '_, PyBool> == bool
332 assert_eq!(py_bool, rust_bool);
333 assert_ne!(py_bool_false, rust_bool);
334
335 // Borrowed<'_, '_, PyBool> == &bool
336 assert_eq!(py_bool, &rust_bool);
337 assert_ne!(py_bool_false, &rust_bool);
338
339 // bool == Borrowed<'_, '_, PyBool>
340 assert_eq!(rust_bool, py_bool);
341 assert_ne!(rust_bool, py_bool_false);
342
343 // &bool == Borrowed<'_, '_, PyBool>
344 assert_eq!(&rust_bool, py_bool);
345 assert_ne!(&rust_bool, py_bool_false);
346 assert_eq!(py_bool, rust_bool);
347 assert_ne!(py_bool_false, rust_bool);
348 })
349 }
350}
351