Show More
@@ -0,0 +1,108 b'' | |||||
|
1 | "Fixer that translates some APIs ignored by the default 2to3 fixers." | |||
|
2 | ||||
|
3 | # FIXME: This fixer has some ugly hacks. Its main design is based on that of | |||
|
4 | # fix_imports, from lib2to3. Unfortunately, the fix_imports framework only | |||
|
5 | # changes module names "without dots", meaning it won't work for some changes | |||
|
6 | # in the email module/package. Thus this fixer was born. I believe that with a | |||
|
7 | # bit more thinking, a more generic fixer can be implemented, but I'll leave | |||
|
8 | # that as future work. | |||
|
9 | ||||
|
10 | from lib2to3.fixer_util import Name | |||
|
11 | from lib2to3.fixes import fix_imports | |||
|
12 | ||||
|
13 | # This maps the old names to the new names. Note that a drawback of the current | |||
|
14 | # design is that the dictionary keys MUST have EXACTLY one dot (.) in them, | |||
|
15 | # otherwise things will break. (If you don't need a module hierarchy, you're | |||
|
16 | # better of just inherit from fix_imports and overriding the MAPPING dict.) | |||
|
17 | ||||
|
18 | MAPPING = {'email.Utils': 'email.utils', | |||
|
19 | 'email.Errors': 'email.errors', | |||
|
20 | 'email.Header': 'email.header', | |||
|
21 | 'email.Parser': 'email.parser', | |||
|
22 | 'email.Encoders': 'email.encoders', | |||
|
23 | 'email.MIMEText': 'email.mime.text', | |||
|
24 | 'email.MIMEBase': 'email.mime.base', | |||
|
25 | 'email.Generator': 'email.generator', | |||
|
26 | 'email.MIMEMultipart': 'email.mime.multipart', | |||
|
27 | } | |||
|
28 | ||||
|
29 | def alternates(members): | |||
|
30 | return "(" + "|".join(map(repr, members)) + ")" | |||
|
31 | ||||
|
32 | def build_pattern(mapping=MAPPING): | |||
|
33 | packages = {} | |||
|
34 | for key in mapping: | |||
|
35 | # What we are doing here is the following: with dotted names, we'll | |||
|
36 | # have something like package_name <trailer '.' module>. Then, we are | |||
|
37 | # making a dictionary to copy this structure. For example, if | |||
|
38 | # mapping={'A.B': 'a.b', 'A.C': 'a.c'}, it will generate the dictionary | |||
|
39 | # {'A': ['b', 'c']} to, then, generate something like "A <trailer '.' | |||
|
40 | # ('b' | 'c')". | |||
|
41 | name = key.split('.') | |||
|
42 | prefix = name[0] | |||
|
43 | if prefix in packages: | |||
|
44 | packages[prefix].append(name[1:][0]) | |||
|
45 | else: | |||
|
46 | packages[prefix] = name[1:] | |||
|
47 | ||||
|
48 | mod_list = ' | '.join(["'%s' '.' ('%s')" % | |||
|
49 | (key, "' | '".join(packages[key])) for key in packages]) | |||
|
50 | mod_list = '(' + mod_list + ' )' | |||
|
51 | bare_names = alternates(mapping.keys()) | |||
|
52 | ||||
|
53 | yield """name_import=import_name< 'import' module_name=dotted_name< %s > > | |||
|
54 | """ % mod_list | |||
|
55 | ||||
|
56 | yield """name_import=import_name< 'import' | |||
|
57 | multiple_imports=dotted_as_names< any* | |||
|
58 | module_name=dotted_name< %s > | |||
|
59 | any* > | |||
|
60 | >""" % mod_list | |||
|
61 | ||||
|
62 | packs = ' | '.join(["'%s' trailer<'.' ('%s')>" % (key, | |||
|
63 | "' | '".join(packages[key])) for key in packages]) | |||
|
64 | ||||
|
65 | yield "power< package=(%s) trailer<'.' any > any* >" % packs | |||
|
66 | ||||
|
67 | class FixLeftoverImports(fix_imports.FixImports): | |||
|
68 | # We want to run this fixer after fix_import has run (this shouldn't matter | |||
|
69 | # for hg, though, as setup3k prefers to run the default fixers first) | |||
|
70 | mapping = MAPPING | |||
|
71 | ||||
|
72 | def build_pattern(self): | |||
|
73 | return "|".join(build_pattern(self.mapping)) | |||
|
74 | ||||
|
75 | def transform(self, node, results): | |||
|
76 | # Mostly copied from fix_imports.py | |||
|
77 | import_mod = results.get("module_name") | |||
|
78 | if import_mod: | |||
|
79 | try: | |||
|
80 | mod_name = import_mod.value | |||
|
81 | except AttributeError: | |||
|
82 | # XXX: A hack to remove whitespace prefixes and suffixes | |||
|
83 | mod_name = str(import_mod).strip() | |||
|
84 | new_name = self.mapping[mod_name] | |||
|
85 | import_mod.replace(Name(new_name, prefix=import_mod.prefix)) | |||
|
86 | if "name_import" in results: | |||
|
87 | # If it's not a "from x import x, y" or "import x as y" import, | |||
|
88 | # marked its usage to be replaced. | |||
|
89 | self.replace[mod_name] = new_name | |||
|
90 | if "multiple_imports" in results: | |||
|
91 | # This is a nasty hack to fix multiple imports on a line (e.g., | |||
|
92 | # "import StringIO, urlparse"). The problem is that I can't | |||
|
93 | # figure out an easy way to make a pattern recognize the keys of | |||
|
94 | # MAPPING randomly sprinkled in an import statement. | |||
|
95 | results = self.match(node) | |||
|
96 | if results: | |||
|
97 | self.transform(node, results) | |||
|
98 | else: | |||
|
99 | # Replace usage of the module. | |||
|
100 | # Now this is, mostly, a hack | |||
|
101 | bare_name = results["package"][0] | |||
|
102 | bare_name_text = ''.join(map(str, results['package'])).strip() | |||
|
103 | new_name = self.replace.get(bare_name_text) | |||
|
104 | prefix = results['package'][0].prefix | |||
|
105 | if new_name: | |||
|
106 | bare_name.replace(Name(new_name, prefix=prefix)) | |||
|
107 | results["package"][1].replace(Name('')) | |||
|
108 |
General Comments 0
You need to be logged in to leave comments.
Login now