contrib/hgfixes/fix_bytesmod.py
branchstable
changeset 28977 740156eedf2c
parent 28665 2d39f987f0ba
parent 28976 9fb2e8c8f320
child 28978 1f277ae29168
equal deleted inserted replaced
28665:2d39f987f0ba 28977:740156eedf2c
     1 """Fixer that changes bytes % whatever to a function that actually formats
       
     2 it."""
       
     3 
       
     4 from lib2to3 import fixer_base
       
     5 from lib2to3.fixer_util import is_tuple, Call, Comma, Name, touch_import
       
     6 
       
     7 # XXX: Implementing a blacklist in 2to3 turned out to be more troublesome than
       
     8 # blacklisting some modules inside the fixers. So, this is what I came with.
       
     9 
       
    10 blacklist = ['mercurial/demandimport.py',
       
    11              'mercurial/py3kcompat.py',
       
    12              'mercurial/i18n.py',
       
    13             ]
       
    14 
       
    15 def isnumberremainder(formatstr, data):
       
    16     try:
       
    17         if data.value.isdigit():
       
    18             return True
       
    19     except AttributeError:
       
    20         return False
       
    21 
       
    22 class FixBytesmod(fixer_base.BaseFix):
       
    23     # XXX: There's one case (I suppose) I can't handle: when a remainder
       
    24     # operation like foo % bar is performed, I can't really know what the
       
    25     # contents of foo and bar are. I believe the best approach is to "correct"
       
    26     # the to-be-converted code and let bytesformatter handle that case in
       
    27     # runtime.
       
    28     PATTERN = '''
       
    29               term< formatstr=STRING '%' data=STRING > |
       
    30               term< formatstr=STRING '%' data=atom > |
       
    31               term< formatstr=NAME '%' data=any > |
       
    32               term< formatstr=any '%' data=any >
       
    33               '''
       
    34 
       
    35     def transform(self, node, results):
       
    36         for bfn in blacklist:
       
    37             if self.filename.endswith(bfn):
       
    38                 return
       
    39         if not self.filename.endswith('mercurial/py3kcompat.py'):
       
    40             touch_import('mercurial', 'py3kcompat', node=node)
       
    41 
       
    42         formatstr = results['formatstr'].clone()
       
    43         data = results['data'].clone()
       
    44         formatstr.prefix = '' # remove spaces from start
       
    45 
       
    46         if isnumberremainder(formatstr, data):
       
    47             return
       
    48 
       
    49         # We have two possibilities:
       
    50         # 1- An identifier or name is passed, it is going to be a leaf, thus, we
       
    51         #    just need to copy its value as an argument to the formatter;
       
    52         # 2- A tuple is explicitly passed. In this case, we're gonna explode it
       
    53         # to pass to the formatter
       
    54         # TODO: Check for normal strings. They don't need to be translated
       
    55 
       
    56         if is_tuple(data):
       
    57             args = [formatstr, Comma().clone()] + \
       
    58                    [c.clone() for c in data.children[:]]
       
    59         else:
       
    60             args = [formatstr, Comma().clone(), data]
       
    61 
       
    62         call = Call(Name('bytesformatter', prefix=' '), args)
       
    63         return call