merge with crew
authorMatt Mackall <mpm@selenic.com>
Thu, 28 Jul 2011 14:20:06 -0500
changeset 14979 a5046880eca3
parent 14940 d78b92353f26 (current diff)
parent 14978 5a0fdc715769 (diff)
child 14983 6312171468f7
merge with crew
--- a/contrib/check-code.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/contrib/check-code.py	Thu Jul 28 14:20:06 2011 -0500
@@ -148,7 +148,7 @@
     (r'(?<!def)\s+(any|all|format)\(',
      "any/all/format not available in Python 2.4"),
     (r'(?<!def)\s+(callable)\(',
-     "callable not available in Python 3, use hasattr(f, '__call__')"),
+     "callable not available in Python 3, use getattr(f, '__call__', None)"),
     (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
     (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
      "gratuitous whitespace after Python keyword"),
@@ -168,6 +168,8 @@
      "comparison with singleton, use 'is' or 'is not' instead"),
     (r'^\s*(while|if) [01]:',
      "use True/False for constant Boolean expression"),
+    (r'(?<!def)\s+hasattr',
+     'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
     (r'opener\([^)]*\).read\(',
      "use opener.read() instead"),
     (r'opener\([^)]*\).write\(',
--- a/contrib/setup3k.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/contrib/setup3k.py	Thu Jul 28 14:20:06 2011 -0500
@@ -8,7 +8,7 @@
 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 getattr(sys, 'version_info', (0, 0, 0)) < (2, 4, 0, 'final'):
     raise SystemExit("Mercurial requires Python 2.4 or later.")
 
 if sys.version_info[0] >= 3:
@@ -236,7 +236,7 @@
         try:
             build_ext.build_extension(self, ext)
         except CCompilerError:
-            if not hasattr(ext, 'optional') or not ext.optional:
+            if getattr(ext, 'optional', False):
                 raise
             log.warn("Failed to build optional extension '%s' (skipping)",
                      ext.name)
--- a/contrib/win32/hgwebdir_wsgi.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/contrib/win32/hgwebdir_wsgi.py	Thu Jul 28 14:20:06 2011 -0500
@@ -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) is not None:
     import win32traceutil
 
 # To serve pages in local charset instead of UTF-8, remove the two lines below
--- a/doc/gendoc.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/doc/gendoc.py	Thu Jul 28 14:20:06 2011 -0500
@@ -9,6 +9,7 @@
 from mercurial.i18n import _
 from mercurial.help import helptable
 from mercurial import extensions
+from mercurial import util
 
 def get_desc(docstr):
     if not docstr:
@@ -95,7 +96,7 @@
             ui.write(".. _%s:\n" % name)
         ui.write("\n")
         section(ui, sec)
-        if hasattr(doc, '__call__'):
+        if util.safehasattr(doc, '__call__'):
             doc = doc()
         ui.write(doc)
         ui.write("\n")
--- a/hgext/convert/cvsps.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/hgext/convert/cvsps.py	Thu Jul 28 14:20:06 2011 -0500
@@ -11,6 +11,7 @@
 from mercurial import util
 from mercurial.i18n import _
 from mercurial import hook
+from mercurial import util
 
 class logentry(object):
     '''Class logentry has the following attributes:
@@ -513,8 +514,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 util.safehasattr(e, 'branchpoints') or
+                    not util.safehasattr (c, 'branchpoints') or
                     e.branchpoints == c.branchpoints) and
                   ((c.date[0] + c.date[1]) <=
                    (e.date[0] + e.date[1]) <=
--- a/hgext/convert/git.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/hgext/convert/git.py	Thu Jul 28 14:20:06 2011 -0500
@@ -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 util.safehasattr(os, 'unsetenv'):
         def gitopen(self, s, noerr=False):
             prevgitdir = os.environ.get('GIT_DIR')
             os.environ['GIT_DIR'] = self.path
--- a/hgext/convert/transport.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/hgext/convert/transport.py	Thu Jul 28 14:20:06 2011 -0500
@@ -54,7 +54,7 @@
                 if p:
                     providers.append(p)
     else:
-        if hasattr(svn.client, 'get_windows_simple_provider'):
+        if util.safehasattr(svn.client, 'get_windows_simple_provider'):
             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 util.safehasattr(svn.ra, 'reparent'):
             self.client = svn.client.create_context(self.pool)
             ab = _create_auth_baton(self.pool)
             if False:
--- a/hgext/inotify/__init__.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/hgext/inotify/__init__.py	Thu Jul 28 14:20:06 2011 -0500
@@ -11,6 +11,7 @@
 # todo: socket permissions
 
 from mercurial.i18n import _
+from mercurial import util
 import server
 from client import client, QueryFailed
 
@@ -31,7 +32,7 @@
         ui.write(('  %s/\n') % path)
 
 def reposetup(ui, repo):
-    if not hasattr(repo, 'dirstate'):
+    if not util.safehasattr(repo, 'dirstate'):
         return
 
     class inotifydirstate(repo.dirstate.__class__):
--- a/hgext/mq.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/hgext/mq.py	Thu Jul 28 14:20:06 2011 -0500
@@ -938,7 +938,7 @@
                         p.write("# User " + user + "\n")
                     if date:
                         p.write("# Date %s %s\n\n" % date)
-                if hasattr(msg, '__call__'):
+                if util.safehasattr(msg, '__call__'):
                     msg = msg()
                 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
                 n = repo.commit(commitmsg, user, date, match=match, force=True)
--- a/hgext/pager.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/hgext/pager.py	Thu Jul 28 14:20:06 2011 -0500
@@ -58,7 +58,7 @@
 from mercurial.i18n import _
 
 def _runpager(p):
-    if not hasattr(os, 'fork'):
+    if not util.safehasattr(os, 'fork'):
         sys.stdout = util.popen(p, 'wb')
         if util.isatty(sys.stderr):
             sys.stderr = sys.stdout
--- a/hgext/relink.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/hgext/relink.py	Thu Jul 28 14:20:06 2011 -0500
@@ -36,7 +36,8 @@
     command is running. (Both repositories will be locked against
     writes.)
     """
-    if not hasattr(util, 'samefile') or not hasattr(util, 'samedevice'):
+    if (not util.safehasattr(util, 'samefile') or
+        not util.safehasattr(util, 'samedevice')):
         raise util.Abort(_('hardlinks are not supported on this system'))
     src = hg.repository(ui, ui.expandpath(origin or 'default-relink',
                                           origin or 'default'))
--- a/mercurial/bookmarks.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/bookmarks.py	Thu Jul 28 14:20:06 2011 -0500
@@ -151,11 +151,10 @@
 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'):
-        return {}
+    marks = getattr(repo, '_bookmarks', {})
 
     d = {}
-    for k, v in repo._bookmarks.iteritems():
+    for k, v in marks.iteritems():
         d[k] = hex(v)
     return d
 
--- a/mercurial/byterange.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/byterange.py	Thu Jul 28 14:20:06 2011 -0500
@@ -103,9 +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):
-            return getattr(self.fo, name)
-        raise AttributeError(name)
+        return getattr(self.fo, name)
 
     def tell(self):
         """Return the position within the range.
@@ -170,10 +168,8 @@
         offset is relative to the current position (self.realpos).
         """
         assert offset >= 0
-        if not hasattr(self.fo, 'seek'):
-            self._poor_mans_seek(offset)
-        else:
-            self.fo.seek(self.realpos + offset)
+        seek = getattr(self.fo, 'seek', self._poor_mans_seek)
+        seek(self.realpos + offset)
         self.realpos += offset
 
     def _poor_mans_seek(self, offset):
--- a/mercurial/cmdutil.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/cmdutil.py	Thu Jul 28 14:20:06 2011 -0500
@@ -161,7 +161,7 @@
 
     if not pat or pat == '-':
         fp = writable and repo.ui.fout or repo.ui.fin
-        if hasattr(fp, 'fileno'):
+        if util.safehasattr(fp, 'fileno'):
             return os.fdopen(os.dup(fp.fileno()), mode)
         else:
             # if this fp can't be duped properly, return
@@ -177,9 +177,9 @@
                         return getattr(self.f, attr)
 
             return wrappedfileobj(fp)
-    if hasattr(pat, 'write') and writable:
+    if util.safehasattr(pat, 'write') and writable:
         return pat
-    if hasattr(pat, 'read') and 'r' in mode:
+    if util.safehasattr(pat, 'read') and 'r' in mode:
         return pat
     return open(makefilename(repo, pat, node, total, seqno, revwidth,
                               pathname),
@@ -520,7 +520,7 @@
                              revwidth=revwidth, mode='ab')
             if fp != template:
                 shouldclose = True
-        if fp != sys.stdout and hasattr(fp, 'name'):
+        if fp != sys.stdout and util.safehasattr(fp, 'name'):
             repo.ui.note("%s\n" % fp.name)
 
         fp.write("# HG changeset patch\n")
--- a/mercurial/commands.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/commands.py	Thu Jul 28 14:20:06 2011 -0500
@@ -1659,8 +1659,9 @@
 def debugignore(ui, repo, *values, **opts):
     """display the combined ignore pattern"""
     ignore = repo.dirstate._ignore
-    if hasattr(ignore, 'includepat'):
-        ui.write("%s\n" % ignore.includepat)
+    includepat = getattr(ignore, 'includepat', None)
+    if includepat is not None:
+        ui.write("%s\n" % includepat)
     else:
         raise util.Abort(_("no ignore patterns found"))
 
@@ -2647,7 +2648,7 @@
         doc = gettext(entry[0].__doc__)
         if not doc:
             doc = _("(no help text available)")
-        if hasattr(entry[0], 'definition'):  # aliased command
+        if util.safehasattr(entry[0], 'definition'):  # aliased command
             if entry[0].definition.startswith('!'):  # shell alias
                 doc = _('shell alias for::\n\n    %s') % entry[0].definition[1:]
             else:
@@ -2732,7 +2733,7 @@
         # description
         if not doc:
             doc = _("(no help text available)")
-        if hasattr(doc, '__call__'):
+        if util.safehasattr(doc, '__call__'):
             doc = doc()
 
         ui.write("%s\n\n" % header)
--- a/mercurial/demandimport.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/demandimport.py	Thu Jul 28 14:20:06 2011 -0500
@@ -27,6 +27,8 @@
 import __builtin__
 _origimport = __import__
 
+nothing = object()
+
 class _demandmod(object):
     """module demand-loader and proxy"""
     def __init__(self, name, globals, locals):
@@ -50,7 +52,7 @@
                 h, t = p, None
                 if '.' in p:
                     h, t = p.split('.', 1)
-                if not hasattr(mod, h):
+                if getattr(mod, h, nothing) is nothing:
                     setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
                 elif t:
                     subload(getattr(mod, h), t)
@@ -109,12 +111,12 @@
         mod = _origimport(name, globals, locals)
         # recurse down the module chain
         for comp in name.split('.')[1:]:
-            if not hasattr(mod, comp):
+            if getattr(mod, comp, nothing) is nothing:
                 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 getattr(mod, x, nothing) is nothing:
                 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
         return mod
 
@@ -137,6 +139,8 @@
     # raise ImportError if x not defined
     '__main__',
     '_ssl', # conditional imports in the stdlib, issue1964
+    'rfc822',
+    'mimetools',
     ]
 
 def enable():
@@ -146,4 +150,3 @@
 def disable():
     "disable global demand-loading of modules"
     __builtin__.__import__ = _origimport
-
--- a/mercurial/dispatch.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/dispatch.py	Thu Jul 28 14:20:06 2011 -0500
@@ -159,16 +159,16 @@
         elif m in "zlib".split():
             ui.warn(_("(is your Python install correct?)\n"))
     except IOError, inst:
-        if hasattr(inst, "code"):
+        if util.safehasattr(inst, "code"):
             ui.warn(_("abort: %s\n") % inst)
-        elif hasattr(inst, "reason"):
+        elif util.safehasattr(inst, "reason"):
             try: # usually it is in the form (errno, strerror)
                 reason = inst.reason.args[1]
             except (AttributeError, IndexError):
                  # 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 util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
             if ui.debugflag:
                 ui.warn(_("broken pipe\n"))
         elif getattr(inst, "strerror", None):
@@ -338,7 +338,7 @@
             ui.debug("alias '%s' shadows command '%s'\n" %
                      (self.name, self.cmdname))
 
-        if hasattr(self, 'shell'):
+        if util.safehasattr(self, 'shell'):
             return self.fn(ui, *args, **opts)
         else:
             try:
@@ -506,7 +506,7 @@
     cmd = aliases[0]
     fn = entry[0]
 
-    if cmd and hasattr(fn, 'shell'):
+    if cmd and util.safehasattr(fn, 'shell'):
         d = lambda: fn(ui, *args[1:])
         return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
 
--- a/mercurial/encoding.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/encoding.py	Thu Jul 28 14:20:06 2011 -0500
@@ -140,12 +140,12 @@
 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'):
+    eaw = getattr(unicodedata, 'east_asian_width', None)
+    if eaw is not 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 sum([eaw(c) in wide and 2 or 1 for c in d])
     return len(d)
 
 def lower(s):
--- a/mercurial/extensions.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/extensions.py	Thu Jul 28 14:20:06 2011 -0500
@@ -124,7 +124,7 @@
     where orig is the original (wrapped) function, and *args, **kwargs
     are the arguments passed to it.
     '''
-    assert hasattr(wrapper, '__call__')
+    assert util.safehasattr(wrapper, '__call__')
     aliases, entry = cmdutil.findcmd(command, table)
     for alias, e in table.iteritems():
         if e is entry:
@@ -177,12 +177,12 @@
     your end users, you should play nicely with others by using the
     subclass trick.
     '''
-    assert hasattr(wrapper, '__call__')
+    assert util.safehasattr(wrapper, '__call__')
     def wrap(*args, **kwargs):
         return wrapper(origfn, *args, **kwargs)
 
     origfn = getattr(container, funcname)
-    assert hasattr(origfn, '__call__')
+    assert util.safehasattr(origfn, '__call__')
     setattr(container, funcname, wrap)
     return origfn
 
--- a/mercurial/fancyopts.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/fancyopts.py	Thu Jul 28 14:20:06 2011 -0500
@@ -75,7 +75,7 @@
         # copy defaults to state
         if isinstance(default, list):
             state[name] = default[:]
-        elif hasattr(default, '__call__'):
+        elif getattr(default, '__call__', False):
             state[name] = None
         else:
             state[name] = default
--- a/mercurial/help.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/help.py	Thu Jul 28 14:20:06 2011 -0500
@@ -31,7 +31,7 @@
     """Return a delayed loader for help/topic.txt."""
 
     def loader():
-        if hasattr(sys, 'frozen'):
+        if util.mainfrozen():
             module = sys.executable
         else:
             module = __file__
--- a/mercurial/hg.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/hg.py	Thu Jul 28 14:20:06 2011 -0500
@@ -537,7 +537,7 @@
 
 def remoteui(src, opts):
     'build a remote ui from ui or repo and opts'
-    if hasattr(src, 'baseui'): # looks like a repository
+    if util.safehasattr(src, 'baseui'): # 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
--- a/mercurial/hgweb/hgweb_mod.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/hgweb/hgweb_mod.py	Thu Jul 28 14:20:06 2011 -0500
@@ -7,7 +7,7 @@
 # GNU General Public License version 2 or any later version.
 
 import os
-from mercurial import ui, hg, hook, error, encoding, templater
+from mercurial import ui, hg, hook, error, encoding, templater, util
 from common import get_stat, ErrorResponse, permhooks, caching
 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
 from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
@@ -147,7 +147,7 @@
                 cmd = cmd[style + 1:]
 
             # avoid accepting e.g. style parameter as command
-            if hasattr(webcommands, cmd):
+            if util.safehasattr(webcommands, cmd):
                 req.form['cmd'] = [cmd]
             else:
                 cmd = ''
--- a/mercurial/hgweb/request.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/hgweb/request.py	Thu Jul 28 14:20:06 2011 -0500
@@ -101,7 +101,7 @@
             self.headers = []
 
     def write(self, thing):
-        if hasattr(thing, "__iter__"):
+        if util.safehasattr(thing, "__iter__"):
             for part in thing:
                 self.write(part)
         else:
--- a/mercurial/hgweb/server.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/hgweb/server.py	Thu Jul 28 14:20:06 2011 -0500
@@ -248,7 +248,7 @@
     from threading import activeCount
     _mixin = SocketServer.ThreadingMixIn
 except ImportError:
-    if hasattr(os, "fork"):
+    if util.safehasattr(os, "fork"):
         _mixin = SocketServer.ForkingMixIn
     else:
         class _mixin(object):
--- a/mercurial/hgweb/webutil.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/hgweb/webutil.py	Thu Jul 28 14:20:06 2011 -0500
@@ -72,7 +72,7 @@
         d['date'] = s.date()
         d['description'] = s.description()
         d['branch'] = s.branch()
-        if hasattr(s, 'path'):
+        if util.safehasattr(s, 'path'):
             d['file'] = s.path()
         yield d
 
--- a/mercurial/hgweb/wsgicgi.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/hgweb/wsgicgi.py	Thu Jul 28 14:20:06 2011 -0500
@@ -78,5 +78,4 @@
         for chunk in content:
             write(chunk)
     finally:
-        if hasattr(content, 'close'):
-            content.close()
+        getattr(content, 'close', lambda : None)()
--- a/mercurial/hook.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/hook.py	Thu Jul 28 14:20:06 2011 -0500
@@ -21,14 +21,14 @@
 
     ui.note(_("calling hook %s: %s\n") % (hname, funcname))
     obj = funcname
-    if not hasattr(obj, '__call__'):
+    if not util.safehasattr(obj, '__call__'):
         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 util.mainfrozen():
             # 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 util.safehasattr(obj, '__call__'):
             raise util.Abort(_('%s hook is invalid '
                                '("%s" is not callable)') %
                              (hname, funcname))
@@ -99,7 +99,7 @@
 
     env = {}
     for k, v in args.iteritems():
-        if hasattr(v, '__call__'):
+        if util.safehasattr(v, '__call__'):
             v = v()
         if isinstance(v, dict):
             # make the dictionary element order stable across Python
@@ -145,7 +145,7 @@
         for hname, cmd in ui.configitems('hooks'):
             if hname.split('.')[0] != name or not cmd:
                 continue
-            if hasattr(cmd, '__call__'):
+            if util.safehasattr(cmd, '__call__'):
                 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
             elif cmd.startswith('python:'):
                 if cmd.count(':') >= 2:
--- a/mercurial/httprepo.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/httprepo.py	Thu Jul 28 14:20:06 2011 -0500
@@ -44,8 +44,7 @@
     def __del__(self):
         for h in self.urlopener.handlers:
             h.close()
-            if hasattr(h, "close_all"):
-                h.close_all()
+            getattr(h, "close_all", lambda : None)()
 
     def url(self):
         return self.path
--- a/mercurial/i18n.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/i18n.py	Thu Jul 28 14:20:06 2011 -0500
@@ -9,7 +9,7 @@
 import gettext, sys, os
 
 # modelled after templater.templatepath:
-if hasattr(sys, 'frozen'):
+if getattr(sys, 'frozen', None) is not None:
     module = sys.executable
 else:
     module = __file__
@@ -61,4 +61,3 @@
     _ = lambda message: message
 else:
     _ = gettext
-
--- a/mercurial/keepalive.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/keepalive.py	Thu Jul 28 14:20:06 2011 -0500
@@ -547,13 +547,14 @@
         print "send:", repr(str)
     try:
         blocksize = 8192
-        if hasattr(str,'read') :
+        read = getattr(str, 'read', None)
+        if read is not None:
             if self.debuglevel > 0:
                 print "sendIng a read()able"
-            data = str.read(blocksize)
+            data = read(blocksize)
             while data:
                 self.sock.sendall(data)
-                data = str.read(blocksize)
+                data = read(blocksize)
         else:
             self.sock.sendall(str)
     except socket.error, v:
--- a/mercurial/lsprof.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/lsprof.py	Thu Jul 28 14:20:06 2011 -0500
@@ -86,9 +86,7 @@
         for k, v in list(sys.modules.iteritems()):
             if v is None:
                 continue
-            if not hasattr(v, '__file__'):
-                continue
-            if not isinstance(v.__file__, str):
+            if not isinstance(getattr(v, '__file__', None), str):
                 continue
             if v.__file__.startswith(code.co_filename):
                 mname = _fn2mod[code.co_filename] = k
--- a/mercurial/mail.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/mail.py	Thu Jul 28 14:20:06 2011 -0500
@@ -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 util.safehasattr(socket, 'ssl'):
         raise util.Abort(_("can't use TLS: Python SSL support not installed"))
     if smtps:
         ui.note(_('(using smtps)\n'))
--- a/mercurial/patch.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/patch.py	Thu Jul 28 14:20:06 2011 -0500
@@ -126,7 +126,7 @@
 
     mimeheaders = ['content-type']
 
-    if not hasattr(stream, 'next'):
+    if not util.safehasattr(stream, 'next'):
         # http responses, for example, have readline but not next
         stream = fiter(stream)
 
--- a/mercurial/revlog.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/revlog.py	Thu Jul 28 14:20:06 2011 -0500
@@ -226,9 +226,10 @@
         self._nodepos = None
 
         v = REVLOG_DEFAULT_VERSION
-        if hasattr(opener, 'options'):
-            if 'revlogv1' in opener.options:
-                if 'generaldelta' in opener.options:
+        opts = getattr(opener, 'options', None)
+        if opts is not None:
+            if 'revlogv1' in opts:
+                if 'generaldelta' in opts:
                     v |= REVLOGGENERALDELTA
             else:
                 v = 0
--- a/mercurial/scmutil.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/scmutil.py	Thu Jul 28 14:20:06 2011 -0500
@@ -324,10 +324,10 @@
     def errhandler(err):
         if err.filename == path:
             raise err
-    if followsym and hasattr(os.path, 'samestat'):
+    samestat = getattr(os.path, 'samestat', None)
+    if followsym and samestat is not None:
         def adddir(dirlst, dirname):
             match = False
-            samestat = os.path.samestat
             dirstat = os.stat(dirname)
             for lstdirstat in dirlst:
                 if samestat(dirstat, lstdirstat):
--- a/mercurial/statichttprepo.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/statichttprepo.py	Thu Jul 28 14:20:06 2011 -0500
@@ -31,15 +31,11 @@
         try:
             f = self.opener.open(req)
             data = f.read()
-            if hasattr(f, 'getcode'):
-                # python 2.6+
-                code = f.getcode()
-            elif hasattr(f, 'code'):
-                # undocumented attribute, seems to be set in 2.4 and 2.5
-                code = f.code
-            else:
-                # Don't know how to check, hope for the best.
-                code = 206
+            # Python 2.6+ defines a getcode() function, and 2.4 and
+            # 2.5 appear to always have an undocumented code attribute
+            # set. If we can't read either of those, fall back to 206
+            # and hope for the best.
+            code = getattr(f, 'getcode', lambda : getattr(f, 'code', 206))()
         except urllib2.HTTPError, inst:
             num = inst.code == 404 and errno.ENOENT or None
             raise IOError(num, inst)
--- a/mercurial/subrepo.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/subrepo.py	Thu Jul 28 14:20:06 2011 -0500
@@ -181,22 +181,22 @@
 def reporelpath(repo):
     """return path to this (sub)repo as seen from outermost repo"""
     parent = repo
-    while hasattr(parent, '_subparent'):
+    while util.safehasattr(parent, '_subparent'):
         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 util.safehasattr(sub, '_relpath'):
         return sub._relpath
-    if not hasattr(sub, '_repo'):
+    if not util.safehasattr(sub, '_repo'):
         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 util.safehasattr(repo, '_subparent'):
         source = util.url(repo._subsource)
         if source.isabs():
             return str(source)
@@ -208,7 +208,7 @@
             parent.path = posixpath.normpath(parent.path)
             return str(parent)
     else: # recursion reached top repo
-        if hasattr(repo, '_subtoppath'):
+        if util.safehasattr(repo, '_subtoppath'):
             return repo._subtoppath
         if push and repo.ui.config('paths', 'default-push'):
             return repo.ui.config('paths', 'default-push')
--- a/mercurial/templatefilters.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/templatefilters.py	Thu Jul 28 14:20:06 2011 -0500
@@ -188,13 +188,13 @@
         return '"%s"' % jsonescape(u)
     elif isinstance(obj, unicode):
         return '"%s"' % jsonescape(obj)
-    elif hasattr(obj, 'keys'):
+    elif util.safehasattr(obj, 'keys'):
         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 util.safehasattr(obj, '__iter__'):
         out = []
         for i in obj:
             out.append(json(i))
@@ -279,7 +279,7 @@
     """:stringify: Any type. Turns the value into text by converting values into
     text and concatenating them.
     """
-    if hasattr(thing, '__iter__') and not isinstance(thing, str):
+    if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
         return "".join([stringify(t) for t in thing if t is not None])
     return str(thing)
 
--- a/mercurial/templater.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/templater.py	Thu Jul 28 14:20:06 2011 -0500
@@ -135,7 +135,7 @@
     v = mapping.get(key)
     if v is None:
         v = context._defaults.get(key, '')
-    if hasattr(v, '__call__'):
+    if util.safehasattr(v, '__call__'):
         return v(**mapping)
     return v
 
@@ -203,14 +203,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 util.safehasattr(thing, '__iter__'):
         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 util.safehasattr(i, '__iter__'):
                 if i is not None:
                     yield str(i)
             elif i is not None:
@@ -341,7 +341,7 @@
     normpaths = []
 
     # executable version (py2exe) doesn't support __file__
-    if hasattr(sys, 'frozen'):
+    if util.mainfrozen():
         module = sys.executable
     else:
         module = __file__
--- a/mercurial/url.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/url.py	Thu Jul 28 14:20:06 2011 -0500
@@ -129,7 +129,7 @@
             orgsend(self, data)
     return _sendfile
 
-has_https = hasattr(urllib2, 'HTTPSHandler')
+has_https = util.safehasattr(urllib2, 'HTTPSHandler')
 if has_https:
     try:
         _create_connection = socket.create_connection
@@ -186,8 +186,8 @@
 # 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:
-        tunnel_host = req._tunnel_host
+    tunnel_host = getattr(req, '_tunnel_host', None)
+    if tunnel_host:
         if tunnel_host[:7] not in ['http://', 'https:/']:
             tunnel_host = 'https://' + tunnel_host
         new_tunnel = True
--- a/mercurial/util.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/util.py	Thu Jul 28 14:20:06 2011 -0500
@@ -75,6 +75,10 @@
 def sha1(s):
     return _fastsha1(s)
 
+_notset = object()
+def safehasattr(thing, attr):
+    return getattr(thing, attr, _notset) is not _notset
+
 def _fastsha1(s):
     # This function will import sha1 from hashlib or sha (whichever is
     # available) and overwrite itself with it on the first call.
@@ -354,8 +358,8 @@
     The code supports py2exe (most common, Windows only) and tools/freeze
     (portable, not much used).
     """
-    return (hasattr(sys, "frozen") or # new py2exe
-            hasattr(sys, "importers") or # old py2exe
+    return (safehasattr(sys, "frozen") or # new py2exe
+            safehasattr(sys, "importers") or # old py2exe
             imp.is_frozen("__main__")) # tools/freeze
 
 def hgexecutable():
@@ -779,7 +783,7 @@
             self._fp.close()
 
     def __del__(self):
-        if hasattr(self, '_fp'): # constructor actually did something
+        if safehasattr(self, '_fp'): # constructor actually did something
             self.close()
 
 def makedirs(name, mode=None):
@@ -1250,8 +1254,9 @@
     def handler(signum, frame):
         terminated.add(os.wait())
     prevhandler = None
-    if hasattr(signal, 'SIGCHLD'):
-        prevhandler = signal.signal(signal.SIGCHLD, handler)
+    SIGCHLD = getattr(signal, 'SIGCHLD', None)
+    if SIGCHLD is not None:
+        prevhandler = signal.signal(SIGCHLD, handler)
     try:
         pid = spawndetached(args)
         while not condfn():
--- a/mercurial/windows.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/windows.py	Thu Jul 28 14:20:06 2011 -0500
@@ -99,8 +99,9 @@
 def setbinary(fd):
     # When run without console, pipes may expose invalid
     # fileno(), usually set to -1.
-    if hasattr(fd, 'fileno') and fd.fileno() >= 0:
-        msvcrt.setmode(fd.fileno(), os.O_BINARY)
+    fno = getattr(fd, 'fileno', None)
+    if fno is not None and fno() >= 0:
+        msvcrt.setmode(fno(), os.O_BINARY)
 
 def pconvert(path):
     return '/'.join(path.split(os.sep))
--- a/mercurial/wireproto.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/mercurial/wireproto.py	Thu Jul 28 14:20:06 2011 -0500
@@ -17,7 +17,7 @@
 class future(object):
     '''placeholder for a value to be set later'''
     def set(self, value):
-        if hasattr(self, 'value'):
+        if util.safehasattr(self, 'value'):
             raise error.RepoError("future is already set")
         self.value = value
 
@@ -58,8 +58,9 @@
         req, rsp = [], []
         for name, args, opts, resref in self.calls:
             mtd = getattr(self.remote, name)
-            if hasattr(mtd, 'batchable'):
-                batchable = getattr(mtd, 'batchable')(mtd.im_self, *args, **opts)
+            batchablefn = getattr(mtd, 'batchable', None)
+            if batchablefn is not None:
+                batchable = batchablefn(mtd.im_self, *args, **opts)
                 encargsorres, encresref = batchable.next()
                 if encresref:
                     req.append((name, encargsorres,))
--- a/setup.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/setup.py	Thu Jul 28 14:20:06 2011 -0500
@@ -5,7 +5,7 @@
 # 'python setup.py --help' for more options
 
 import sys, platform
-if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
+if getattr(sys, 'version_info', (0, 0, 0)) < (2, 4, 0, 'final'):
     raise SystemExit("Mercurial requires Python 2.4 or later.")
 
 if sys.version_info[0] >= 3:
--- a/tests/run-tests.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/tests/run-tests.py	Thu Jul 28 14:20:06 2011 -0500
@@ -340,10 +340,7 @@
     """Terminate subprocess (with fallback for Python versions < 2.6)"""
     vlog('# Terminating process %d' % proc.pid)
     try:
-        if hasattr(proc, 'terminate'):
-            proc.terminate()
-        else:
-            os.kill(proc.pid, signal.SIGTERM)
+        getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
     except OSError:
         pass
 
--- a/tests/sitecustomize.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/tests/sitecustomize.py	Thu Jul 28 14:20:06 2011 -0500
@@ -1,6 +1,5 @@
 try:
     import coverage
-    if hasattr(coverage, 'process_startup'):
-        coverage.process_startup()
+    getattr(coverage, 'process_startup', lambda: None)()
 except ImportError:
     pass
--- a/tests/test-symlink-os-yes-fs-no.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/tests/test-symlink-os-yes-fs-no.py	Thu Jul 28 14:20:06 2011 -0500
@@ -5,7 +5,7 @@
 BUNDLEPATH = os.path.join(TESTDIR, 'bundles', 'test-no-symlinks.hg')
 
 # only makes sense to test on os which supports symlinks
-if not hasattr(os, "symlink"):
+if not getattr(os, "symlink", False):
     sys.exit(80) # SKIPPED_STATUS defined in run-tests.py
 
 # clone with symlink support
--- a/tests/test-walkrepo.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/tests/test-walkrepo.py	Thu Jul 28 14:20:06 2011 -0500
@@ -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', False) and getattr(os.path, 'samestat', False)
 
 hg.repository(u, 'top1', create=1)
 mkdir('subdir')
--- a/tests/tinyproxy.py	Tue Jul 26 21:30:12 2011 +0200
+++ b/tests/tinyproxy.py	Thu Jul 28 14:20:06 2011 -0500
@@ -23,7 +23,8 @@
 
     def handle(self):
         (ip, port) =  self.client_address
-        if hasattr(self, 'allowed_clients') and ip not in self.allowed_clients:
+        allowed = getattr(self, 'allowed_clients', None)
+        if allowed is not None and ip not in allowed:
             self.raw_requestline = self.rfile.readline()
             if self.parse_request():
                 self.send_error(403)