How to robustly check a Python property exists?

101 picture 101 · May 19, 2016 · Viewed 7.1k times · Source

Given the following class (with a buggy property) then what is the best foolproof way of checking that the bar property exists?

class Foo(object):
    @property
    def bar(self):
        raise AttributeError('unforeseen attribute error!')

Both hasattr and getattr fail and only dir works:

foo = Foo()

print hasattr(foo, 'bar')
# False

try:
    getattr(foo, 'bar')
    print True
except AttributeError as e:
    print False
# False    

print 'bar' in dir(foo)
# True

The best all round solution I can think of is:

def robust_hasattr(obj, attr):
    return hasattr(obj, attr) or attr in dir(obj)

Is there a better way?

Answer

Martijn Pieters picture Martijn Pieters · May 19, 2016

If you have a buggy property, fix the bug. If raising AttributeError is a bug, then make the property not do that. Raising that exception is the way to signal that you should not be using that attribute.

Using dir() can be a work-around, but it is not foolproof, as dir() is a debugging aid that can both omit information and can be overridden by the object.__dir__ hook (giving your code another vector to introduce bugs). Then there is the possibility of a buggy object.__getattr__ hook, a buggy object.__getattribute__ hook, or even descriptors on the metaclass, all of which would not be detectable by using dir().

Since you are specifically looking for a property, look for the same attribute on the class of your object:

hasattr(foo, 'bar') or isinstance(getattr(type(foo), 'bar', None), property)

For your specific case, the above returns True:

>>> class Foo(object):
...     @property
...     def bar(self):
...         raise AttributeError('unforeseen attribute error!')
...
>>> foo = Foo()
>>> hasattr(foo, 'bar') or isinstance(getattr(type(foo), 'bar', None), property)
True

because there indeed is such a property object on the class.