[PATCH 4 of 6] contrib: remove references to 2to3

Gregory Szorc gregory.szorc at gmail.com
Sun Feb 28 00:35:55 EST 2016


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1456636516 28800
#      Sat Feb 27 21:15:16 2016 -0800
# Node ID 53617b5f2e136f4960228aa559c698db58591a0e
# Parent  5b26746f741f3818a9f7632b3130f618478e845a
contrib: remove references to 2to3

The custom porting fixers are removed. A comment related to 2to3
has been removed from the import checker.

After this patch, no references to 2to3 remain.

diff --git a/contrib/hgfixes/__init__.py b/contrib/hgfixes/__init__.py
deleted file mode 100644
diff --git a/contrib/hgfixes/fix_bytes.py b/contrib/hgfixes/fix_bytes.py
deleted file mode 100644
--- a/contrib/hgfixes/fix_bytes.py
+++ /dev/null
@@ -1,98 +0,0 @@
-"""Fixer that changes plain strings to bytes strings."""
-
-import re
-
-from lib2to3 import fixer_base
-from lib2to3.pgen2 import token
-from lib2to3.fixer_util import Name
-from lib2to3.pygram import python_symbols as syms
-
-_re = re.compile(r'[rR]?[\'\"]')
-
-# XXX: Implementing a blacklist in 2to3 turned out to be more troublesome than
-# blacklisting some modules inside the fixers. So, this is what I came with.
-
-blacklist = ('mercurial/demandimport.py',
-             'mercurial/py3kcompat.py', # valid python 3 already
-             'mercurial/i18n.py',
-            )
-
-def isdocstring(node):
-    def isclassorfunction(ancestor):
-        symbols = (syms.funcdef, syms.classdef)
-        # if the current node is a child of a function definition, a class
-        # definition or a file, then it is a docstring
-        if ancestor.type == syms.simple_stmt:
-            try:
-                while True:
-                    if ancestor.type in symbols:
-                        return True
-                    ancestor = ancestor.parent
-            except AttributeError:
-                return False
-        return False
-
-    def ismodule(ancestor):
-        # Our child is a docstring if we are a simple statement, and our
-        # ancestor is file_input. In other words, our child is a lone string in
-        # the source file.
-        try:
-            if (ancestor.type == syms.simple_stmt and
-                ancestor.parent.type == syms.file_input):
-                    return True
-        except AttributeError:
-            return False
-
-    def isdocassignment(ancestor):
-        # Assigning to __doc__, definitely a string
-        try:
-            while True:
-                if (ancestor.type == syms.expr_stmt and
-                    Name('__doc__') in ancestor.children):
-                        return True
-                ancestor = ancestor.parent
-        except AttributeError:
-            return False
-
-    if ismodule(node.parent) or \
-       isdocassignment(node.parent) or \
-       isclassorfunction(node.parent):
-        return True
-    return False
-
-def shouldtransform(node):
-    specialnames = ['__main__']
-
-    if node.value in specialnames:
-        return False
-
-    ggparent = node.parent.parent.parent
-    sggparent = str(ggparent)
-
-    if 'getattr' in sggparent or \
-       'hasattr' in sggparent or \
-       'setattr' in sggparent or \
-       'encode' in sggparent or \
-       'decode' in sggparent:
-        return False
-
-    return True
-
-class FixBytes(fixer_base.BaseFix):
-
-    PATTERN = 'STRING'
-
-    def transform(self, node, results):
-        # The filename may be prefixed with a build directory.
-        if self.filename.endswith(blacklist):
-            return
-        if node.type == token.STRING:
-            if _re.match(node.value):
-                if isdocstring(node):
-                    return
-                if not shouldtransform(node):
-                    return
-                new = node.clone()
-                new.value = 'b' + new.value
-                return new
-
diff --git a/contrib/hgfixes/fix_bytesmod.py b/contrib/hgfixes/fix_bytesmod.py
deleted file mode 100644
--- a/contrib/hgfixes/fix_bytesmod.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""Fixer that changes bytes % whatever to a function that actually formats
-it."""
-
-from lib2to3 import fixer_base
-from lib2to3.fixer_util import is_tuple, Call, Comma, Name, touch_import
-
-# XXX: Implementing a blacklist in 2to3 turned out to be more troublesome than
-# blacklisting some modules inside the fixers. So, this is what I came with.
-
-blacklist = ['mercurial/demandimport.py',
-             'mercurial/py3kcompat.py',
-             'mercurial/i18n.py',
-            ]
-
-def isnumberremainder(formatstr, data):
-    try:
-        if data.value.isdigit():
-            return True
-    except AttributeError:
-        return False
-
-class FixBytesmod(fixer_base.BaseFix):
-    # XXX: There's one case (I suppose) I can't handle: when a remainder
-    # operation like foo % bar is performed, I can't really know what the
-    # contents of foo and bar are. I believe the best approach is to "correct"
-    # the to-be-converted code and let bytesformatter handle that case in
-    # runtime.
-    PATTERN = '''
-              term< formatstr=STRING '%' data=STRING > |
-              term< formatstr=STRING '%' data=atom > |
-              term< formatstr=NAME '%' data=any > |
-              term< formatstr=any '%' data=any >
-              '''
-
-    def transform(self, node, results):
-        for bfn in blacklist:
-            if self.filename.endswith(bfn):
-                return
-        if not self.filename.endswith('mercurial/py3kcompat.py'):
-            touch_import('mercurial', 'py3kcompat', node=node)
-
-        formatstr = results['formatstr'].clone()
-        data = results['data'].clone()
-        formatstr.prefix = '' # remove spaces from start
-
-        if isnumberremainder(formatstr, data):
-            return
-
-        # We have two possibilities:
-        # 1- An identifier or name is passed, it is going to be a leaf, thus, we
-        #    just need to copy its value as an argument to the formatter;
-        # 2- A tuple is explicitly passed. In this case, we're gonna explode it
-        # to pass to the formatter
-        # TODO: Check for normal strings. They don't need to be translated
-
-        if is_tuple(data):
-            args = [formatstr, Comma().clone()] + \
-                   [c.clone() for c in data.children[:]]
-        else:
-            args = [formatstr, Comma().clone(), data]
-
-        call = Call(Name('bytesformatter', prefix=' '), args)
-        return call
diff --git a/contrib/hgfixes/fix_leftover_imports.py b/contrib/hgfixes/fix_leftover_imports.py
deleted file mode 100644
--- a/contrib/hgfixes/fix_leftover_imports.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"Fixer that translates some APIs ignored by the default 2to3 fixers."
-
-# FIXME: This fixer has some ugly hacks. Its main design is based on that of
-# fix_imports, from lib2to3. Unfortunately, the fix_imports framework only
-# changes module names "without dots", meaning it won't work for some changes
-# in the email module/package. Thus this fixer was born. I believe that with a
-# bit more thinking, a more generic fixer can be implemented, but I'll leave
-# that as future work.
-
-from lib2to3.fixer_util import Name
-from lib2to3.fixes import fix_imports
-
-# This maps the old names to the new names. Note that a drawback of the current
-# design is that the dictionary keys MUST have EXACTLY one dot (.) in them,
-# otherwise things will break. (If you don't need a module hierarchy, you're
-# better of just inherit from fix_imports and overriding the MAPPING dict.)
-
-MAPPING = {'email.Utils': 'email.utils',
-           'email.Errors': 'email.errors',
-           'email.Header': 'email.header',
-           'email.Parser': 'email.parser',
-           'email.Encoders': 'email.encoders',
-           'email.MIMEText': 'email.mime.text',
-           'email.MIMEBase': 'email.mime.base',
-           'email.Generator': 'email.generator',
-           'email.MIMEMultipart': 'email.mime.multipart',
-}
-
-def alternates(members):
-    return "(" + "|".join(map(repr, members)) + ")"
-
-def build_pattern(mapping=MAPPING):
-    packages = {}
-    for key in mapping:
-        # What we are doing here is the following: with dotted names, we'll
-        # have something like package_name <trailer '.' module>. Then, we are
-        # making a dictionary to copy this structure. For example, if
-        # mapping={'A.B': 'a.b', 'A.C': 'a.c'}, it will generate the dictionary
-        # {'A': ['b', 'c']} to, then, generate something like "A <trailer '.'
-        # ('b' | 'c')".
-        name = key.split('.')
-        prefix = name[0]
-        if prefix in packages:
-            packages[prefix].append(name[1:][0])
-        else:
-            packages[prefix] = name[1:]
-
-    mod_list = ' | '.join(["'%s' '.' ('%s')" %
-        (key, "' | '".join(packages[key])) for key in packages])
-    mod_list = '(' + mod_list + ' )'
-
-    yield """name_import=import_name< 'import' module_name=dotted_name< %s > >
-          """ % mod_list
-
-    yield """name_import=import_name< 'import'
-            multiple_imports=dotted_as_names< any*
-            module_name=dotted_name< %s >
-            any* >
-            >""" % mod_list
-
-    packs = ' | '.join(["'%s' trailer<'.' ('%s')>" % (key,
-               "' | '".join(packages[key])) for key in packages])
-
-    yield "power< package=(%s) trailer<'.' any > any* >" % packs
-
-class FixLeftoverImports(fix_imports.FixImports):
-    # We want to run this fixer after fix_import has run (this shouldn't matter
-    # for hg, though, as setup3k prefers to run the default fixers first)
-    mapping = MAPPING
-
-    def build_pattern(self):
-        return "|".join(build_pattern(self.mapping))
-
-    def transform(self, node, results):
-        # Mostly copied from fix_imports.py
-        import_mod = results.get("module_name")
-        if import_mod:
-            try:
-                mod_name = import_mod.value
-            except AttributeError:
-                # XXX: A hack to remove whitespace prefixes and suffixes
-                mod_name = str(import_mod).strip()
-            new_name = self.mapping[mod_name]
-            import_mod.replace(Name(new_name, prefix=import_mod.prefix))
-            if "name_import" in results:
-                # If it's not a "from x import x, y" or "import x as y" import,
-                # marked its usage to be replaced.
-                self.replace[mod_name] = new_name
-            if "multiple_imports" in results:
-                # This is a nasty hack to fix multiple imports on a line (e.g.,
-                # "import StringIO, urlparse"). The problem is that I can't
-                # figure out an easy way to make a pattern recognize the keys of
-                # MAPPING randomly sprinkled in an import statement.
-                results = self.match(node)
-                if results:
-                    self.transform(node, results)
-        else:
-            # Replace usage of the module.
-            # Now this is, mostly, a hack
-            bare_name = results["package"][0]
-            bare_name_text = ''.join(map(str, results['package'])).strip()
-            new_name = self.replace.get(bare_name_text)
-            prefix = results['package'][0].prefix
-            if new_name:
-                bare_name.replace(Name(new_name, prefix=prefix))
-                results["package"][1].replace(Name(''))
-
diff --git a/contrib/import-checker.py b/contrib/import-checker.py
--- a/contrib/import-checker.py
+++ b/contrib/import-checker.py
@@ -489,20 +489,16 @@ def verify_modern_convention(module, roo
                 if n.name in requirealias and n.asname != requirealias[n.name]:
                     yield msg('%s from %s must be "as" aliased to %s',
                               n.name, fullname, requirealias[n.name])
 
 def verify_stdlib_on_own_line(root):
     """Given some python source, verify that stdlib imports are done
     in separate statements from relative local module imports.
 
