Skip to content

Commit

Permalink
gh-102213: Optimize the performance of __getattr__ (GH-103761)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
4 people authored May 1, 2023
1 parent 487f55d commit 59c27fa
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 8 deletions.
15 changes: 14 additions & 1 deletion Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5004,7 +5004,7 @@ class Child(Parent):
self.assertEqual(Parent.__subclasses__(), [])

def test_attr_raise_through_property(self):
# add test case for gh-103272
# test case for gh-103272
class A:
def __getattr__(self, name):
raise ValueError("FOO")
Expand All @@ -5016,6 +5016,19 @@ def foo(self):
with self.assertRaisesRegex(ValueError, "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):
def setUp(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix performance loss when accessing an object's attributes with ``__getattr__`` defined.
20 changes: 13 additions & 7 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -8306,17 +8306,23 @@ _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name)
if (getattribute == NULL ||
(Py_IS_TYPE(getattribute, &PyWrapperDescr_Type) &&
((PyWrapperDescrObject *)getattribute)->d_wrapped ==
(void *)PyObject_GenericGetAttr))
res = PyObject_GenericGetAttr(self, name);
else {
(void *)PyObject_GenericGetAttr)) {
res = _PyObject_GenericGetAttrWithDict(self, name, NULL, 1);
/* 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);
res = call_attribute(self, getattribute, name);
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);
return res;
}
Expand Down

0 comments on commit 59c27fa

Please sign in to comment.