[PATCH 2 of 3 RFC] Remove all uses of hasattr(a, b) in favor of getattr(a, b, None)

Augie Fackler durin42 at gmail.com
Mon Mar 7 18:42:27 CST 2011


On Mar 3, 2011, at 8:03 AM, Michael Haggerty wrote:
> 
> On 03/02/2011 06:36 AM, Augie Fackler wrote:
>> # HG changeset patch
>> # User Augie Fackler <durin42 at gmail.com>
>> # Date 1299024193 21600
>> # Branch stable
>> # Node ID 75e012dfe383900f1932bdb0831eb44a11d35876
>> # Parent  f202bb8bc5e81a41893144065cc634a786d549b0
>> Remove all uses of hasattr(a, b) in favor of getattr(a, b, None)
>> 
>> hasattr() in Python swallows all exceptions rather than only
>> AttributeError, making it unsafe for general use. getattr(a, b, None)
>> is a mostly-compatible replacement (it fails to distinguish missing
>> attributes and None-value attributes) that we can use instead.
> 
> I understand that you were doing a largely automatic replacement of
> hasattr(), but there are several places where the default value of
> getattr() can be set more usefully than None to shorten the code and
> avoid a double-lookup.  A few examples:

Good catch. I'll re-roll the series as a larger batch of patches and make sure to get this optimization in.

> 
>> diff --git a/contrib/setup3k.py b/contrib/setup3k.py
>> --- a/contrib/setup3k.py
>> +++ b/contrib/setup3k.py
>> @@ -8,7 +8,8 @@
>> from lib2to3.refactor import get_fixers_from_package as getfixers
>> 
>> import sys
>> -if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
>> +if (not getattr(sys, 'version_info', None)
>> +    or sys.version_info < (2, 4, 0, 'final')):
> 
> if getattr(sys, 'version_info', (0,)) < (2, 4, 0, 'final'):
> 
>> @@ -236,7 +237,7 @@
>>         try:
>>             build_ext.build_extension(self, ext)
>>         except CCompilerError:
>> -            if not hasattr(ext, 'optional') or not ext.optional:
>> +            if not getattr(ext, 'optional', None) or not ext.optional:
> 
> if not getattr(ext, 'optional', False):
> 
>> diff --git a/mercurial/byterange.py b/mercurial/byterange.py
>> --- a/mercurial/byterange.py
>> +++ b/mercurial/byterange.py
>> @@ -103,7 +103,7 @@
>>         """This effectively allows us to wrap at the instance level.
>>         Any attribute not found in _this_ object will be searched for
>>         in self.fo.  This includes methods."""
>> -        if hasattr(self.fo, name):
>> +        if getattr(self.fo, name, None):
>>             return getattr(self.fo, name)
>>         raise AttributeError(name)
> 
> Wouldn't it be enough here just to
> 
>    # If this raises AttributeError, let it escape:
>    return getattr(self.fo, name)
> 
>> diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
>> --- a/mercurial/dispatch.py
>> +++ b/mercurial/dispatch.py
>> @@ -182,7 +182,7 @@
>>     return -1
>> 
>> def aliasargs(fn):
>> -    if hasattr(fn, 'args'):
>> +    if getattr(fn, 'args', None):
>>         return fn.args
>>     return []
> 
> return getattr(fn, 'args', [])
> 
> etc.
> 
> For the other cases, maybe it would make sense to define a
> 
> def safe_hasattr(o, name):
>    try:
>        getattr(o, name)
>    except AttributeError:
>        return False
>    else:
>        return True

Does anyone in the wider mercurial-devel have a feeling on this one? It seems reasonable as a hedge against the day in about 2050 that we can be Python3 only...

> 
> Such a helper function would be safer (no problems with values that
> evaluate to False), more obvious, and easier to replace if/when
> Mercurial migrates to Python 3.
> 
> Michael





More information about the Mercurial-devel mailing list