Skip to content

Commit 20e0cdc

Browse files
add PyObject_HasAttrWithError & use it in PyAnyMethods::hasattr (#5944)
* add `PyObject_HasAttrWithError` & use it in `PyAnyMethods::hasattr` * Add 'PyObject_GetOptionalAttr' compat * Remove comment * Update changelog * Fix * Add back `inner` function
1 parent 8a00673 commit 20e0cdc

4 files changed

Lines changed: 32 additions & 12 deletions

File tree

newsfragments/5944.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add `pyo3-ffi::compat::PyObject_HasAttrWithError`

newsfragments/5944.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use `PyObject_HasAttrWithError` in `PyAnyMethods::hasattr`

pyo3-ffi/src/compat/py_3_13.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,15 @@ compat_function!(
202202
-1 // other error
203203
}
204204
);
205+
206+
compat_function!(
207+
originally_defined_for(Py_3_13);
208+
209+
#[inline]
210+
pub unsafe fn PyObject_HasAttrWithError(obj: *mut crate::PyObject, attr_name: *mut crate::PyObject) -> std::ffi::c_int {
211+
let mut res: *mut crate::PyObject = std::ptr::null_mut();
212+
let rc = crate::compat::PyObject_GetOptionalAttr(obj, attr_name, &mut res);
213+
crate::Py_XDECREF(res);
214+
rc
215+
}
216+
);

src/types/any.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::call::PyCallArgs;
22
use crate::class::basic::CompareOp;
33
use crate::conversion::{FromPyObject, IntoPyObject};
4-
use crate::err::{PyErr, PyResult};
5-
use crate::exceptions::{PyAttributeError, PyTypeError};
4+
use crate::err::{error_on_minusone, PyErr, PyResult};
5+
use crate::exceptions::PyTypeError;
66
use crate::ffi_ptr_ext::FfiPtrExt;
77
use crate::impl_::pycell::PyStaticClassObject;
88
use crate::instance::Bound;
@@ -11,7 +11,7 @@ use crate::py_result_ext::PyResultExt;
1111
use crate::type_object::{PyTypeCheck, PyTypeInfo};
1212
use crate::types::PySuper;
1313
use crate::types::{PyDict, PyIterator, PyList, PyString, PyType};
14-
use crate::{err, ffi, Borrowed, BoundObject, IntoPyObjectExt, Py, Python};
14+
use crate::{err, ffi, Borrowed, BoundObject, IntoPyObjectExt, Py};
1515
#[allow(deprecated)]
1616
use crate::{DowncastError, DowncastIntoError};
1717
use std::cell::UnsafeCell;
@@ -980,17 +980,23 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
980980
where
981981
N: IntoPyObject<'py, Target = PyString>,
982982
{
983-
// PyObject_HasAttr suppresses all exceptions, which was the behaviour of `hasattr` in Python 2.
984-
// Use an implementation which suppresses only AttributeError, which is consistent with `hasattr` in Python 3.
985-
fn inner(py: Python<'_>, getattr_result: PyResult<Bound<'_, PyAny>>) -> PyResult<bool> {
986-
match getattr_result {
987-
Ok(_) => Ok(true),
988-
Err(err) if err.is_instance_of::<PyAttributeError>(py) => Ok(false),
989-
Err(e) => Err(e),
990-
}
983+
fn inner<'py>(
984+
any: &Bound<'py, PyAny>,
985+
attr_name: Borrowed<'_, '_, PyString>,
986+
) -> PyResult<bool> {
987+
let result =
988+
unsafe { ffi::compat::PyObject_HasAttrWithError(any.as_ptr(), attr_name.as_ptr()) };
989+
error_on_minusone(any.py(), result)?;
990+
Ok(result > 0)
991991
}
992992

993-
inner(self.py(), self.getattr(attr_name))
993+
inner(
994+
self,
995+
attr_name
996+
.into_pyobject(self.py())
997+
.map_err(Into::into)?
998+
.as_borrowed(),
999+
)
9941000
}
9951001

9961002
fn getattr<N>(&self, attr_name: N) -> PyResult<Bound<'py, PyAny>>

0 commit comments

Comments
 (0)