Avoid attribute lookup overhead for __bool__ if the unlimited API is available.

This commit is contained in:
Adam Reichold 2023-12-10 20:21:11 +01:00
parent 8133aaa5d8
commit 3e10d64fa2
2 changed files with 31 additions and 9 deletions

View File

@ -146,6 +146,7 @@ impl PyAny {
/// ///
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used /// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
/// to intern `attr_name`. /// to intern `attr_name`.
#[allow(dead_code)] // Currently only used with num-complex+abi3, so dead without that.
pub(crate) fn lookup_special<N>(&self, attr_name: N) -> PyResult<Option<&PyAny>> pub(crate) fn lookup_special<N>(&self, attr_name: N) -> PyResult<Option<&PyAny>>
where where
N: IntoPy<Py<PyString>>, N: IntoPy<Py<PyString>>,

View File

@ -81,12 +81,32 @@ impl<'source> FromPyObject<'source> for bool {
return Ok(obj.is_true()); return Ok(obj.is_true());
} }
let meth = obj #[cfg(not(any(Py_LIMITED_API, PyPy)))]
.lookup_special(intern!(obj.py(), "__bool__"))? unsafe {
.ok_or_else(|| PyTypeError::new_err("object has no __bool__ magic method"))?; let ptr = obj.as_ptr();
let obj = meth.call0()?.downcast::<PyBool>()?; if let Some(tp_as_number) = (*(*ptr).ob_type).tp_as_number.as_ref() {
Ok(obj.is_true()) 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::<PyBool>()?;
Ok(obj.is_true())
}
} }
#[cfg(feature = "experimental-inspect")] #[cfg(feature = "experimental-inspect")]
@ -145,10 +165,11 @@ class D:
assert!(a.extract::<bool>().unwrap()); assert!(a.extract::<bool>().unwrap());
let b = module.getattr("B").unwrap().call0().unwrap(); let b = module.getattr("B").unwrap().call0().unwrap();
assert_eq!( assert!(matches!(
b.extract::<bool>().unwrap_err().to_string(), &*b.extract::<bool>().unwrap_err().to_string(),
"TypeError: 'str' object cannot be converted to 'PyBool'", "TypeError: 'str' object cannot be converted to 'PyBool'"
); | "TypeError: __bool__ should return bool, returned str"
));
let c = module.getattr("C").unwrap().call0().unwrap(); let c = module.getattr("C").unwrap().call0().unwrap();
assert_eq!( assert_eq!(