Mercurial > public > mercurial-scm > hg
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 |