Issue #1515: Enable use of deepcopy() with instance methods. Patch by Robert Collins.
[python.git] / Lib / lib2to3 / fixes / fix_urllib.py
blobdb18ca84eef0f44139405752e9e57c55c24a629b
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
7 # Local imports
8 from .fix_imports import alternates, FixImports
9 from .. import fixer_base
10 from ..fixer_util import Name, Comma, FromImport, Newline, attr_chain
12 MAPPING = {'urllib': [
13 ('urllib.request',
14 ['URLOpener', 'FancyURLOpener', 'urlretrieve',
15 '_urlopener', 'urlopen', 'urlcleanup',
16 'pathname2url', 'url2pathname']),
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']),
44 # Duplicate the url parsing functions for urllib2.
45 MAPPING["urllib2"].append(MAPPING["urllib"][1])
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)
71 class FixUrllib(FixImports):
73 def build_pattern(self):
74 return "|".join(build_pattern())
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
84 names = []
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)
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')
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,
115 'This is an invalid module element')
117 # Multiple members being imported
118 else:
119 # a dictionary for replacements, order matters
120 modules = []
121 mod_dict = {}
122 members = results.get('members')
123 for member in members:
124 member = member.value
125 # we only care about the actual members
126 if member != ',':
127 for change in MAPPING[mod_member.value]:
128 if member in change[1]:
129 if change[0] in mod_dict:
130 mod_dict[change[0]].append(member)
131 else:
132 mod_dict[change[0]] = [member]
133 modules.append(change[0])
135 new_nodes = []
136 for module in modules:
137 elts = mod_dict[module]
138 names = []
139 for elt in elts[:-1]:
140 names.extend([Name(elt, prefix=pref), Comma()])
141 names.append(Name(elts[-1], prefix=pref))
142 new_nodes.append(FromImport(module, names))
143 if new_nodes:
144 nodes = []
145 for new_node in new_nodes[:-1]:
146 nodes.extend([new_node, Newline()])
147 nodes.append(new_nodes[-1])
148 node.replace(nodes)
149 else:
150 self.cannot_convert(node, 'All module elements are invalid')
152 def transform_dot(self, node, results):
153 """Transform for calls to module members in code."""
154 module_dot = results.get('bare_with_attr')
155 member = results.get('member')
156 new_name = None
157 if isinstance(member, list):
158 member = member[0]
159 for change in MAPPING[module_dot.value]:
160 if member.value in change[1]:
161 new_name = change[0]
162 break
163 if new_name:
164 module_dot.replace(Name(new_name,
165 prefix=module_dot.prefix))
166 else:
167 self.cannot_convert(node, 'This is an invalid module element')
169 def transform(self, node, results):
170 if results.get('module'):
171 self.transform_import(node, results)
172 elif results.get('mod_member'):
173 self.transform_member(node, results)
174 elif results.get('bare_with_attr'):
175 self.transform_dot(node, results)
176 # Renaming and star imports are not supported for these modules.
177 elif results.get('module_star'):
178 self.cannot_convert(node, 'Cannot handle star imports.')
179 elif results.get('module_as'):
180 self.cannot_convert(node, 'This module is now multiple modules')