Patchwork [7,of,8,v2] python3: handle range/xrange divergence

login
register
mail settings
Submitter timeless@mozdev.org
Date March 30, 2016, 4:33 a.m.
Message ID <7049e93dc5766ba35e91.1459312402@waste.org>
Download mbox | patch
Permalink /patch/14168/
State Accepted
Headers show

Comments

timeless@mozdev.org - March 30, 2016, 4:33 a.m.
# HG changeset patch
# User timeless <timeless@mozdev.org>
# Date 1459272984 0
#      Tue Mar 29 17:36:24 2016 +0000
# Node ID 7049e93dc5766ba35e91fd15755f4c5a899bfcc0
# Parent  d90f8d4c6cbaa4046a2b3a242010defefd24dc25
python3: handle range/xrange divergence
Matt Mackall - March 31, 2016, 11:36 p.m.
On Tue, 2016-03-29 at 23:33 -0500, timeless wrote:
> # HG changeset patch
> # User timeless <timeless@mozdev.org>
> # Date 1459272984 0
> #      Tue Mar 29 17:36:24 2016 +0000
> # Node ID 7049e93dc5766ba35e91fd15755f4c5a899bfcc0
> # Parent  d90f8d4c6cbaa4046a2b3a242010defefd24dc25
> python3: handle range/xrange divergence

Sorry, but this is too much. I'm going to drop these.

The absolute_import stuff we've been doing is ugly, but tolerable. But having a
growing stanza of Py3 compatibility hacks at the top of every file is not. For
something as pervasive as xrange (or byte strings!), it's simply not an
acceptable level of impact. Py3 support is like an unemployed cousin we're
letting crash on the couch: we're already annoyed that it's here, so it should
try not to stack up dirty dishes everywhere.

Alternate solutions:

- cStringIO -> util.stringio
- Queue -> util.queue
- urllib.foo -> url.foo
- __builtins__.setdefault("xrange", range)

Also, the check-py3-compact test is nice, but should not be seen as a substitute
for check-code rules, because the latter provide valuable replacement advice. So
these sorts of things will also need check-code rules.

-- 
Mathematics is the supreme nostalgia of our time.
Gregory Szorc - April 3, 2016, 6:33 p.m.
On Thu, Mar 31, 2016 at 4:36 PM, Matt Mackall <mpm@selenic.com> wrote:

> On Tue, 2016-03-29 at 23:33 -0500, timeless wrote:
> > # HG changeset patch
> > # User timeless <timeless@mozdev.org>
> > # Date 1459272984 0
> > #      Tue Mar 29 17:36:24 2016 +0000
> > # Node ID 7049e93dc5766ba35e91fd15755f4c5a899bfcc0
> > # Parent  d90f8d4c6cbaa4046a2b3a242010defefd24dc25
> > python3: handle range/xrange divergence
>
> Sorry, but this is too much. I'm going to drop these.
>
> The absolute_import stuff we've been doing is ugly, but tolerable. But
> having a
> growing stanza of Py3 compatibility hacks at the top of every file is not.
> For
> something as pervasive as xrange (or byte strings!), it's simply not an
> acceptable level of impact. Py3 support is like an unemployed cousin we're
> letting crash on the couch: we're already annoyed that it's here, so it
> should
> try not to stack up dirty dishes everywhere.
>
> Alternate solutions:
>
> - cStringIO -> util.stringio
> - Queue -> util.queue
> - urllib.foo -> url.foo
>

I agree that Python 2/3 shims should live in util or a similar module (like
py3kcompat.py, which I just noticed appears orphaned and unused since we
removed 2to3 support, which is where it was getting injected).


> - __builtins__.setdefault("xrange", range)
>
>
I'm not a fan of mucking about with stdlib modules.


> Also, the check-py3-compact test is nice, but should not be seen as a
> substitute
> for check-code rules, because the latter provide valuable replacement
> advice. So
> these sorts of things will also need check-code rules.


