comparison contrib/hgfixes/fix_bytesmod.py @ 11749:e627fef94604

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.
author Renato Cunha <renatoc@gmail.com>
date Tue, 03 Aug 2010 13:59:14 -0300
parents
children 681f7b9213a4
comparison
equal deleted inserted replaced
11748:37a70a784397 11749:e627fef94604
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 if self.filename in blacklist:
37 return
38 elif self.filename == 'mercurial/util.py':
39 touch_import('.', 'py3kcompat', node=node)
40
41 formatstr = results['formatstr'].clone()
42 data = results['data'].clone()
43 formatstr.prefix = '' # remove spaces from start
44
45 if isnumberremainder(formatstr, data):
46 return
47
48 # We have two possibilities:
49 # 1- An identifier or name is passed, it is going to be a leaf, thus, we
50 # just need to copy its value as an argument to the formatter;
51 # 2- A tuple is explicitly passed. In this case, we're gonna explode it
52 # to pass to the formatter
53 # TODO: Check for normal strings. They don't need to be translated
54
55 if is_tuple(data):
56 args = [formatstr, Comma().clone()] + \
57 [c.clone() for c in data.children[:]]
58 else:
59 args = [formatstr, Comma().clone(), data]
60
61 call = Call(Name('bytesformatter', prefix = ' '), args)
62 return call
63