gh-102213: Optimize the performance of `__getattr__` (GH-103761)

Co-authored-by: Kirill <80244920+Eclips4@users.noreply.github.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
Co-authored-by: Xiang Wang <34048878+wangxiang-hz@users.noreply.github.com>
This commit is contained in:
sunmy2019 2023-05-01 18:10:35 +08:00 committed by GitHub
parent 487f55d580
commit 59c27fa5cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 8 deletions

View File

@ -5004,7 +5004,7 @@ order (MRO) for bases """
self.assertEqual(Parent.__subclasses__(), []) self.assertEqual(Parent.__subclasses__(), [])
def test_attr_raise_through_property(self): def test_attr_raise_through_property(self):
# add test case for gh-103272 # test case for gh-103272
class A: class A:
def __getattr__(self, name): def __getattr__(self, name):
raise ValueError("FOO") raise ValueError("FOO")
@ -5016,6 +5016,19 @@ order (MRO) for bases """
with self.assertRaisesRegex(ValueError, "FOO"): with self.assertRaisesRegex(ValueError, "FOO"):
A().foo A().foo
# test case for gh-103551
class B:
@property
def __getattr__(self, name):
raise ValueError("FOO")
@property
def foo(self):
raise NotImplementedError("BAR")
with self.assertRaisesRegex(NotImplementedError, "BAR"):
B().foo
class DictProxyTests(unittest.TestCase): class DictProxyTests(unittest.TestCase):
def setUp(self): def setUp(self):

View File

@ -0,0 +1 @@
Fix performance loss when accessing an object's attributes with ``__getattr__`` defined.

View File

@ -8306,17 +8306,23 @@ _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name)
if (getattribute == NULL || if (getattribute == NULL ||
(Py_IS_TYPE(getattribute, &PyWrapperDescr_Type) && (Py_IS_TYPE(getattribute, &PyWrapperDescr_Type) &&
((PyWrapperDescrObject *)getattribute)->d_wrapped == ((PyWrapperDescrObject *)getattribute)->d_wrapped ==
(void *)PyObject_GenericGetAttr)) (void *)PyObject_GenericGetAttr)) {
res = PyObject_GenericGetAttr(self, name); res = _PyObject_GenericGetAttrWithDict(self, name, NULL, 1);
else { /* if res == NULL with no exception set, then it must be an
AttributeError suppressed by us. */
if (res == NULL && !PyErr_Occurred()) {
res = call_attribute(self, getattr, name);
}
} else {
Py_INCREF(getattribute); Py_INCREF(getattribute);
res = call_attribute(self, getattribute, name); res = call_attribute(self, getattribute, name);
Py_DECREF(getattribute); Py_DECREF(getattribute);
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
res = call_attribute(self, getattr, name);
}
} }
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
res = call_attribute(self, getattr, name);
}
Py_DECREF(getattr); Py_DECREF(getattr);
return res; return res;
} }