[Python-Dev] 'hasattr' is broken by design
Yury Selivanov
yselivanov at gmail.com
Mon Aug 23 22:00:29 CEST 2010
On 2010-08-23, at 3:47 PM, Raymond Hettinger wrote:
>
> On Aug 23, 2010, at 7:22 AM, Yury Selivanov wrote:
>
>> I know the issue has been discussed several times already, however I couldn't find any reasonable explanation of its strange behaviour. The main problem with 'hasattr' function is that is swallows all exceptions derived from Exception class. It's a good thing that it doesn't do that with BaseException as it was fixed not a long time ago, but it's definitely not enough.
>>
>> First of all, this behaviour of 'hasattr' contradicts with the very core principle of python: "Errors should never pass silently." And since 'hasattr' function is in builtins module and is a widely used function it impacts the whole language.
>>
>> Secondly, take a look at the following:
>>
>>>>> class Test:
>> ... @property
>> ... def attr(self):
>> ... self['foo']
>> ...
>>>>> hasattr(Test(), 'attr')
>> False
>>
>> There can be any exception instead of KeyError in the above snippet of code, but this small example shows how 'hasattr': misleadingly breaks the code logic (1) and masks bug (2). And that's the simplest possible example, there are much more in real life.
>>
>> While (1) is maybe acceptable for someone, there is no excuse for the (2). Moreover, current 'hasattr' behaviour tremendously complicates use of '__getattribute__' magic. And forget about importlib magic with LazyImports, one 'hasattr' ruins everything by catching ImportError.
>>
>>
>> To conclude:
>>
>> 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.
>>
>> 2) If you afraid that this new behaviour will break too much python 2 code converted with 2to3, we can introduce another 'hasattr' function defined in 2to3 module itself, and make it imported automatically in all files passed through 2to3 transformation pipeline. This new function will mimic 'hasattr' behaviour from python 2 and converted code should work as expected.
>
>
> Thanks for the nice analysis and good example.
>
> I disagree with the solution though. If we want to see the exceptions associated
> with actually getting an attribute, then using getattr() instead is a perfectly
> reasonable solution that people can already use without a language change.
>
> But hasattr() has a far different set of use cases, so we should explore
> an alternate solution to the problem. The usual reason that people use
> hasattr() instead of getattr() is that they want to check for the presence of
> of a method/attribute without actually running it, binding it, or triggering
> any other behavior.
>
> As your example shows, property() defeats this intent by actually executing
> the code. A better behavior would not run the code at all. It would check
> the dictionaries along the MRO but not execute any descriptors associated
> with a given key.
>
> IMO, this is a much better solution, more in line with known use cases
> for hasattr(). If the proposed change when through, it would fail to
> address the common use case and cause people to start writing their
> own versions of hasattr() that just scan but do not run code.
This is impossible to implement because of '__getattribute__' and '__getattr__' methods. There is no way of detecting the presence of an attribute but trying to get it through 'getattr'. Partial solution like getting information about property presence through __dict__ and call __getattribute__/__getattr__ if they are defined wouldn't work either.
So, your solution would make 'hasattr' even more incompatible.
-
Yury Selivanov
More information about the Python-Dev
mailing list