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, PyResult, Python,
6};
7
8use super::any::PyAnyMethods;
9use crate::conversion::IntoPyObject;
10use std::convert::Infallible;
11use std::ptr;
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(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#[doc(alias = "PyBool")]
47pub trait PyBoolMethods<'py>: crate::sealed::Sealed {
48 fn is_true(&self) -> bool;
50}
51
52impl<'py> PyBoolMethods<'py> for Bound<'py, PyBool> {
53 #[inline]
54 fn is_true(&self) -> bool {
55 unsafe { ptr::eq(self.as_ptr(), ffi::Py_True()) }
56 }
57}
58
59impl PartialEq<bool> for Bound<'_, PyBool> {
61 #[inline]
62 fn eq(&self, other: &bool) -> bool {
63 self.as_borrowed() == *other
64 }
65}
66
67impl PartialEq<bool> for &'_ Bound<'_, PyBool> {
69 #[inline]
70 fn eq(&self, other: &bool) -> bool {
71 self.as_borrowed() == *other
72 }
73}
74
75impl PartialEq<&'_ bool> for Bound<'_, PyBool> {
77 #[inline]
78 fn eq(&self, other: &&bool) -> bool {
79 self.as_borrowed() == **other
80 }
81}
82
83impl PartialEq<Bound<'_, PyBool>> for bool {
85 #[inline]
86 fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
87 *self == other.as_borrowed()
88 }
89}
90
91impl PartialEq<&'_ Bound<'_, PyBool>> for bool {
93 #[inline]
94 fn eq(&self, other: &&'_ Bound<'_, PyBool>) -> bool {
95 *self == other.as_borrowed()
96 }
97}
98
99impl PartialEq<Bound<'_, PyBool>> for &'_ bool {
101 #[inline]
102 fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
103 **self == other.as_borrowed()
104 }
105}
106
107impl PartialEq<bool> for Borrowed<'_, '_, PyBool> {
109 #[inline]
110 fn eq(&self, other: &bool) -> bool {
111 self.is_true() == *other
112 }
113}
114
115impl PartialEq<&bool> for Borrowed<'_, '_, PyBool> {
117 #[inline]
118 fn eq(&self, other: &&bool) -> bool {
119 self.is_true() == **other
120 }
121}
122
123impl PartialEq<Borrowed<'_, '_, PyBool>> for bool {
125 #[inline]
126 fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
127 *self == other.is_true()
128 }
129}
130
131impl PartialEq<Borrowed<'_, '_, PyBool>> for &'_ bool {
133 #[inline]
134 fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
135 **self == other.is_true()
136 }
137}
138
139impl<'py> IntoPyObject<'py> for bool {
140 type Target = PyBool;
141 type Output = Borrowed<'py, 'py, Self::Target>;
142 type Error = Infallible;
143
144 #[inline]
145 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
146 Ok(PyBool::new(py, self))
147 }
148
149 #[cfg(feature = "experimental-inspect")]
150 fn type_output() -> TypeInfo {
151 TypeInfo::builtin("bool")
152 }
153}
154
155impl<'py> IntoPyObject<'py> for &bool {
156 type Target = PyBool;
157 type Output = Borrowed<'py, 'py, Self::Target>;
158 type Error = Infallible;
159
160 #[inline]
161 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
162 (*self).into_pyobject(py)
163 }
164
165 #[cfg(feature = "experimental-inspect")]
166 fn type_output() -> TypeInfo {
167 TypeInfo::builtin("bool")
168 }
169}
170
171impl FromPyObject<'_> for bool {
175 #[cfg(feature = "experimental-inspect")]
176 const INPUT_TYPE: &'static str = "bool";
177
178 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
179 let err = match obj.downcast::<PyBool>() {
180 Ok(obj) => return Ok(obj.is_true()),
181 Err(err) => err,
182 };
183
184 let is_numpy_bool = {
185 let ty = obj.get_type();
186 ty.module().is_ok_and(|module| module == "numpy")
187 && ty
188 .name()
189 .is_ok_and(|name| name == "bool_" || name == "bool")
190 };
191
192 if is_numpy_bool {
193 let missing_conversion = |obj: &Bound<'_, PyAny>| {
194 PyTypeError::new_err(format!(
195 "object of type '{}' does not define a '__bool__' conversion",
196 obj.get_type()
197 ))
198 };
199
200 #[cfg(not(any(Py_LIMITED_API, PyPy)))]
201 unsafe {
202 let ptr = obj.as_ptr();
203
204 if let Some(tp_as_number) = (*(*ptr).ob_type).tp_as_number.as_ref() {
205 if let Some(nb_bool) = tp_as_number.nb_bool {
206 match (nb_bool)(ptr) {
207 0 => return Ok(false),
208 1 => return Ok(true),
209 _ => return Err(crate::PyErr::fetch(obj.py())),
210 }
211 }
212 }
213
214 return Err(missing_conversion(obj));
215 }
216
217 #[cfg(any(Py_LIMITED_API, PyPy))]
218 {
219 let meth = obj
220 .lookup_special(crate::intern!(obj.py(), "__bool__"))?
221 .ok_or_else(|| missing_conversion(obj))?;
222
223 let obj = meth.call0()?.downcast_into::<PyBool>()?;
224 return Ok(obj.is_true());
225 }
226 }
227
228 Err(err.into())
229 }
230
231 #[cfg(feature = "experimental-inspect")]
232 fn type_input() -> TypeInfo {
233 Self::type_output()
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use crate::types::any::PyAnyMethods;
240 use crate::types::boolobject::PyBoolMethods;
241 use crate::types::PyBool;
242 use crate::IntoPyObject;
243 use crate::Python;
244
245 #[test]
246 fn test_true() {
247 Python::attach(|py| {
248 assert!(PyBool::new(py, true).is_true());
249 let t = PyBool::new(py, true);
250 assert!(t.extract::<bool>().unwrap());
251 assert!(true.into_pyobject(py).unwrap().is(&*PyBool::new(py, true)));
252 });
253 }
254
255 #[test]
256 fn test_false() {
257 Python::attach(|py| {
258 assert!(!PyBool::new(py, false).is_true());
259 let t = PyBool::new(py, false);
260 assert!(!t.extract::<bool>().unwrap());
261 assert!(false
262 .into_pyobject(py)
263 .unwrap()
264 .is(&*PyBool::new(py, false)));
265 });
266 }
267
268 #[test]
269 fn test_pybool_comparisons() {
270 Python::attach(|py| {
271 let py_bool = PyBool::new(py, true);
272 let py_bool_false = PyBool::new(py, false);
273 let rust_bool = true;
274
275 assert_eq!(*py_bool, rust_bool);
277 assert_ne!(*py_bool_false, rust_bool);
278
279 assert_eq!(*py_bool, &rust_bool);
281 assert_ne!(*py_bool_false, &rust_bool);
282
283 assert_eq!(&*py_bool, rust_bool);
285 assert_ne!(&*py_bool_false, rust_bool);
286
287 assert_eq!(&*py_bool, &rust_bool);
289 assert_ne!(&*py_bool_false, &rust_bool);
290
291 assert_eq!(rust_bool, *py_bool);
293 assert_ne!(rust_bool, *py_bool_false);
294
295 assert_eq!(rust_bool, &*py_bool);
297 assert_ne!(rust_bool, &*py_bool_false);
298
299 assert_eq!(&rust_bool, *py_bool);
301 assert_ne!(&rust_bool, *py_bool_false);
302
303 assert_eq!(&rust_bool, &*py_bool);
305 assert_ne!(&rust_bool, &*py_bool_false);
306
307 assert_eq!(py_bool, rust_bool);
309 assert_ne!(py_bool_false, rust_bool);
310
311 assert_eq!(py_bool, &rust_bool);
313 assert_ne!(py_bool_false, &rust_bool);
314
315 assert_eq!(rust_bool, py_bool);
317 assert_ne!(rust_bool, py_bool_false);
318
319 assert_eq!(&rust_bool, py_bool);
321 assert_ne!(&rust_bool, py_bool_false);
322 assert_eq!(py_bool, rust_bool);
323 assert_ne!(py_bool_false, rust_bool);
324 })
325 }
326}