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