__getattribute__'s error is not available in __getattr__

Jason Maldonis jjmaldonis at gmail.com
Tue May 2 14:16:08 EDT 2017


Moving this conversation from python-dev to here because I shouldn't have
sent it to python-dev.

My original message:
I'm working on a large class architecture and I find myself often
overloading __getattr__.  I am continuously running into the issue where I
want __getattr__ to have access to the error that was raised in
__getattribute__, but it seems completely unavailable. Is that true?

What I've learned since then:
There is some weird interplay between __getattribute__, __getattr__, and
python's attribute lookup order which I don't entirely understand.  I've
learned some things from Nick Coghlan and Joao S. O. Bueno, which I'll try
to summarize:

* __getattribute__ does not call __getattr__ within itself; instead,
__getattr__ seems to be called during python's natural attribute lookup
order, but only if an AttributeError is raised during this lookup.
- This has the side effect that calling __getattr__ from __getattribute__
doesn't work properly.

* If __getattr__ is triggered (when an AttributeError is raised during
attribute lookup), it will not have any information about the
AttributeError that triggered it.

* Things may get more complicated if we are doing an attribute lookup on a
property, and an AttributeError is raised within the property's method.
(This is because properties are data descriptors, which complicates
python's natural lookup order.)


Here is the simplest example showing what I mean (there are many more
complicating variations this, and unfortunately I'm running into some of
them):

class A(object):
    def __getattr__(self, attr):
        raise AttributeError("raised from A.__getattr__ to stop execution")

a = A()
print(a.x)

results in:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print(a.x)
  File "test.py", line 3, in __getattr__
    raise AttributeError("raised from A.__getattr__ to stop execution")
AttributeError: raised from A.__getattr__ to stop execution

The thing to note here is that the AttributeError on the normal
__getattribute__'s lookup isn't in the stack trace.  That error is:
Traceback (most recent call last):
  File "test2.py", line 35, in <module>
    print(a.x)
AttributeError: 'A' object has no attribute 'x'

-- that last line, "AttributeError: 'A' object has no attribute 'x'" does
not appear in the stack trace for my above example (because __getattr__ is
implemented).  This is because in python's attribute lookup order,
__getattr__ is called if an AttributeError is raised, and that raised
AttributeError gets completely discarded.

So basically I want access to the intermediate AttributeError that caused
__getattr__ to be raised in the first place.


This is complicated, and I may have explained something poorly. If so,
please don't hesitate to ask for more explanation or examples.  This is
already long, so I'll stop typing now.

Thanks,
Jason



More information about the Python-list mailing list