Beware of Property Descriptor and __getattr__

What’s wrong with this code?

class C:

  def __init__(self, id):
    self.id = id

  def getdata(self):
    if not hasattr(self, '_data')
      temp = longComputation()
      self._data = temp.value()
    return self._data

  def __getattr__(self,  name):
    return databaseLookup(name, 'ID=%s' % self.id)

  data = property(getdata)

We were getting intermittent errors where

c = C(82201)
print c.data
... SQL error "column data does not exist"

It turns out that longComputation() had a bug, and returned a None sometimes. This caused temp.value() to fail with AttributeError.
The AttributeError caused python to fallback to __getattr__ , which uses the database lookup routine, which tried to look up a column which does not exist.

The moral of the story?
surround property accessors with try…except AttributeError, log the error and raise it as a different kind of exception.

Update 20-Nov-2006: Fixed up the syntax error. Thanks Ben.

Link Exchanges:
Just Web Design. Ford automotive panel van utility trucks

2 Responses to “Beware of Property Descriptor and __getattr__”

  1. Ben Bangert writes:

    I’m a little confused how the code you pasted even works. A getter for a property doesn’t take an argument, and when I paste the code you have into an interpreter, sure enough I get:

    >>> print c.data
    Traceback (most recent call last):
    File “”, line 1, in ?
    TypeError: getdata() takes exactly 2 arguments (1 given)

    Beyond that though is another problem, your getattr method is called by hasattr itself, so it will never actually execute your block since hasattr(self, ‘_data’) will call __getattr__ with ‘_data’ as the name, which then causes the db lookup which failed.

    Generally if I have a getattr and I’m looking to see if something is present, I use the underlying __dict__, so for the getdata function:
    def getdata(self):
    if ‘_data’ not in self.__dict__:

    Would do the trick and avoid calling the getattr. Plus, you avoid accidentally having hasattr call your getattr override.

    Cheers,
    Ben

  2. Chui writes:

    Thank Ben, I was working off memory, plus the original code was really ugly.

Leave a Reply