From 19c1cd5b352e503c4585398d2533e5ae3bf4c189 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 21 Sep 2001 21:24:49 +0000 Subject: [PATCH] Add the __getattr__ hook back. The rules are now: - if __getattribute__ exists, it is called first; if it doesn't exists, PyObject_GenericGetAttr is called first. - if the above raises AttributeError, and __getattr__ exists, it is called. --- Lib/test/test_descr.py | 9 ++++----- Objects/typeobject.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index bbd4372461c..2f540af648b 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -880,9 +880,8 @@ class C(object): def mygetattr(self, name): if name == "spam": return "spam" - else: - return object.__getattribute__(self, name) - C.__getattribute__ = mygetattr + raise AttributeError + C.__getattr__ = mygetattr verify(a.spam == "spam") a.new = 12 verify(a.new == 12) @@ -1105,11 +1104,11 @@ class B(object): class C(B): - def __getattribute__(self, name): + def __getattr__(self, name): if name == "foo": return ("getattr", name) else: - return B.__getattribute__(self, name) + raise AttributeError def __setattr__(self, name, value): if name == "foo": self.setattr = (name, value) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 5f8fd012e7a..1842f3c855e 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2891,6 +2891,44 @@ slot_tp_getattro(PyObject *self, PyObject *name) return PyObject_CallFunction(getattr, "OO", self, name); } +static PyObject * +slot_tp_getattr_hook(PyObject *self, PyObject *name) +{ + PyTypeObject *tp = self->ob_type; + PyObject *getattr, *getattribute, *res; + static PyObject *getattribute_str = NULL; + static PyObject *getattr_str = NULL; + + if (getattr_str == NULL) { + getattr_str = PyString_InternFromString("__getattr__"); + if (getattr_str == NULL) + return NULL; + } + if (getattribute_str == NULL) { + getattribute_str = + PyString_InternFromString("__getattribute__"); + if (getattribute_str == NULL) + return NULL; + } + getattr = _PyType_Lookup(tp, getattr_str); + getattribute = _PyType_Lookup(tp, getattribute_str); + if (getattr == NULL && getattribute == NULL) { + /* Avoid further slowdowns */ + if (tp->tp_getattro == slot_tp_getattr_hook) + tp->tp_getattro = PyObject_GenericGetAttr; + return PyObject_GenericGetAttr(self, name); + } + if (getattribute == NULL) + res = PyObject_GenericGetAttr(self, name); + else + res = PyObject_CallFunction(getattribute, "OO", self, name); + if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + res = PyObject_CallFunction(getattr, "OO", self, name); + } + return res; +} + static int slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value) { @@ -3197,6 +3235,7 @@ override_slots(PyTypeObject *type, PyObject *dict) TPSLOT("__call__", tp_call, slot_tp_call); TPSLOT("__str__", tp_str, slot_tp_str); TPSLOT("__getattribute__", tp_getattro, slot_tp_getattro); + TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook); TPSLOT("__setattr__", tp_setattro, slot_tp_setattro); TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare); TPSLOT("__le__", tp_richcompare, slot_tp_richcompare);