As the person who wrote check-py3-compat, I completely agree. I originally
made it its own test because it was a niche thing and I wanted people
working on Python 3 to have a single place to go to view and manage porting
progress.

These recent patches from timeless represent a shift in Python 3 porting.
Before, we were making changes to things like absolute_import and
print_function that had their own advantages independent of Python 3
(although the main reason for doing them was Python 3). (absolute_import
gave us an excuse to establish a sane module import convention and print()
is more powerful than the print statement.) Now, we're making changes that
serve no purpose other than Python 3 support. These changes are much more
invasive. They also require developers to have some working knowledge of
differences between Python 2 and 3. I think this shift is the appropriate
time to start capturing things like range/xrange and items/iteritems in
check-code rules.
Matt Mackall - April 4, 2016, 5:02 p.m.
On Sun, 2016-04-03 at 11:33 -0700, Gregory Szorc wrote:

> > - __builtins__.setdefault("xrange", range)
> > 
> > 
> I'm not a fan of mucking about with stdlib modules.

Neither am I, but it seems better than all of the following:

- switching all xranges to ranges
- putting a definition of xrange at the top of every module
- util.xrange

Patch

diff --git a/contrib/check-code.py b/contrib/check-code.py
--- a/contrib/check-code.py
+++ b/contrib/check-code.py
@@ -31,6 +31,11 @@ 
 except ImportError:
     re2 = None
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def compilere(pat, multiline=False):
     if multiline:
         pat = '(?m)' + pat
diff --git a/contrib/perf.py b/contrib/perf.py
--- a/contrib/perf.py
+++ b/contrib/perf.py
@@ -28,6 +28,11 @@ 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def getlen(ui):
     if ui.configbool("perf", "stub"):
         return lambda x: 1
diff --git a/contrib/synthrepo.py b/contrib/synthrepo.py
--- a/contrib/synthrepo.py
+++ b/contrib/synthrepo.py
@@ -72,6 +72,11 @@ 
 
 newfile = set(('new fi', 'rename', 'copy f', 'copy t'))
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def zerodict():
     return collections.defaultdict(lambda: 0)
 
diff --git a/doc/check-seclevel.py b/doc/check-seclevel.py
--- a/doc/check-seclevel.py
+++ b/doc/check-seclevel.py
@@ -15,6 +15,11 @@ 
 from mercurial import minirst
 from mercurial import ui as uimod
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 level2mark = ['"', '=', '-', '.', '#']
 reservedmarks = ['"']
 
diff --git a/doc/hgmanpage.py b/doc/hgmanpage.py
--- a/doc/hgmanpage.py
+++ b/doc/hgmanpage.py
@@ -53,6 +53,11 @@ 
     from docutils.utils import roman
 import inspect
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 FIELD_LIST_INDENT = 7
 DEFINITION_LIST_INDENT = 7
 OPTION_LIST_INDENT = 7
diff --git a/hgext/acl.py b/hgext/acl.py
--- a/hgext/acl.py
+++ b/hgext/acl.py
@@ -215,6 +215,11 @@ 
 # leave the attribute unspecified.
 testedwith = 'internal'
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _getusers(ui, group):
 
     # First, try to use group definition from section [acl.groups]
diff --git a/hgext/blackbox.py b/hgext/blackbox.py
--- a/hgext/blackbox.py
+++ b/hgext/blackbox.py
@@ -60,6 +60,11 @@ 
 
 filehandles = {}
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _openlog(vfs):
     path = vfs.join('blackbox.log')
     if path in filehandles:
