[Python-Dev] 'hasattr' is broken by design

Hrvoje Niksic hrvoje.niksic at avl.com
Tue Aug 24 13:12:45 CEST 2010


On 08/23/2010 04:56 PM, Guido van Rossum wrote:
> On Mon, Aug 23, 2010 at 7:46 AM, Benjamin Peterson<benjamin at python.org>  wrote:
>>  2010/8/23 Yury Selivanov<yselivanov at gmail.com>:
>>>  1) I propose to change 'hasattr' behaviour in Python 3, making it to swallow only AttributeError exceptions (exactly like 'getattr').  Probably, Python 3.2 release is our last chance.
>>
>>  I would be in support of that.
>
> I am cautiously in favor. The existing behavior is definitely a
> mistake and a trap. But it has been depended on for almost 20 years
> now.

I'll note that a similar incompatible change has made it to python2.6. 
This has bitten us in production:

class X(object):
     def __getattr__(self, name):
         raise KeyError, "error looking for %s" % (name,)

     def __iter__(self):
         yield 1

print list(X())

I would expect it to print [1], and in python2.5 it does.  In python2.6 
it raises a KeyError!  The attribute being looked up is an unexpected one:

{hrzagude5003}[~]$ python2.6 a.py
Traceback (most recent call last):
   File "a.py", line 8, in <module>
     print list(X())
   File "a.py", line 3, in __getattr__
     raise KeyError, "error looking for %s" % (name,)
KeyError: 'error looking for __length_hint__'

The __length_hint__ lookup expects either no exception or 
AttributeError, and will propagate others.  I'm not sure if this is a 
bug.  On the one hand, throwing anything except AttributeError from 
__getattr__ is bad style (which is why we fixed the bug by deriving our 
business exception from AttributeError), but the __length_hint__ check 
is supposed to be an internal optimization completely invisible to the 
caller of list().

Being aware that this can be construed as an argument both in favor and 
against the change at hand, my point is that, if propagating 
non-AttributeError exceptions is done in checks intended to be 
invisible, it should certainly be done in hasattr, where it is at least 
obvious what is being done.  Other generic functions and operators, 
including boolean ones such as ==, happily propagate exceptions.

Also, don't expect that this won't break code out there.  It certainly 
will, it's only a matter of assessment whether such code was broken in a 
different, harder to detect way, to begin with.


More information about the Python-Dev mailing list