diff --git a/src/types/any.rs b/src/types/any.rs index 43bce57d3c6..dc29b1dea56 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -144,6 +144,7 @@ impl PyAny { /// /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// to intern `attr_name`. + #[allow(dead_code)] // Currently only used with num-complex+abi3, so dead without that. pub(crate) fn lookup_special(&self, attr_name: N) -> PyResult> where N: IntoPy>, diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index 48b4816111c..918aaa520f7 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -1,7 +1,7 @@ #[cfg(feature = "experimental-inspect")] use crate::inspect::types::TypeInfo; use crate::{ - exceptions::PyTypeError, ffi, intern, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, + exceptions::PyTypeError, ffi, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject, }; @@ -63,12 +63,32 @@ impl<'source> FromPyObject<'source> for bool { return Ok(obj.is_true()); } - let meth = obj - .lookup_special(intern!(obj.py(), "__bool__"))? - .ok_or_else(|| PyTypeError::new_err("object has no __bool__ magic method"))?; + #[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())), + } + } + } + + Err(PyTypeError::new_err("object has no __bool__ magic method")) + } + + #[cfg(any(Py_LIMITED_API, PyPy))] + { + let meth = obj + .lookup_special(crate::intern!(obj.py(), "__bool__"))? + .ok_or_else(|| PyTypeError::new_err("object has no __bool__ magic method"))?; - let obj = meth.call0()?.downcast::()?; - Ok(obj.is_true()) + let obj = meth.call0()?.downcast::()?; + Ok(obj.is_true()) + } } #[cfg(feature = "experimental-inspect")] @@ -127,10 +147,11 @@ class D: assert!(a.extract::().unwrap()); let b = module.getattr("B").unwrap().call0().unwrap(); - assert_eq!( - b.extract::().unwrap_err().to_string(), - "TypeError: 'str' object cannot be converted to 'PyBool'", - ); + assert!(matches!( + &*b.extract::().unwrap_err().to_string(), + "TypeError: 'str' object cannot be converted to 'PyBool'" + | "TypeError: __bool__ should return bool, returned str" + )); let c = module.getattr("C").unwrap().call0().unwrap(); assert_eq!(