diff --git a/hgext/censor.py b/hgext/censor.py
--- a/hgext/censor.py
+++ b/hgext/censor.py
@@ -48,6 +48,11 @@ 
 # leave the attribute unspecified.
 testedwith = 'internal'
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 @command('censor',
     [('r', 'rev', '', _('censor file from specified revision'), _('REV')),
      ('t', 'tombstone', '', _('replacement tombstone data'), _('TEXT'))],
diff --git a/hgext/convert/cvsps.py b/hgext/convert/cvsps.py
--- a/hgext/convert/cvsps.py
+++ b/hgext/convert/cvsps.py
@@ -16,6 +16,11 @@ 
 )
 from mercurial.i18n import _
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class logentry(object):
     '''Class logentry has the following attributes:
         .author    - author name as CVS knows it
diff --git a/hgext/eol.py b/hgext/eol.py
--- a/hgext/eol.py
+++ b/hgext/eol.py
@@ -108,6 +108,10 @@ 
 # stray CR is an error.
 eolre = re.compile('\r*\n')
 
+try:
+    xrange
+except NameError:
+    xrange = range
 
 def inconsistenteol(data):
     return '\r\n' in data and singlelf.search(data)
diff --git a/hgext/hgcia.py b/hgext/hgcia.py
--- a/hgext/hgcia.py
+++ b/hgext/hgcia.py
@@ -63,6 +63,10 @@ 
 HGCIA_VERSION = '0.1'
 HGCIA_URL = 'http://hg.kublai.com/mercurial/hgcia'
 
+try:
+    xrange
+except NameError:
+    xrange = range
 
 class ciamsg(object):
     """ A CIA message """
diff --git a/hgext/hgk.py b/hgext/hgk.py
--- a/hgext/hgk.py
+++ b/hgext/hgk.py
@@ -47,6 +47,11 @@ 
 # leave the attribute unspecified.
 testedwith = 'internal'
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 @command('debug-diff-tree',
     [('p', 'patch', None, _('generate patch')),
     ('r', 'recursive', None, _('recursive')),
diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -196,6 +196,11 @@ 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class _constraints(object):
     # aborts if there are multiple rules for one node
     noduplicates = 'noduplicates'
diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -85,6 +85,11 @@ 
 # leave the attribute unspecified.
 testedwith = 'internal'
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 # force load strip extension formerly included in mq and import some utility
 try:
     stripext = extensions.find('strip')
diff --git a/hgext/notify.py b/hgext/notify.py
--- a/hgext/notify.py
+++ b/hgext/notify.py
@@ -178,6 +178,11 @@ 
     'changegroup': multiple_template,
 }
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class notifier(object):
     '''email notification class.'''
 
diff --git a/hgext/shelve.py b/hgext/shelve.py
--- a/hgext/shelve.py
+++ b/hgext/shelve.py
@@ -61,6 +61,11 @@ 
 
 backupdir = 'shelve-backup'
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class shelvedfile(object):
     """Helper for the file storing a single shelve
 
diff --git a/hgext/win32text.py b/hgext/win32text.py
--- a/hgext/win32text.py
+++ b/hgext/win32text.py
@@ -58,6 +58,11 @@ 
 newlinestr = {'\r\n': 'CRLF', '\r': 'CR'}
 filterstr = {'\r\n': 'clever', '\r': 'mac'}
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def checknewline(s, newline, ui=None, repo=None, filename=None):
     # warn if already has 'newline' in repository.
     # it might cause unexpected eol conversion.
diff --git a/mercurial/ancestor.py b/mercurial/ancestor.py
--- a/mercurial/ancestor.py
+++ b/mercurial/ancestor.py
@@ -12,6 +12,11 @@ 
 
 from .node import nullrev
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def commonancestorsheads(pfunc, *nodes):
     """Returns a set with the heads of all common ancestors of all nodes,
     heads(::nodes[0] and ::nodes[1] and ...) .
diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -34,6 +34,11 @@ 
 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
 _CHANGEGROUPV3_DELTA_HEADER = ">20s20s20s20s20sH"
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def readexactly(stream, n):
     '''read n bytes from stream.read and abort if less was available'''
     s = stream.read(n)
diff --git a/mercurial/changelog.py b/mercurial/changelog.py
--- a/mercurial/changelog.py
+++ b/mercurial/changelog.py
@@ -25,6 +25,11 @@ 
 
 _defaultextra = {'branch': 'default'}
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _string_escape(text):
     """
     >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -51,6 +51,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def ishunk(x):
     hunkclasses = (crecordmod.uihunk, patch.recordhunk)
     return isinstance(x, hunkclasses)
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -217,6 +217,11 @@ 
     ('', 'dir', False, _('open directory manifest')),
 ]
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 # Commands start here, listed alphabetically
 
 @command('^add',
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -46,6 +46,11 @@ 
 
 nonascii = re.compile(r'[^\x21-\x7f]').search
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class basectx(object):
     """A basectx object represents the common logic for its children:
     changectx: read-only context that is already present in the repo,
diff --git a/mercurial/dagparser.py b/mercurial/dagparser.py
--- a/mercurial/dagparser.py
+++ b/mercurial/dagparser.py
@@ -13,6 +13,11 @@ 
 from .i18n import _
 from . import error
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def parsedag(desc):
     '''parses a DAG from a concise textual description; generates events
 
