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