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 <c...@zip.com.au>
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to