diff --git a/mercurial/encoding.py b/mercurial/encoding.py
--- a/mercurial/encoding.py
+++ b/mercurial/encoding.py
@@ -19,6 +19,7 @@ 
 
 if sys.version_info[0] >= 3:
     unichr = chr
+    xrange = range
 
 # These unicode characters are ignored by HFS+ (Apple Technote 1150,
 # "Unicode Subtleties"), so we need to ignore them in some places for
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -46,6 +46,11 @@ 
 __all__ = []
 commands = {}
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class webcommand(object):
     """Decorator used to register a web command handler.
 
diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py
--- a/mercurial/hgweb/webutil.py
+++ b/mercurial/hgweb/webutil.py
@@ -33,6 +33,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def up(p):
     if p[0] != "/":
         p = "/" + p
diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -41,6 +41,11 @@ 
     wireproto,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def zgenerator(f):
     zd = zlib.decompressobj()
     try:
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -67,6 +67,11 @@ 
 propertycache = util.propertycache
 filecache = scmutil.filecache
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class repofilecache(filecache):
     """All filecache usage on repo are done for logic that should be unfiltered
     """
diff --git a/mercurial/mdiff.py b/mercurial/mdiff.py
--- a/mercurial/mdiff.py
+++ b/mercurial/mdiff.py
@@ -20,6 +20,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def splitnewlines(text):
     '''like str.splitlines, but only split on newlines.'''
     lines = [l + '\n' for l in text.split('\n')]
diff --git a/mercurial/minirst.py b/mercurial/minirst.py
--- a/mercurial/minirst.py
+++ b/mercurial/minirst.py
@@ -29,6 +29,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def section(s):
     return "%s\n%s\n\n" % (s, "\"" * encoding.colwidth(s))
 
diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py
--- a/mercurial/obsolete.py
+++ b/mercurial/obsolete.py
@@ -155,6 +155,11 @@ 
 _fm0fsize = _calcsize(_fm0fixed)
 _fm0fnodesize = _calcsize(_fm0node)
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _fm0readmarkers(data, off):
     # Loop on markers
     l = len(data)
diff --git a/mercurial/patch.py b/mercurial/patch.py
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -45,6 +45,11 @@ 
 gitre = re.compile('diff --git a/(.*) b/(.*)')
 tabsplitter = re.compile(r'(\t+|[^\t]+)')
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class PatchError(Exception):
     pass
 
diff --git a/mercurial/phases.py b/mercurial/phases.py
--- a/mercurial/phases.py
+++ b/mercurial/phases.py
@@ -121,6 +121,11 @@ 
 trackedphases = allphases[1:]
 phasenames = ['public', 'draft', 'secret']
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _readroots(repo, phasedefaults=None):
     """Read phase roots from disk
 
diff --git a/mercurial/pure/diffhelpers.py b/mercurial/pure/diffhelpers.py
--- a/mercurial/pure/diffhelpers.py
+++ b/mercurial/pure/diffhelpers.py
@@ -7,6 +7,11 @@ 
 
 from __future__ import absolute_import
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def addlines(fp, hunk, lena, lenb, a, b):
     while True:
         todoa = lena - len(a)
