[PATCH 3 of 3 RFC] hgfixes: added a fixer that makes bytes to be formatted correctly

Renato Cunha renatoc at gmail.com
Tue Aug 3 12:06:40 CDT 2010


# HG changeset patch
# User Renato Cunha <renatoc at gmail.com>
# Date 1280854754 10800
# Node ID 8c6e4c0a5ed121deccef0b3a68c3ff40a8c85335
# Parent  6f03bc4c53e5d9fa072923bf1beb29c4e9aa8531
hgfixes: added a fixer that makes bytes to be formatted correctly

This patch implement a fixer that replaces all calls to the '%' when bytes
arguments are used to a call to bytesformatter(), a function that knows how to
format byte strings. As one can't be sure if a formatting call is done when
only variables are used in a '%' call, these calls are also translated. The
bytesformatter, in runtime, makes sure to return the "raw" % operation if
that's what was intended.

diff --git a/contrib/hgfixes/fix_bytesmod.py b/contrib/hgfixes/fix_bytesmod.py
new file mode 100644
--- /dev/null
+++ b/contrib/hgfixes/fix_bytesmod.py
@@ -0,0 +1,63 @@
+"""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):
+        if self.filename in blacklist:
+            return
+        elif self.filename == 'mercurial/util.py':
+            touch_import('.', '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
+


More information about the Mercurial-devel mailing list