today's gotcha: @property and AttributeError

Cameron Simpson cs at zip.com.au
Sat Nov 12 18:37:24 EST 2016


I've just spent a long time debugging what I imagined was a class inheritance 
issue, but which was actually a lack of deep understanding of @property on my 
part.

TL;DR: I am now using this @prop decorator instead of @property in an 
increasing amount of my code:

  def prop(func):
    ''' The builtin @property decorator lets internal AttributeErrors escape.
        While that can support properties that appear to exist conditionally,
        in practice this is almost never what I want, and it masks deeper errors.
        Hence this wrapper for @property that transmutes internal AttributeErrors
        into RuntimeErrors.
    '''
    def wrapper(*a, **kw):
      try:
        return func(*a, **kw)
      except AttributeError as e:
        raise RuntimeError("inner function %s raised %s" % (func, e))
    return property(wrapper)

I was debugging a class with a __getattr__ in the base class and a property in 
the super class, and wondering why the property was not being found. (This was 
a simplification from a dynamicly constructed class with mixins with the same 
problem.)

The root cause was apiece of code like this:

  @property
  def foo(self):
    return self.bah(...blah...)

where self.bah did not exist. This raised AttributeError naturally enough, and 
when a @property has an AttributeError occur the property appears not to exist 
when resolving "foo". And the codefell through to the __getattr_ method from 
the base class, confusing me greatly, because I was looking for a bad MRO 
situation instead of a bug inside the .foo property.

In a sense this is a particular case of a general issue when using exceptions: 
from outside a function one doesn't inherently know if a raised exception is 
"shallow" - from the function one just called, or "deep" - from the inner 
workings of some function deeper in the call stack.

Most of the time this is fine, but when the raised exception is handled my a 
mechanism like @property - in this case to indicate that such a property does 
not exist, a "deep" exception indicating a bug is handled just like a "shallow" 
exception indicating "no property here thanks".

Now that I know what's going on there are plenty of related anecdote apparent 
to web searches, but none offers an alternative to @property such as the code 
above, so I thought I'd share it.

I appreciate that there may be times when I _want_ to raise AttributeError from 
a property to "conceal" it in particular circumstances, but at present that is 
never what i want, and @prop gives me far more debuggable code.

Cheers,
Cameron Simpson <cs at zip.com.au>



More information about the Python-list mailing list