diff --git a/mercurial/pure/osutil.py b/mercurial/pure/osutil.py
--- a/mercurial/pure/osutil.py
+++ b/mercurial/pure/osutil.py
@@ -14,6 +14,11 @@ 
 import stat as statmod
 import sys
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _mode_to_kind(mode):
     if statmod.S_ISREG(mode):
         return statmod.S_IFREG
diff --git a/mercurial/pvec.py b/mercurial/pvec.py
--- a/mercurial/pvec.py
+++ b/mercurial/pvec.py
@@ -64,6 +64,11 @@ 
 _vecbits = _vecbytes * 8
 _radius = (_vecbits - 30) / 2 # high probability vectors are related
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _bin(bs):
     '''convert a bytestring to a long'''
     v = 0
diff --git a/mercurial/repair.py b/mercurial/repair.py
--- a/mercurial/repair.py
+++ b/mercurial/repair.py
@@ -20,6 +20,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _bundle(repo, bases, heads, node, suffix, compress=True):
     """create a bundle with the specified revisions as a backup"""
     cgversion = changegroup.safeversion(repo)
diff --git a/mercurial/repoview.py b/mercurial/repoview.py
--- a/mercurial/repoview.py
+++ b/mercurial/repoview.py
@@ -21,6 +21,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def hideablerevs(repo):
     """Revisions candidates to be hidden
 
diff --git a/mercurial/revlog.py b/mercurial/revlog.py
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -65,6 +65,11 @@ 
 LookupError = error.LookupError
 CensoredNodeError = error.CensoredNodeError
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def getoffset(q):
     return int(q >> 16)
 
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -27,6 +27,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def _revancestors(repo, revs, followfirst):
     """Like revlog.ancestors(), but supports followfirst."""
     if followfirst:
diff --git a/mercurial/simplemerge.py b/mercurial/simplemerge.py
--- a/mercurial/simplemerge.py
+++ b/mercurial/simplemerge.py
@@ -29,6 +29,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class CantReprocessAndShowBase(Exception):
     pass
 
diff --git a/mercurial/sshserver.py b/mercurial/sshserver.py
--- a/mercurial/sshserver.py
+++ b/mercurial/sshserver.py
@@ -18,6 +18,11 @@ 
     wireproto,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class sshserver(wireproto.abstractserverproto):
     def __init__(self, ui, repo):
         self.ui = ui
diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
--- a/mercurial/sslutil.py
+++ b/mercurial/sslutil.py
@@ -37,6 +37,11 @@ 
     OP_NO_SSLv3 = 0x2000000
 
 try:
+    xrange
+except NameError:
+    xrange = range
+
+try:
     # ssl.SSLContext was added in 2.7.9 and presence indicates modern
     # SSL/TLS features are available.
     SSLContext = ssl.SSLContext
diff --git a/mercurial/store.py b/mercurial/store.py
--- a/mercurial/store.py
+++ b/mercurial/store.py
@@ -21,6 +21,11 @@ 
 
 _sha = util.sha1
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 # This avoids a collision between a file named foo and a dir named
 # foo.i or foo.d
 def _encodedir(path):
diff --git a/mercurial/streamclone.py b/mercurial/streamclone.py
--- a/mercurial/streamclone.py
+++ b/mercurial/streamclone.py
@@ -18,6 +18,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def canperformstreamclone(pullop, bailifbundle2supported=False):
     """Whether it is possible to perform a streaming clone as part of pull.
 
diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py
--- a/mercurial/templatefilters.py
+++ b/mercurial/templatefilters.py
@@ -27,6 +27,11 @@ 
     util,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def addbreaks(text):
     """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
     every line except the last.
diff --git a/mercurial/treediscovery.py b/mercurial/treediscovery.py
--- a/mercurial/treediscovery.py
+++ b/mercurial/treediscovery.py
@@ -18,6 +18,11 @@ 
     error,
 )
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def findcommonincoming(repo, remote, heads=None, force=False):
     """Return a tuple (common, fetch, heads) used to identify the common
     subset of nodes between repo and remote.
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -125,6 +125,11 @@ 
 # libraries, and sure enough Mercurial is not a library.)
 os.stat_float_times(False)
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 def safehasattr(thing, attr):
     return getattr(thing, attr, _notset) is not _notset
 
