Today I wasted a lot of time trying to figure out why a class using both a __getattr__ and a property mysteriously failed. The short version is: Make sure you don't raise an AttributeError in the property.fget()

The start point of this horrible voyage was a class which looked roughly like this:

class Foo(object): def __getattr__(self, name): return self._container.get(name, 'some_default') @property def foo(self): val = self._container.get(foo) if test(val): return some_helper(val) return val

This sees fine enough. Only it turns out that some_helper() raised an AttributeError for some invalid input. Certainly reasonable since it was never meant to deal with incorrect input, that was meant to have been sanitised already (that was a bug in the caller which was actually just an incorrect unittest). The main gotcha was that it seems that python doesn't just check whether a "foo" is present in all the relevant __dict__ 's along the mro. Instead it seems to use getattr(inst, "foo") and then delegate to __getattr__() if it gets an AttributeError. Now suddenly finding a bug in some_helper() has turned into a puzzling question as to why __getattr__() was called.

Personally I can't see why it doesn't use the mro to statically look up the required object instead of using the AttributeError-swallowing approach. But maybe there's a good reason.

Friday, June 17, 2011 | Labels: python |