view contrib/hgfixes/fix_bytesmod.py @ 19966:7985e3469f58 stable

largefiles: systematic testing of merges to/from largefiles 427ce5633c1c fixed one problem with update and added a test case for it. The test coverage was thus insufficient before that. To make sure we have good test coverage in this area we add systematic testing of all cases of merges that may or may not change normal files to largefiles or vice versa. The tests shows some annoying extra merge prompts in some cases, but these prompts are hard to avoid and they are now "safe" - they do not leave the system in a confused inconsistent state.
author Mads Kiilerich <madski@unity3d.com>
date Mon, 28 Oct 2013 22:34:05 +0100
parents 681f7b9213a4
children 74daabdf5ab5
line wrap: on
line source

"""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