1 """Fix changes imports of urllib which are now incompatible.
2 This is rather similar to fix_imports, but because of the more
3 complex nature of the fixing for urllib, it has its own fixer.
4 """
5 # Author: Nick Edds
6
7 # Local imports
8 from lib2to3.fixes.fix_imports import alternates, FixImports
9 from lib2to3.fixer_util import (Name, Comma, FromImport, Newline,
10 find_indentation, Node, syms)
11
12 MAPPING = {"urllib": [
13 ("urllib.request",
14 ["URLopener", "FancyURLopener", "urlretrieve",
15 "_urlopener", "urlopen", "urlcleanup",
16 "pathname2url", "url2pathname", "getproxies"]),
17 ("urllib.parse",
18 ["quote", "quote_plus", "unquote", "unquote_plus",
19 "urlencode", "splitattr", "splithost", "splitnport",
20 "splitpasswd", "splitport", "splitquery", "splittag",
21 "splittype", "splituser", "splitvalue", ]),
22 ("urllib.error",
23 ["ContentTooShortError"])],
24 "urllib2" : [
25 ("urllib.request",
26 ["urlopen", "install_opener", "build_opener",
27 "Request", "OpenerDirector", "BaseHandler",
28 "HTTPDefaultErrorHandler", "HTTPRedirectHandler",
29 "HTTPCookieProcessor", "ProxyHandler",
30 "HTTPPasswordMgr",
31 "HTTPPasswordMgrWithDefaultRealm",
32 "AbstractBasicAuthHandler",
33 "HTTPBasicAuthHandler", "ProxyBasicAuthHandler",
34 "AbstractDigestAuthHandler",
35 "HTTPDigestAuthHandler", "ProxyDigestAuthHandler",
36 "HTTPHandler", "HTTPSHandler", "FileHandler",
37 "FTPHandler", "CacheFTPHandler",
38 "UnknownHandler"]),
39 ("urllib.error",
40 ["URLError", "HTTPError"]),
41 ]
42 }
43
44 # Duplicate the url parsing functions for urllib2.
45 MAPPING["urllib2"].append(MAPPING["urllib"][1])
46
47
48 def build_pattern():
49 bare = set()
50 for old_module, changes in MAPPING.items():
51 for change in changes:
52 new_module, members = change
53 members = alternates(members)
54 yield """import_name< 'import' (module=%r
55 | dotted_as_names< any* module=%r any* >) >
56 """ % (old_module, old_module)
57 yield """import_from< 'from' mod_member=%r 'import'
58 ( member=%s | import_as_name< member=%s 'as' any > |
59 import_as_names< members=any* >) >
60 """ % (old_module, members, members)
61 yield """import_from< 'from' module_star=%r 'import' star='*' >
62 """ % old_module
63 yield """import_name< 'import'
64 dotted_as_name< module_as=%r 'as' any > >
65 """ % old_module
66 # bare_with_attr has a special significance for FixImports.match().
67 yield """power< bare_with_attr=%r trailer< '.' member=%s > any* >
68 """ % (old_module, members)
69
70
71 class ESC[4;38;5;81mFixUrllib(ESC[4;38;5;149mFixImports):
72
73 def build_pattern(self):
74 return "|".join(build_pattern())
75
76 def transform_import(self, node, results):
77 """Transform for the basic import case. Replaces the old
78 import name with a comma separated list of its
79 replacements.
80 """
81 import_mod = results.get("module")
82 pref = import_mod.prefix
83
84 names = []
85
86 # create a Node list of the replacement modules
87 for name in MAPPING[import_mod.value][:-1]:
88 names.extend([Name(name[0], prefix=pref), Comma()])
89 names.append(Name(MAPPING[import_mod.value][-1][0], prefix=pref))
90 import_mod.replace(names)
91
92 def transform_member(self, node, results):
93 """Transform for imports of specific module elements. Replaces
94 the module to be imported from with the appropriate new
95 module.
96 """
97 mod_member = results.get("mod_member")
98 pref = mod_member.prefix
99 member = results.get("member")
100
101 # Simple case with only a single member being imported
102 if member:
103 # this may be a list of length one, or just a node
104 if isinstance(member, list):
105 member = member[0]
106 new_name = None
107 for change in MAPPING[mod_member.value]:
108 if member.value in change[1]:
109 new_name = change[0]
110 break
111 if new_name:
112 mod_member.replace(Name(new_name, prefix=pref))
113 else:
114 self.cannot_convert(node, "This is an invalid module element")
115
116 # Multiple members being imported
117 else:
118 # a dictionary for replacements, order matters
119 modules = []
120 mod_dict = {}
121 members = results["members"]
122 for member in members:
123 # we only care about the actual members
124 if member.type == syms.import_as_name:
125 as_name = member.children[2].value
126 member_name = member.children[0].value
127 else:
128 member_name = member.value
129 as_name = None
130 if member_name != ",":
131 for change in MAPPING[mod_member.value]:
132 if member_name in change[1]:
133 if change[0] not in mod_dict:
134 modules.append(change[0])
135 mod_dict.setdefault(change[0], []).append(member)
136
137 new_nodes = []
138 indentation = find_indentation(node)
139 first = True
140 def handle_name(name, prefix):
141 if name.type == syms.import_as_name:
142 kids = [Name(name.children[0].value, prefix=prefix),
143 name.children[1].clone(),
144 name.children[2].clone()]
145 return [Node(syms.import_as_name, kids)]
146 return [Name(name.value, prefix=prefix)]
147 for module in modules:
148 elts = mod_dict[module]
149 names = []
150 for elt in elts[:-1]:
151 names.extend(handle_name(elt, pref))
152 names.append(Comma())
153 names.extend(handle_name(elts[-1], pref))
154 new = FromImport(module, names)
155 if not first or node.parent.prefix.endswith(indentation):
156 new.prefix = indentation
157 new_nodes.append(new)
158 first = False
159 if new_nodes:
160 nodes = []
161 for new_node in new_nodes[:-1]:
162 nodes.extend([new_node, Newline()])
163 nodes.append(new_nodes[-1])
164 node.replace(nodes)
165 else:
166 self.cannot_convert(node, "All module elements are invalid")
167
168 def transform_dot(self, node, results):
169 """Transform for calls to module members in code."""
170 module_dot = results.get("bare_with_attr")
171 member = results.get("member")
172 new_name = None
173 if isinstance(member, list):
174 member = member[0]
175 for change in MAPPING[module_dot.value]:
176 if member.value in change[1]:
177 new_name = change[0]
178 break
179 if new_name:
180 module_dot.replace(Name(new_name,
181 prefix=module_dot.prefix))
182 else:
183 self.cannot_convert(node, "This is an invalid module element")
184
185 def transform(self, node, results):
186 if results.get("module"):
187 self.transform_import(node, results)
188 elif results.get("mod_member"):
189 self.transform_member(node, results)
190 elif results.get("bare_with_attr"):
191 self.transform_dot(node, results)
192 # Renaming and star imports are not supported for these modules.
193 elif results.get("module_star"):
194 self.cannot_convert(node, "Cannot handle star imports.")
195 elif results.get("module_as"):
196 self.cannot_convert(node, "This module is now multiple modules")