diff --git a/mercurial/win32.py b/mercurial/win32.py
--- a/mercurial/win32.py
+++ b/mercurial/win32.py
@@ -45,6 +45,11 @@ 
     _WPARAM = ctypes.c_ulonglong
     _LPARAM = ctypes.c_longlong
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class _FILETIME(ctypes.Structure):
     _fields_ = [('dwLowDateTime', _DWORD),
                 ('dwHighDateTime', _DWORD)]
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -43,6 +43,11 @@ 
     'incompatible Mercurial client; bundle2 required\n'
     '(see https://www.mercurial-scm.org/wiki/IncompatibleClient)\n')
 
+try:
+    xrange
+except NameError:
+    xrange = range
+
 class abstractserverproto(object):
     """abstract class that summarizes the protocol API
 
diff --git a/tests/test-check-py3-compat.t b/tests/test-check-py3-compat.t
--- a/tests/test-check-py3-compat.t
+++ b/tests/test-check-py3-compat.t
@@ -124,13 +124,13 @@ 
   contrib/import-checker.py: invalid syntax: Missing parentheses in call to 'print' (<unknown>, line *) (glob)
   doc/hgmanpage.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
   hgext/automv.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob)
-  hgext/blackbox.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/blackbox.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/bugzilla.py: error importing module: <ImportError> No module named 'urlparse' (line *) (glob)
-  hgext/censor.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/censor.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/chgserver.py: error importing module: <ImportError> No module named 'SocketServer' (line *) (glob)
-  hgext/children.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/churn.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/clonebundles.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/children.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/churn.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/clonebundles.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/color.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
   hgext/convert/bzr.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
   hgext/convert/common.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob)
@@ -141,69 +141,69 @@ 
   hgext/convert/filemap.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
   hgext/convert/git.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
   hgext/convert/gnuarch.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
-  hgext/convert/hg.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/convert/hg.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/convert/monotone.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
   hgext/convert/p*.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob)
   hgext/convert/subversion.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob)
   hgext/convert/transport.py: error importing module: <ImportError> No module named 'svn.client' (line *) (glob)
-  hgext/eol.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/extdiff.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/eol.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/extdiff.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/factotum.py: error importing: <ImportError> No module named 'httplib' (error at url.py:*) (glob)
   hgext/fetch.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob)
   hgext/fsmonitor/watchmanclient.py: error importing module: <SystemError> Parent module 'hgext.fsmonitor' not loaded, cannot perform relative import (line *) (glob)
   hgext/gpg.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob)
-  hgext/graphlog.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/hgcia.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/hgk.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/graphlog.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/hgcia.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/hgk.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/highlight/highlight.py: error importing module: <ImportError> No module named 'pygments' (line *) (glob)
   hgext/histedit.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
   hgext/keyword.py: error importing: <ImportError> No module named 'BaseHTTPServer' (error at common.py:*) (glob)
   hgext/largefiles/basestore.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
   hgext/largefiles/lfcommands.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
-  hgext/largefiles/lfutil.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/largefiles/lfutil.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/largefiles/localstore.py: error importing module: <ImportError> No module named 'lfutil' (line *) (glob)
   hgext/largefiles/overrides.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
   hgext/largefiles/proto.py: error importing: <ImportError> No module named 'httplib' (error at httppeer.py:*) (glob)
   hgext/largefiles/remotestore.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob)
-  hgext/largefiles/reposetup.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/largefiles/reposetup.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/largefiles/uisetup.py: error importing module: <SyntaxError> invalid syntax (archival.py, line *) (line *) (glob)
   hgext/largefiles/wirestore.py: error importing module: <ImportError> No module named 'lfutil' (line *) (glob)
   hgext/mq.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob)
-  hgext/notify.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/pager.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/patchbomb.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/purge.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/notify.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/pager.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/patchbomb.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/purge.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/rebase.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
-  hgext/record.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/relink.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/schemes.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  hgext/share.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/record.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/relink.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/schemes.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  hgext/share.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/shelve.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
-  hgext/strip.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  hgext/strip.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   hgext/transplant.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
   mercurial/archival.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
-  mercurial/branchmap.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/branchmap.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/bundle*.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
   mercurial/bundlerepo.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
-  mercurial/changegroup.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/changelog.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/cmdutil.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/changegroup.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/changelog.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/cmdutil.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/commands.py: invalid syntax: invalid syntax (<unknown>, line *) (glob)
   mercurial/commandserver.py: error importing module: <ImportError> No module named 'SocketServer' (line *) (glob)
-  mercurial/context.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/copies.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/crecord.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/dirstate.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/discovery.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/dispatch.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/context.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/copies.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/crecord.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/dirstate.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/discovery.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/dispatch.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/exchange.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
-  mercurial/extensions.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/filelog.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/filemerge.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/fileset.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/extensions.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/filelog.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/filemerge.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/fileset.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/formatter.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob)
-  mercurial/graphmod.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/help.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/graphmod.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/help.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/hg.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob)
   mercurial/hgweb/common.py: error importing module: <ImportError> No module named 'BaseHTTPServer' (line *) (glob)
   mercurial/hgweb/hgweb_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
@@ -214,38 +214,37 @@ 
   mercurial/hgweb/webcommands.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
   mercurial/hgweb/webutil.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
   mercurial/hgweb/wsgicgi.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob)
-  mercurial/hook.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/hook.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/httpclient/_readers.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
   mercurial/httpconnection.py: error importing: <ImportError> No module named 'httplib' (error at __init__.py:*) (glob)
   mercurial/httppeer.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
   mercurial/keepalive.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
-  mercurial/localrepo.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/localrepo.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/mail.py: error importing module: <AttributeError> module 'email' has no attribute 'Header' (line *) (glob)
-  mercurial/manifest.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/merge.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/namespaces.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/patch.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/manifest.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/merge.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/namespaces.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/patch.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/pure/parsers.py: error importing module: <ImportError> No module named 'mercurial.pure.node' (line *) (glob)
-  mercurial/pvec.py: error importing module: <NameError> name 'xrange' is not defined (line *) (glob)
   mercurial/repair.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)
-  mercurial/revlog.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/revset.py: error importing module: <NameError> name 'xrange' is not defined (line *) (glob)
-  mercurial/scmutil.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/revlog.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/revset.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/scmutil.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/scmwindows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob)
-  mercurial/simplemerge.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/simplemerge.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/sshpeer.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob)
-  mercurial/sshserver.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/statichttprepo.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/store.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/streamclone.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/subrepo.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/templatefilters.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/templatekw.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
-  mercurial/templater.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/sshserver.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/statichttprepo.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/store.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/streamclone.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/subrepo.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/templatefilters.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/templatekw.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
+  mercurial/templater.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/ui.py: error importing: <ImportError> No module named 'cPickle' (error at formatter.py:*) (glob)
-  mercurial/unionrepo.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/unionrepo.py: error importing: <AttributeError> 'function' object has no attribute 'func_code' (error at util.py:*) (glob)
   mercurial/url.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob)
-  mercurial/verify.py: error importing: <NameError> name 'xrange' is not defined (error at revset.py:*) (glob)
+  mercurial/verify.py: error importing module: <AttributeError> 'function' object has no attribute 'func_code' (line *) (glob)
   mercurial/win*.py: error importing module: <ImportError> No module named 'msvcrt' (line *) (glob)
   mercurial/windows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob)
   mercurial/wireproto.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob)