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