-    Observing this limitation is important as it works around an
-    annoying lib2to3 bug in relative import rewrites:
-    http://bugs.python.org/issue19510.
-
     >>> list(verify_stdlib_on_own_line(ast.parse('import sys, foo')))
     [('mixed imports\\n   stdlib:    sys\\n   relative:  foo', 1)]
     >>> list(verify_stdlib_on_own_line(ast.parse('import sys, os')))
     []
     >>> list(verify_stdlib_on_own_line(ast.parse('import foo, bar')))
     []
     """
     for node in ast.walk(root):
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
@@ -10,19 +10,16 @@
   contrib/check-config.py requires print_function
   contrib/debugcmdserver.py not using absolute_import
   contrib/debugcmdserver.py requires print_function
   contrib/debugshell.py not using absolute_import
   contrib/fixpax.py not using absolute_import
   contrib/fixpax.py requires print_function
   contrib/hgclient.py not using absolute_import
   contrib/hgclient.py requires print_function
-  contrib/hgfixes/fix_bytes.py not using absolute_import
-  contrib/hgfixes/fix_bytesmod.py not using absolute_import
-  contrib/hgfixes/fix_leftover_imports.py not using absolute_import
   contrib/import-checker.py not using absolute_import
   contrib/import-checker.py requires print_function
   contrib/memory.py not using absolute_import
   contrib/perf.py not using absolute_import
   contrib/python-hook-examples.py not using absolute_import
   contrib/revsetbenchmarks.py not using absolute_import
   contrib/revsetbenchmarks.py requires print_function
   contrib/showstack.py not using absolute_import


More information about the Mercurial-devel mailing list