[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
Tue Mar 1 23:36:41 CST 2011


# 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.

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')):
     raise SystemExit("Mercurial requires Python 2.4 or later.")
 
 if sys.version_info[0] >= 3:
@@ -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:
                 raise
             log.warn("Failed to build optional extension '%s' (skipping)",
                      ext.name)
diff --git a/contrib/win32/hgwebdir_wsgi.py b/contrib/win32/hgwebdir_wsgi.py
--- a/contrib/win32/hgwebdir_wsgi.py
+++ b/contrib/win32/hgwebdir_wsgi.py
@@ -50,7 +50,7 @@
 #sys.path.insert(0, r'c:\path\to\python\lib')
 
 # Enable tracing. Run 'python -m win32traceutil' to debug
-if hasattr(sys, 'isapidllhandle'):
+if getattr(sys, 'isapidllhandle', None):
     import win32traceutil
 
 # To serve pages in local charset instead of UTF-8, remove the two lines below
diff --git a/doc/gendoc.py b/doc/gendoc.py
--- a/doc/gendoc.py
+++ b/doc/gendoc.py
@@ -91,7 +91,7 @@
             ui.write(".. _%s:\n" % name)
         ui.write("\n")
         section(ui, sec)
-        if hasattr(doc, '__call__'):
+        if getattr(doc, '__call__', None):
             doc = doc()
         ui.write(doc)
         ui.write("\n")
diff --git a/hgext/convert/cvsps.py b/hgext/convert/cvsps.py
--- a/hgext/convert/cvsps.py
+++ b/hgext/convert/cvsps.py
@@ -513,8 +513,8 @@
                   e.comment == c.comment and
                   e.author == c.author and
                   e.branch == c.branch and
-                  (not hasattr(e, 'branchpoints') or
-                    not hasattr (c, 'branchpoints') or
+                  (not getattr(e, 'branchpoints', None) or
+                    not getattr (c, 'branchpoints', None) or
                     e.branchpoints == c.branchpoints) and
                   ((c.date[0] + c.date[1]) <=
                    (e.date[0] + e.date[1]) <=
diff --git a/hgext/convert/git.py b/hgext/convert/git.py
--- a/hgext/convert/git.py
+++ b/hgext/convert/git.py
@@ -16,7 +16,7 @@
     # Windows does not support GIT_DIR= construct while other systems
     # cannot remove environment variable. Just assume none have
     # both issues.
-    if hasattr(os, 'unsetenv'):
+    if getattr(os, 'unsetenv', None):
         def gitopen(self, s):
             prevgitdir = os.environ.get('GIT_DIR')
             os.environ['GIT_DIR'] = self.path
diff --git a/hgext/convert/transport.py b/hgext/convert/transport.py
--- a/hgext/convert/transport.py
+++ b/hgext/convert/transport.py
@@ -54,7 +54,7 @@
                 if p:
                     providers.append(p)
     else:
-        if hasattr(svn.client, 'get_windows_simple_provider'):
+        if getattr(svn.client, 'get_windows_simple_provider', None):
             providers.append(svn.client.get_windows_simple_provider(pool))
 
     return svn.core.svn_auth_open(providers, pool)
@@ -73,7 +73,7 @@
         self.password = ''
 
         # Only Subversion 1.4 has reparent()
-        if ra is None or not hasattr(svn.ra, 'reparent'):
+        if ra is None or not getattr(svn.ra, 'reparent', None):
             self.client = svn.client.create_context(self.pool)
             ab = _create_auth_baton(self.pool)
             if False:
diff --git a/hgext/inotify/__init__.py b/hgext/inotify/__init__.py
--- a/hgext/inotify/__init__.py
+++ b/hgext/inotify/__init__.py
@@ -31,7 +31,7 @@
         ui.write(('  %s/\n') % path)
 
 def reposetup(ui, repo):
-    if not hasattr(repo, 'dirstate'):
+    if not getattr(repo, 'dirstate', None):
         return
 
     class inotifydirstate(repo.dirstate.__class__):
diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -904,7 +904,7 @@
                         p.write("# User " + user + "\n")
                     if date:
                         p.write("# Date %s %s\n\n" % date)
-                if hasattr(msg, '__call__'):
+                if getattr(msg, '__call__', None):
                     msg = msg()
                 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
                 n = repo.commit(commitmsg, user, date, match=match, force=True)
diff --git a/hgext/pager.py b/hgext/pager.py
--- a/hgext/pager.py
+++ b/hgext/pager.py
@@ -58,7 +58,7 @@
 from mercurial.i18n import _
 
 def _runpager(p):
-    if not hasattr(os, 'fork'):
+    if not getattr(os, 'fork', None):
         sys.stderr = sys.stdout = util.popen(p, 'wb')
         return
     fdin, fdout = os.pipe()
diff --git a/hgext/relink.py b/hgext/relink.py
--- a/hgext/relink.py
+++ b/hgext/relink.py
@@ -36,7 +36,7 @@
     command is running. (Both repositories will be locked against
     writes.)
     """
-    if not hasattr(util, 'samefile') or not hasattr(util, 'samedevice'):
+    if not getattr(util, 'samefile', None) or not getattr(util, 'samedevice', None):
         raise util.Abort(_('hardlinks are not supported on this system'))
     src = hg.repository(
         hg.remoteui(repo, opts),
diff --git a/mercurial/bookmarks.py b/mercurial/bookmarks.py
--- a/mercurial/bookmarks.py
+++ b/mercurial/bookmarks.py
@@ -138,7 +138,7 @@
 def listbookmarks(repo):
     # We may try to list bookmarks on a repo type that does not
     # support it (e.g., statichttprepository).
-    if not hasattr(repo, '_bookmarks'):
+    if not getattr(repo, '_bookmarks', None):
         return {}
 
     d = {}
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)
 
@@ -170,7 +170,7 @@
         offset is relative to the current position (self.realpos).
         """
         assert offset >= 0
-        if not hasattr(self.fo, 'seek'):
+        if not getattr(self.fo, 'seek', None):
             self._poor_mans_seek(offset)
         else:
             self.fo.seek(self.realpos + offset)
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -235,9 +235,9 @@
     if not pat or pat == '-':
         fp = writable and sys.stdout or sys.stdin
         return os.fdopen(os.dup(fp.fileno()), mode)
-    if hasattr(pat, 'write') and writable:
+    if getattr(pat, 'write', None) and writable:
         return pat
-    if hasattr(pat, 'read') and 'r' in mode:
+    if getattr(pat, 'read', None) and 'r' in mode:
         return pat
     return open(make_filename(repo, pat, node, total, seqno, revwidth,
                               pathname),
@@ -685,7 +685,7 @@
                            revwidth=revwidth, mode='ab')
             if fp != template:
                 shouldclose = True
-        if fp != sys.stdout and hasattr(fp, 'name'):
+        if fp != sys.stdout and getattr(fp, 'name', None):
             repo.ui.note("%s\n" % fp.name)
 
         fp.write("# HG changeset patch\n")
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1351,7 +1351,7 @@
 def debugignore(ui, repo, *values, **opts):
     """display the combined ignore pattern"""
     ignore = repo.dirstate._ignore
-    if hasattr(ignore, 'includepat'):
+    if getattr(ignore, 'includepat', None):
         ui.write("%s\n" % ignore.includepat)
     else:
         raise util.Abort(_("no ignore patterns found"))
@@ -2019,7 +2019,7 @@
         doc = gettext(entry[0].__doc__)
         if not doc:
             doc = _("(no help text available)")
-        if hasattr(entry[0], 'definition'):  # aliased command
+        if getattr(entry[0], 'definition', None):  # aliased command
             if entry[0].definition.startswith('!'):  # shell alias
                 doc = _('shell alias for::\n\n    %s') % entry[0].definition[1:]
             else:
@@ -2092,7 +2092,7 @@
         # description
         if not doc:
             doc = _("(no help text available)")
-        if hasattr(doc, '__call__'):
+        if getattr(doc, '__call__', None):
             doc = doc()
 
         ui.write("%s\n\n" % header)
diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py
--- a/mercurial/demandimport.py
+++ b/mercurial/demandimport.py
@@ -50,7 +50,7 @@
                 h, t = p, None
                 if '.' in p:
                     h, t = p.split('.', 1)
-                if not hasattr(mod, h):
+                if not getattr(mod, h, None):
                     setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
                 elif t:
                     subload(getattr(mod, h), t)
@@ -109,12 +109,12 @@
         mod = _origimport(name, globals, locals)
         # recurse down the module chain
         for comp in name.split('.')[1:]:
-            if not hasattr(mod, comp):
+            if not getattr(mod, comp, None):
                 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
             mod = getattr(mod, comp)
         for x in fromlist:
             # set requested submodules for demand load
-            if not hasattr(mod, x):
+            if not getattr(mod, x, None):
                 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
         return mod
 
diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py
--- a/mercurial/dispatch.py
+++ b/mercurial/dispatch.py
@@ -128,15 +128,15 @@
         elif m in "zlib".split():
             ui.warn(_("(is your Python install correct?)\n"))
     except IOError, inst:
-        if hasattr(inst, "code"):
+        if getattr(inst, "code", None):
             ui.warn(_("abort: %s\n") % inst)
-        elif hasattr(inst, "reason"):
+        elif getattr(inst, "reason", None):
             try: # usually it is in the form (errno, strerror)
                 reason = inst.reason.args[1]
             except: # it might be anything, for example a string
                 reason = inst.reason
             ui.warn(_("abort: error: %s\n") % reason)
-        elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
+        elif getattr(inst, "args", None) and inst.args[0] == errno.EPIPE:
             if ui.debugflag:
                 ui.warn(_("broken pipe\n"))
         elif getattr(inst, "strerror", None):
@@ -182,7 +182,7 @@
     return -1
 
 def aliasargs(fn):
-    if hasattr(fn, 'args'):
+    if getattr(fn, 'args', None):
         return fn.args
     return []
 
@@ -295,7 +295,7 @@
             ui.debug("alias '%s' shadows command '%s'\n" %
                      (self.name, self.cmdname))
 
-        if hasattr(self, 'shell'):
+        if getattr(self, 'shell', None):
             return self.fn(ui, *args, **opts)
         else:
             try:
@@ -466,7 +466,7 @@
     cmd = aliases[0]
     fn = entry[0]
 
-    if cmd and hasattr(fn, 'shell'):
+    if cmd and getattr(fn, 'shell', None):
         d = lambda: fn(ui, *args[1:])
         return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
 
diff --git a/mercurial/encoding.py b/mercurial/encoding.py
--- a/mercurial/encoding.py
+++ b/mercurial/encoding.py
@@ -136,11 +136,10 @@
 def colwidth(s):
     "Find the column width of a UTF-8 string for display"
     d = s.decode(encoding, 'replace')
-    if hasattr(unicodedata, 'east_asian_width'):
+    if getattr(unicodedata, 'east_asian_width', None):
         wide = "WF"
         if ambiguous == "wide":
             wide = "WFA"
         w = unicodedata.east_asian_width
         return sum([w(c) in wide and 2 or 1 for c in d])
     return len(d)
-
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -120,7 +120,7 @@
     where orig is the original (wrapped) function, and *args, **kwargs
     are the arguments passed to it.
     '''
-    assert hasattr(wrapper, '__call__')
+    assert getattr(wrapper, '__call__', None)
     aliases, entry = cmdutil.findcmd(command, table)
     for alias, e in table.iteritems():
         if e is entry:
@@ -173,12 +173,12 @@
     your end users, you should play nicely with others by using the
     subclass trick.
     '''
-    assert hasattr(wrapper, '__call__')
+    assert getattr(wrapper, '__call__', None)
     def wrap(*args, **kwargs):
         return wrapper(origfn, *args, **kwargs)
 
     origfn = getattr(container, funcname)
-    assert hasattr(origfn, '__call__')
+    assert getattr(origfn, '__call__', None)
     setattr(container, funcname, wrap)
     return origfn
 
diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py
--- a/mercurial/fancyopts.py
+++ b/mercurial/fancyopts.py
@@ -75,7 +75,7 @@
         # copy defaults to state
         if isinstance(default, list):
             state[name] = default[:]
-        elif hasattr(default, '__call__'):
+        elif getattr(default, '__call__', None):
             state[name] = None
         else:
             state[name] = default
diff --git a/mercurial/help.py b/mercurial/help.py
--- a/mercurial/help.py
+++ b/mercurial/help.py
@@ -67,7 +67,7 @@
     """Return a delayed loader for help/topic.txt."""
 
     def loader():
-        if hasattr(sys, 'frozen'):
+        if getattr(sys, 'frozen', None):
             module = sys.executable
         else:
             module = __file__
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -437,7 +437,7 @@
 
         displayer.close()
     finally:
-        if hasattr(other, 'close'):
+        if getattr(other, 'close', None):
             other.close()
         if bundle:
             os.unlink(bundle)
@@ -526,7 +526,7 @@
 
 def remoteui(src, opts):
     'build a remote ui from ui or repo and opts'
-    if hasattr(src, 'baseui'): # looks like a repository
+    if getattr(src, 'baseui', None): # looks like a repository
         dst = src.baseui.copy() # drop repo-specific config
         src = src.ui # copy target options from repo
     else: # assume it's a global ui object
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -138,7 +138,7 @@
                 cmd = cmd[style + 1:]
 
             # avoid accepting e.g. style parameter as command
-            if hasattr(webcommands, cmd):
+            if getattr(webcommands, cmd, None):
                 req.form['cmd'] = [cmd]
             else:
                 cmd = ''
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -101,7 +101,7 @@
             self.headers = []
 
     def write(self, thing):
-        if hasattr(thing, "__iter__"):
+        if getattr(thing, "__iter__", None):
             for part in thing:
                 self.write(part)
         else:
diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py
--- a/mercurial/hgweb/server.py
+++ b/mercurial/hgweb/server.py
@@ -238,7 +238,7 @@
     from threading import activeCount
     _mixin = SocketServer.ThreadingMixIn
 except ImportError:
-    if hasattr(os, "fork"):
+    if getattr(os, "fork", None):
         _mixin = SocketServer.ForkingMixIn
     else:
         class _mixin:
diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py
--- a/mercurial/hgweb/webutil.py
+++ b/mercurial/hgweb/webutil.py
@@ -71,7 +71,7 @@
         d['date'] = s.date()
         d['description'] = s.description()
         d['branch'] = s.branch()
-        if hasattr(s, 'path'):
+        if getattr(s, 'path', None):
             d['file'] = s.path()
         yield d
 
diff --git a/mercurial/hgweb/wsgicgi.py b/mercurial/hgweb/wsgicgi.py
--- a/mercurial/hgweb/wsgicgi.py
+++ b/mercurial/hgweb/wsgicgi.py
@@ -73,5 +73,5 @@
         for chunk in content:
             write(chunk)
     finally:
-        if hasattr(content, 'close'):
+        if getattr(content, 'close', None):
             content.close()
diff --git a/mercurial/hook.py b/mercurial/hook.py
--- a/mercurial/hook.py
+++ b/mercurial/hook.py
@@ -21,14 +21,14 @@
 
     ui.note(_("calling hook %s: %s\n") % (hname, funcname))
     obj = funcname
-    if not hasattr(obj, '__call__'):
+    if not getattr(obj, '__call__', None):
         d = funcname.rfind('.')
         if d == -1:
             raise util.Abort(_('%s hook is invalid ("%s" not in '
                                'a module)') % (hname, funcname))
         modname = funcname[:d]
         oldpaths = sys.path
-        if hasattr(sys, "frozen"):
+        if getattr(sys, "frozen", None):
             # binary installs require sys.path manipulation
             modpath, modfile = os.path.split(modname)
             if modpath and modfile:
@@ -60,7 +60,7 @@
             raise util.Abort(_('%s hook is invalid '
                                '("%s" is not defined)') %
                              (hname, funcname))
-        if not hasattr(obj, '__call__'):
+        if not getattr(obj, '__call__', None):
             raise util.Abort(_('%s hook is invalid '
                                '("%s" is not callable)') %
                              (hname, funcname))
@@ -90,7 +90,7 @@
 
     env = {}
     for k, v in args.iteritems():
-        if hasattr(v, '__call__'):
+        if getattr(v, '__call__', None):
             v = v()
         if isinstance(v, dict):
             # make the dictionary element order stable across Python
@@ -136,7 +136,7 @@
         for hname, cmd in ui.configitems('hooks'):
             if hname.split('.')[0] != name or not cmd:
                 continue
-            if hasattr(cmd, '__call__'):
+            if getattr(cmd, '__call__', None):
                 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
             elif cmd.startswith('python:'):
                 if cmd.count(':') >= 2:
diff --git a/mercurial/httprepo.py b/mercurial/httprepo.py
--- a/mercurial/httprepo.py
+++ b/mercurial/httprepo.py
@@ -44,7 +44,7 @@
     def __del__(self):
         for h in self.urlopener.handlers:
             h.close()
-            if hasattr(h, "close_all"):
+            if getattr(h, "close_all", None):
                 h.close_all()
 
     def url(self):
diff --git a/mercurial/i18n.py b/mercurial/i18n.py
--- a/mercurial/i18n.py
+++ b/mercurial/i18n.py
@@ -9,7 +9,7 @@
 import gettext, sys, os
 
 # modelled after templater.templatepath:
-if hasattr(sys, 'frozen'):
+if getattr(sys, 'frozen', None):
     module = sys.executable
 else:
     module = __file__
diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py
--- a/mercurial/keepalive.py
+++ b/mercurial/keepalive.py
@@ -547,7 +547,7 @@
         print "send:", repr(str)
     try:
         blocksize = 8192
-        if hasattr(str,'read') :
+        if getattr(str,'read', None) :
             if self.debuglevel > 0:
                 print "sendIng a read()able"
             data = str.read(blocksize)
diff --git a/mercurial/lsprof.py b/mercurial/lsprof.py
--- a/mercurial/lsprof.py
+++ b/mercurial/lsprof.py
@@ -86,7 +86,7 @@
         for k, v in list(sys.modules.iteritems()):
             if v is None:
                 continue
-            if not hasattr(v, '__file__'):
+            if not getattr(v, '__file__', None):
                 continue
             if not isinstance(v.__file__, str):
                 continue
diff --git a/mercurial/mail.py b/mercurial/mail.py
--- a/mercurial/mail.py
+++ b/mercurial/mail.py
@@ -37,7 +37,7 @@
     # backward compatible: when tls = true, we use starttls.
     starttls = tls == 'starttls' or util.parsebool(tls)
     smtps = tls == 'smtps'
-    if (starttls or smtps) and not hasattr(socket, 'ssl'):
+    if (starttls or smtps) and not getattr(socket, 'ssl', None):
         raise util.Abort(_("can't use TLS: Python SSL support not installed"))
     if smtps:
         ui.note(_('(using smtps)\n'))
diff --git a/mercurial/patch.py b/mercurial/patch.py
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -143,7 +143,7 @@
 
     mimeheaders = ['content-type']
 
-    if not hasattr(stream, 'next'):
+    if not getattr(stream, 'next', None):
         # http responses, for example, have readline but not next
         stream = fiter(stream)
 
diff --git a/mercurial/revlog.py b/mercurial/revlog.py
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -229,7 +229,7 @@
         self._nodepos = None
 
         v = REVLOG_DEFAULT_VERSION
-        if hasattr(opener, 'options') and 'defversion' in opener.options:
+        if getattr(opener, 'options', None) and 'defversion' in opener.options:
             v = opener.options['defversion']
             if v & REVLOGNG:
                 v |= REVLOGNGINLINEDATA
diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py
--- a/mercurial/statichttprepo.py
+++ b/mercurial/statichttprepo.py
@@ -31,10 +31,10 @@
         try:
             f = self.opener.open(req)
             data = f.read()
-            if hasattr(f, 'getcode'):
+            if getattr(f, 'getcode', None):
                 # python 2.6+
                 code = f.getcode()
-            elif hasattr(f, 'code'):
+            elif getattr(f, 'code', None):
                 # undocumented attribute, seems to be set in 2.4 and 2.5
                 code = f.code
             else:
diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -177,22 +177,22 @@
 def reporelpath(repo):
     """return path to this (sub)repo as seen from outermost repo"""
     parent = repo
-    while hasattr(parent, '_subparent'):
+    while getattr(parent, '_subparent', None):
         parent = parent._subparent
     return repo.root[len(parent.root)+1:]
 
 def subrelpath(sub):
     """return path to this subrepo as seen from outermost repo"""
-    if hasattr(sub, '_relpath'):
+    if getattr(sub, '_relpath', None):
         return sub._relpath
-    if not hasattr(sub, '_repo'):
+    if not getattr(sub, '_repo', None):
         return sub._path
     return reporelpath(sub._repo)
 
 def _abssource(repo, push=False, abort=True):
     """return pull/push path of repo - either based on parent repo .hgsub info
     or on the top repo config. Abort or return None if no source found."""
-    if hasattr(repo, '_subparent'):
+    if getattr(repo, '_subparent', None):
         source = repo._subsource
         if source.startswith('/') or '://' in source:
             return source
@@ -209,7 +209,7 @@
             else: # plain file system path
                 return posixpath.normpath(os.path.join(parent, repo._subsource))
     else: # recursion reached top repo
-        if hasattr(repo, '_subtoppath'):
+        if getattr(repo, '_subtoppath', None):
             return repo._subtoppath
         if push and repo.ui.config('paths', 'default-push'):
             return repo.ui.config('paths', 'default-push')
diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py
--- a/mercurial/templatefilters.py
+++ b/mercurial/templatefilters.py
@@ -10,7 +10,7 @@
 
 def stringify(thing):
     '''turn nested template iterator into string.'''
-    if hasattr(thing, '__iter__') and not isinstance(thing, str):
+    if getattr(thing, '__iter__', None) and not isinstance(thing, str):
         return "".join([stringify(t) for t in thing if t is not None])
     return str(thing)
 
@@ -166,13 +166,13 @@
         return '"%s"' % jsonescape(u)
     elif isinstance(obj, unicode):
         return '"%s"' % jsonescape(obj)
-    elif hasattr(obj, 'keys'):
+    elif getattr(obj, 'keys', None):
         out = []
         for k, v in obj.iteritems():
             s = '%s: %s' % (json(k), json(v))
             out.append(s)
         return '{' + ', '.join(out) + '}'
-    elif hasattr(obj, '__iter__'):
+    elif getattr(obj, '__iter__', None):
         out = []
         for i in obj:
             out.append(json(i))
diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -136,7 +136,7 @@
     v = mapping.get(key)
     if v is None:
         v = context._defaults.get(key, '')
-    if hasattr(v, '__call__'):
+    if getattr(v, '__call__', None):
         return v(**mapping)
     return v
 
@@ -201,14 +201,14 @@
     '''yield a single stream from a possibly nested set of iterators'''
     if isinstance(thing, str):
         yield thing
-    elif not hasattr(thing, '__iter__'):
+    elif not getattr(thing, '__iter__', None):
         if thing is not None:
             yield str(thing)
     else:
         for i in thing:
             if isinstance(i, str):
                 yield i
-            elif not hasattr(i, '__iter__'):
+            elif not getattr(i, '__iter__', None):
                 if i is not None:
                     yield str(i)
             elif i is not None:
@@ -339,7 +339,7 @@
     normpaths = []
 
     # executable version (py2exe) doesn't support __file__
-    if hasattr(sys, 'frozen'):
+    if getattr(sys, 'frozen', None):
         module = sys.executable
     else:
         module = __file__
diff --git a/mercurial/url.py b/mercurial/url.py
--- a/mercurial/url.py
+++ b/mercurial/url.py
@@ -303,7 +303,7 @@
             orgsend(self, data)
     return _sendfile
 
-has_https = hasattr(urllib2, 'HTTPSHandler')
+has_https = getattr(urllib2, 'HTTPSHandler', None)
 if has_https:
     try:
         # avoid using deprecated/broken FakeSocket in python 2.6
@@ -377,7 +377,7 @@
 # general transaction handler to support different ways to handle
 # HTTPS proxying before and after Python 2.6.3.
 def _generic_start_transaction(handler, h, req):
-    if hasattr(req, '_tunnel_host') and req._tunnel_host:
+    if getattr(req, '_tunnel_host', None) and req._tunnel_host:
         tunnel_host = req._tunnel_host
         if tunnel_host[:7] not in ['http://', 'https:/']:
             tunnel_host = 'https://' + tunnel_host
@@ -572,7 +572,7 @@
             else:
                 self.sock = _ssl_wrap_socket(self.sock, self.key_file,
                     self.cert_file)
-                if hasattr(self.sock, 'getpeercert'):
+                if getattr(self.sock, 'getpeercert', None):
                     peercert = self.sock.getpeercert(True)
                     peerfingerprint = util.sha1(peercert).hexdigest()
                     nicefingerprint = ":".join([peerfingerprint[x:x + 2]
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -352,7 +352,7 @@
     (portable, not much used).
     """
     return (hasattr(sys, "frozen") or # new py2exe
-            hasattr(sys, "importers") or # old py2exe
+            getattr(sys, "importers", None) or # old py2exe
             imp.is_frozen("__main__")) # tools/freeze
 
 def hgexecutable():
@@ -1271,7 +1271,7 @@
     def errhandler(err):
         if err.filename == path:
             raise err
-    if followsym and hasattr(os.path, 'samestat'):
+    if followsym and getattr(os.path, 'samestat', None):
         def _add_dir_if_not_there(dirlst, dirname):
             match = False
             samestat = os.path.samestat
@@ -1483,7 +1483,7 @@
     def handler(signum, frame):
         terminated.add(os.wait())
     prevhandler = None
-    if hasattr(signal, 'SIGCHLD'):
+    if getattr(signal, 'SIGCHLD', None):
         prevhandler = signal.signal(signal.SIGCHLD, handler)
     try:
         pid = spawndetached(args)
diff --git a/mercurial/windows.py b/mercurial/windows.py
--- a/mercurial/windows.py
+++ b/mercurial/windows.py
@@ -135,7 +135,7 @@
 def set_binary(fd):
     # When run without console, pipes may expose invalid
     # fileno(), usually set to -1.
-    if hasattr(fd, 'fileno') and fd.fileno() >= 0:
+    if getattr(fd, 'fileno', None) and fd.fileno() >= 0:
         msvcrt.setmode(fd.fileno(), os.O_BINARY)
 
 def pconvert(path):
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@
 # 'python setup.py --help' for more options
 
 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'):
     raise SystemExit("Mercurial requires Python 2.4 or later.")
 
 if sys.version_info[0] >= 3:
diff --git a/tests/sitecustomize.py b/tests/sitecustomize.py
--- a/tests/sitecustomize.py
+++ b/tests/sitecustomize.py
@@ -1,6 +1,6 @@
 try:
     import coverage
-    if hasattr(coverage, 'process_startup'):
+    if getattr(coverage, 'process_startup', None):
         coverage.process_startup()
 except ImportError:
     pass
diff --git a/tests/test-symlink-os-yes-fs-no.py b/tests/test-symlink-os-yes-fs-no.py
--- a/tests/test-symlink-os-yes-fs-no.py
+++ b/tests/test-symlink-os-yes-fs-no.py
@@ -4,7 +4,7 @@
 TESTDIR = os.environ["TESTDIR"]
 
 # only makes sense to test on os which supports symlinks
-if not hasattr(os, "symlink"):
+if not getattr(os, "symlink", None):
     sys.exit(80) # SKIPPED_STATUS defined in run-tests.py
 
 # clone with symlink support
diff --git a/tests/test-walkrepo.py b/tests/test-walkrepo.py
--- a/tests/test-walkrepo.py
+++ b/tests/test-walkrepo.py
@@ -5,7 +5,7 @@
 from os.path import join as pjoin
 
 u = ui.ui()
-sym = hasattr(os, 'symlink') and hasattr(os.path, 'samestat')
+sym = getattr(os, 'symlink', None) and getattr(os.path, 'samestat', None)
 
 hg.repository(u, 'top1', create=1)
 mkdir('subdir')
diff --git a/tests/tinyproxy.py b/tests/tinyproxy.py
--- a/tests/tinyproxy.py
+++ b/tests/tinyproxy.py
@@ -23,7 +23,8 @@
 
     def handle(self):
         (ip, port) =  self.client_address
-        if hasattr(self, 'allowed_clients') and ip not in self.allowed_clients:
+        if (getattr(self, 'allowed_clients', None)
+            and ip not in self.allowed_clients):
             self.raw_requestline = self.rfile.readline()
             if self.parse_request():
                 self.send_error(403)


More information about the Mercurial-devel mailing list