use super::any::PyAnyMethods;
use crate::conversion::IntoPyObject;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::PyStaticExpr;
#[cfg(feature = "experimental-inspect")]
use crate::type_object::PyTypeInfo;
use crate::PyErr;
use crate::{
exceptions::PyTypeError, ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound,
types::typeobject::PyTypeMethods, Borrowed, FromPyObject, PyAny, Python,
};
use std::convert::Infallible;
use std::ptr;
#[repr(transparent)]
pub struct PyBool(PyAny);
pyobject_native_type!(PyBool, ffi::PyObject, pyobject_native_static_type_object!(ffi::PyBool_Type), "builtins", "bool", #checkfunction=ffi::PyBool_Check);
impl PyBool {
#[inline]
pub fn new(py: Python<'_>, val: bool) -> Borrowed<'_, '_, Self> {
unsafe {
if val { ffi::Py_True() } else { ffi::Py_False() }
.assume_borrowed_unchecked(py)
.cast_unchecked()
}
}
}
#[doc(alias = "PyBool")]
pub trait PyBoolMethods<'py>: crate::sealed::Sealed {
fn is_true(&self) -> bool;
}
impl<'py> PyBoolMethods<'py> for Bound<'py, PyBool> {
#[inline]
fn is_true(&self) -> bool {
unsafe { ptr::eq(self.as_ptr(), ffi::Py_True()) }
}
}
impl PartialEq<bool> for Bound<'_, PyBool> {
#[inline]
fn eq(&self, other: &bool) -> bool {
self.as_borrowed() == *other
}
}
impl PartialEq<bool> for &'_ Bound<'_, PyBool> {
#[inline]
fn eq(&self, other: &bool) -> bool {
self.as_borrowed() == *other
}
}
impl PartialEq<&'_ bool> for Bound<'_, PyBool> {
#[inline]
fn eq(&self, other: &&bool) -> bool {
self.as_borrowed() == **other
}
}
impl PartialEq<Bound<'_, PyBool>> for bool {
#[inline]
fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
*self == other.as_borrowed()
}
}
impl PartialEq<&'_ Bound<'_, PyBool>> for bool {
#[inline]
fn eq(&self, other: &&'_ Bound<'_, PyBool>) -> bool {
*self == other.as_borrowed()
}
}
impl PartialEq<Bound<'_, PyBool>> for &'_ bool {
#[inline]
fn eq(&self, other: &Bound<'_, PyBool>) -> bool {
**self == other.as_borrowed()
}
}
impl PartialEq<bool> for Borrowed<'_, '_, PyBool> {
#[inline]
fn eq(&self, other: &bool) -> bool {
self.is_true() == *other
}
}
impl PartialEq<&bool> for Borrowed<'_, '_, PyBool> {
#[inline]
fn eq(&self, other: &&bool) -> bool {
self.is_true() == **other
}
}
impl PartialEq<Borrowed<'_, '_, PyBool>> for bool {
#[inline]
fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
*self == other.is_true()
}
}
impl PartialEq<Borrowed<'_, '_, PyBool>> for &'_ bool {
#[inline]
fn eq(&self, other: &Borrowed<'_, '_, PyBool>) -> bool {
**self == other.is_true()
}
}
impl<'py> IntoPyObject<'py> for bool {
type Target = PyBool;
type Output = Borrowed<'py, 'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = PyBool::TYPE_HINT;
#[inline]
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(PyBool::new(py, self))
}
#[cfg(feature = "experimental-inspect")]
fn type_output() -> TypeInfo {
TypeInfo::builtin("bool")
}
}
impl<'py> IntoPyObject<'py> for &bool {
type Target = PyBool;
type Output = Borrowed<'py, 'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = bool::OUTPUT_TYPE;
#[inline]
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
(*self).into_pyobject(py)
}
#[cfg(feature = "experimental-inspect")]
fn type_output() -> TypeInfo {
TypeInfo::builtin("bool")
}
}
impl FromPyObject<'_, '_> for bool {
type Error = PyErr;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = PyBool::TYPE_HINT;
fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
let err = match obj.cast::<PyBool>() {
Ok(obj) => return Ok(obj.is_true()),
Err(err) => err,
};
let is_numpy_bool = {
let ty = obj.get_type();
ty.module().is_ok_and(|module| module == "numpy")
&& ty
.name()
.is_ok_and(|name| name == "bool_" || name == "bool")
};
if is_numpy_bool {
let missing_conversion = |obj: Borrowed<'_, '_, PyAny>| {
PyTypeError::new_err(format!(
"object of type '{}' does not define a '__bool__' conversion",
obj.get_type()
))
};
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
unsafe {
let ptr = obj.as_ptr();
if let Some(tp_as_number) = (*(*ptr).ob_type).tp_as_number.as_ref() {
if let Some(nb_bool) = tp_as_number.nb_bool {
match (nb_bool)(ptr) {
0 => return Ok(false),
1 => return Ok(true),
_ => return Err(crate::PyErr::fetch(obj.py())),
}
}
}
return Err(missing_conversion(obj));
}
#[cfg(any(Py_LIMITED_API, PyPy))]
{
let meth = obj
.lookup_special(crate::intern!(obj.py(), "__bool__"))?
.ok_or_else(|| missing_conversion(obj))?;
let obj = meth.call0()?.cast_into::<PyBool>()?;
return Ok(obj.is_true());
}
}
Err(err.into())
}
#[cfg(feature = "experimental-inspect")]
fn type_input() -> TypeInfo {
Self::type_output()
}
}
#[cfg(test)]
mod tests {
use crate::types::{PyAnyMethods, PyBool, PyBoolMethods};
use crate::IntoPyObject;
use crate::Python;
#[test]
fn test_true() {
Python::attach(|py| {
assert!(PyBool::new(py, true).is_true());
let t = PyBool::new(py, true);
assert!(t.extract::<bool>().unwrap());
assert!(true.into_pyobject(py).unwrap().is(&*PyBool::new(py, true)));
});
}
#[test]
fn test_false() {
Python::attach(|py| {
assert!(!PyBool::new(py, false).is_true());
let t = PyBool::new(py, false);
assert!(!t.extract::<bool>().unwrap());
assert!(false
.into_pyobject(py)
.unwrap()
.is(&*PyBool::new(py, false)));
});
}
#[test]
fn test_pybool_comparisons() {
Python::attach(|py| {
let py_bool = PyBool::new(py, true);
let py_bool_false = PyBool::new(py, false);
let rust_bool = true;
assert_eq!(*py_bool, rust_bool);
assert_ne!(*py_bool_false, rust_bool);
assert_eq!(*py_bool, &rust_bool);
assert_ne!(*py_bool_false, &rust_bool);
assert_eq!(&*py_bool, rust_bool);
assert_ne!(&*py_bool_false, rust_bool);
assert_eq!(&*py_bool, &rust_bool);
assert_ne!(&*py_bool_false, &rust_bool);
assert_eq!(rust_bool, *py_bool);
assert_ne!(rust_bool, *py_bool_false);
assert_eq!(rust_bool, &*py_bool);
assert_ne!(rust_bool, &*py_bool_false);
assert_eq!(&rust_bool, *py_bool);
assert_ne!(&rust_bool, *py_bool_false);
assert_eq!(&rust_bool, &*py_bool);
assert_ne!(&rust_bool, &*py_bool_false);
assert_eq!(py_bool, rust_bool);
assert_ne!(py_bool_false, rust_bool);
assert_eq!(py_bool, &rust_bool);
assert_ne!(py_bool_false, &rust_bool);
assert_eq!(rust_bool, py_bool);
assert_ne!(rust_bool, py_bool_false);
assert_eq!(&rust_bool, py_bool);
assert_ne!(&rust_bool, py_bool_false);
assert_eq!(py_bool, rust_bool);
assert_ne!(py_bool_false, rust_bool);
})
}
}