[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