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 .cast_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 #[cfg(feature = "experimental-inspect")]
145 const OUTPUT_TYPE: &'static str = "bool";
146
147 #[inline]
148 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
149 Ok(PyBool::new(py, self))
150 }
151
152 #[cfg(feature = "experimental-inspect")]
153 fn type_output() -> TypeInfo {
154 TypeInfo::builtin("bool")
155 }
156}
157
158impl<'py> IntoPyObject<'py> for &bool {
159 type Target = PyBool;
160 type Output = Borrowed<'py, 'py, Self::Target>;
161 type Error = Infallible;
162
163 #[cfg(feature = "experimental-inspect")]
164 const OUTPUT_TYPE: &'static str = bool::OUTPUT_TYPE;
165
166 #[inline]
167 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
168 (*self).into_pyobject(py)
169 }
170
171 #[cfg(feature = "experimental-inspect")]
172 fn type_output() -> TypeInfo {
173 TypeInfo::builtin("bool")
174 }
175}
176
177impl FromPyObject<'_> for bool {
181 #[cfg(feature = "experimental-inspect")]
182 const INPUT_TYPE: &'static str = "bool";
183
184 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
185 let err = match obj.cast::<PyBool>() {
186 Ok(obj) => return Ok(obj.is_true()),
187 Err(err) => err,
188 };
189
190 let is_numpy_bool = {
191 let ty = obj.get_type();
192 ty.module().is_ok_and(|module| module == "numpy")
193 && ty
194 .name()
195 .is_ok_and(|name| name == "bool_" || name == "bool")
196 };
197
198 if is_numpy_bool {
199 let missing_conversion = |obj: &Bound<'_, PyAny>| {
200 PyTypeError::new_err(format!(
201 "object of type '{}' does not define a '__bool__' conversion",
202 obj.get_type()
203 ))
204 };
205
206 #[cfg(not(any(Py_LIMITED_API, PyPy)))]
207 unsafe {
208 let ptr = obj.as_ptr();
209
210 if let Some(tp_as_number) = (*(*ptr).ob_type).tp_as_number.as_ref() {
211 if let Some(nb_bool) = tp_as_number.nb_bool {
212 match (nb_bool)(ptr) {
213 0 => return Ok(false),
214 1 => return Ok(true),
215 _ => return Err(crate::PyErr::fetch(obj.py())),
216 }
217 }
218 }
219
220 return Err(missing_conversion(obj));
221 }
222
223 #[cfg(any(Py_LIMITED_API, PyPy))]
224 {
225 let meth = obj
226 .lookup_special(crate::intern!(obj.py(), "__bool__"))?
227 .ok_or_else(|| missing_conversion(obj))?;
228
229 let obj = meth.call0()?.cast_into::<PyBool>()?;
230 return Ok(obj.is_true());
231 }
232 }
233
234 Err(err.into())
235 }
236
237 #[cfg(feature = "experimental-inspect")]
238 fn type_input() -> TypeInfo {
239 Self::type_output()
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use crate::types::any::PyAnyMethods;
246 use crate::types::boolobject::PyBoolMethods;
247 use crate::types::PyBool;
248 use crate::IntoPyObject;
249 use crate::Python;
250
251 #[test]
252 fn test_true() {
253 Python::attach(|py| {
254 assert!(PyBool::new(py, true).is_true());
255 let t = PyBool::new(py, true);
256 assert!(t.extract::<bool>().unwrap());
257 assert!(true.into_pyobject(py).unwrap().is(&*PyBool::new(py, true)));
258 });
259 }
260
261 #[test]
262 fn test_false() {
263 Python::attach(|py| {
264 assert!(!PyBool::new(py, false).is_true());
265 let t = PyBool::new(py, false);
266 assert!(!t.extract::<bool>().unwrap());
267 assert!(false
268 .into_pyobject(py)
269 .unwrap()
270 .is(&*PyBool::new(py, false)));
271 });
272 }
273
274 #[test]
275 fn test_pybool_comparisons() {
276 Python::attach(|py| {
277 let py_bool = PyBool::new(py, true);
278 let py_bool_false = PyBool::new(py, false);
279 let rust_bool = true;
280
281 assert_eq!(*py_bool, rust_bool);
283 assert_ne!(*py_bool_false, rust_bool);
284
285 assert_eq!(*py_bool, &rust_bool);
287 assert_ne!(*py_bool_false, &rust_bool);
288
289 assert_eq!(&*py_bool, rust_bool);
291 assert_ne!(&*py_bool_false, rust_bool);
292
293 assert_eq!(&*py_bool, &rust_bool);
295 assert_ne!(&*py_bool_false, &rust_bool);
296
297 assert_eq!(rust_bool, *py_bool);
299 assert_ne!(rust_bool, *py_bool_false);
300
301 assert_eq!(rust_bool, &*py_bool);
303 assert_ne!(rust_bool, &*py_bool_false);
304
305 assert_eq!(&rust_bool, *py_bool);
307 assert_ne!(&rust_bool, *py_bool_false);
308
309 assert_eq!(&rust_bool, &*py_bool);
311 assert_ne!(&rust_bool, &*py_bool_false);
312
313 assert_eq!(py_bool, rust_bool);
315 assert_ne!(py_bool_false, rust_bool);
316
317 assert_eq!(py_bool, &rust_bool);
319 assert_ne!(py_bool_false, &rust_bool);
320
321 assert_eq!(rust_bool, py_bool);
323 assert_ne!(rust_bool, py_bool_false);
324
325 assert_eq!(&rust_bool, py_bool);
327 assert_ne!(&rust_bool, py_bool_false);
328 assert_eq!(py_bool, rust_bool);
329 assert_ne!(py_bool_false, rust_bool);
330 })
331 }
332}