Eric Snow added the comment:

Got bit by a variation of this today in 2.7:


class Spam(object):
    def __getattr__(self, attr):
        raise AttributeError(attr)
    @property
    def eggs(self):
        return self.ham

s = Spam()
s.eggs
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __getattr__
AttributeError: eggs


It took me a little while to figure out what was going on.  A real 
head-scratcher.  This is because the AttributeError was attributed to the 
property, but was actually caused by the __getattr__ call triggered by the 
property's code.  I would expect the AttributeError to reference "ham", not 
"eggs".  As already noted, if __getattr__() is not there, that's what happens.

Regardless, I'm just not seeing where the hurdle is to improving this behavior. 
 I certainly agree that this is not a feature.  It is the source of very 
mysterious failures.

I was surprised that AttributeError does not have an attribute to which the 
name would be bound.  If it did, then slot_tp_getattr_hook() could check 
against that:


    if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
        PyObject *tp, *exc, *tb, *exc_attr;

        PyErr_Fetch(&tp, &exc, &tb);
        exc_attr = PyObject_GetAttrString(exc, "attribute");
        PyErr_Restore(tp, exc, tb);

        if (!exc_attr || exc_attr == name) { 
            PyErr_Clear();
            res = call_attribute(self, getattr, name);
        }
        Py_XDECREF(exc_attr);
    }


Alternatively, when an AttributeError comes out of a getter in 
_PyObject_GenericSetAttrWithDict() (in either spot they're called), another 
exception (not AttributeError) could be raised with the original chained onto 
it.  Then slot_tp_getattr_hook() won't silently ignore it.  It would be 
something like this:


        if (f != NULL && PyDescr_IsData(descr)) {
            res = f(descr, obj, value);
            if (!res && PyErr_ExceptionMatches(PyExc_AttributeError)) {
                PyObject *msg = PyUnicode_FromFormat("getter failed for '%U'", 
name);
                /* implicit chaining here */
                PyErr_SetObject(PyExc_???Error, msg);
            }
            goto done;
        }


Conceptually, it's as though locating the attribute and extracting the value 
are lumped together here.  Distinguishing the two would help make this failure 
situation much less confusing.

Additionally, it would be really helpful to have a brief mention of this 
behavior (AttributeErrors in getters falling back to __getattr__) in the 
language reference entry for __getattr__ and/or descriptors.

----------
nosy: +eric.snow
versions: +Python 3.4

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue1615>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to