| """Fix changes imports of urllib which are now incompatible. |
| This is rather similar to fix_imports, but because of the more |
| complex nature of the fixing for urllib, it has its own fixer. |
| """ |
| # Author: Nick Edds |
| |
| # Local imports |
| from .fix_imports import alternates, FixImports |
| from .. import fixer_base |
| from ..fixer_util import Name, Comma, FromImport, Newline, attr_chain |
| |
| MAPPING = {'urllib': [ |
| ('urllib.request', |
| ['URLOpener', 'FancyURLOpener', 'urlretrieve', |
| '_urlopener', 'urlopen', 'urlcleanup', |
| 'pathname2url', 'url2pathname']), |
| ('urllib.parse', |
| ['quote', 'quote_plus', 'unquote', 'unquote_plus', |
| 'urlencode', 'splitattr', 'splithost', 'splitnport', |
| 'splitpasswd', 'splitport', 'splitquery', 'splittag', |
| 'splittype', 'splituser', 'splitvalue', ]), |
| ('urllib.error', |
| ['ContentTooShortError'])], |
| 'urllib2' : [ |
| ('urllib.request', |
| ['urlopen', 'install_opener', 'build_opener', |
| 'Request', 'OpenerDirector', 'BaseHandler', |
| 'HTTPDefaultErrorHandler', 'HTTPRedirectHandler', |
| 'HTTPCookieProcessor', 'ProxyHandler', |
| 'HTTPPasswordMgr', |
| 'HTTPPasswordMgrWithDefaultRealm', |
| 'AbstractBasicAuthHandler', |
| 'HTTPBasicAuthHandler', 'ProxyBasicAuthHandler', |
| 'AbstractDigestAuthHandler', |
| 'HTTPDigestAuthHandler', 'ProxyDigestAuthHandler', |
| 'HTTPHandler', 'HTTPSHandler', 'FileHandler', |
| 'FTPHandler', 'CacheFTPHandler', |
| 'UnknownHandler']), |
| ('urllib.error', |
| ['URLError', 'HTTPError']), |
| ] |
| } |
| |
| # Duplicate the url parsing functions for urllib2. |
| MAPPING["urllib2"].append(MAPPING["urllib"][1]) |
| |
| |
| def build_pattern(): |
| bare = set() |
| for old_module, changes in MAPPING.items(): |
| for change in changes: |
| new_module, members = change |
| members = alternates(members) |
| yield """import_name< 'import' (module=%r |
| | dotted_as_names< any* module=%r any* >) > |
| """ % (old_module, old_module) |
| yield """import_from< 'from' mod_member=%r 'import' |
| ( member=%s | import_as_name< member=%s 'as' any > | |
| import_as_names< members=any* >) > |
| """ % (old_module, members, members) |
| yield """import_from< 'from' module_star=%r 'import' star='*' > |
| """ % old_module |
| yield """import_name< 'import' |
| dotted_as_name< module_as=%r 'as' any > > |
| """ % old_module |
| # bare_with_attr has a special significance for FixImports.match(). |
| yield """power< bare_with_attr=%r trailer< '.' member=%s > any* > |
| """ % (old_module, members) |
| |
| |
| class FixUrllib(FixImports): |
| |
| def build_pattern(self): |
| return "|".join(build_pattern()) |
| |
| def transform_import(self, node, results): |
| """Transform for the basic import case. Replaces the old |
| import name with a comma separated list of its |
| replacements. |
| """ |
| import_mod = results.get('module') |
| pref = import_mod.prefix |
| |
| names = [] |
| |
| # create a Node list of the replacement modules |
| for name in MAPPING[import_mod.value][:-1]: |
| names.extend([Name(name[0], prefix=pref), Comma()]) |
| names.append(Name(MAPPING[import_mod.value][-1][0], prefix=pref)) |
| import_mod.replace(names) |
| |
| def transform_member(self, node, results): |
| """Transform for imports of specific module elements. Replaces |
| the module to be imported from with the appropriate new |
| module. |
| """ |
| mod_member = results.get('mod_member') |
| pref = mod_member.prefix |
| member = results.get('member') |
| |
| # Simple case with only a single member being imported |
| if member: |
| # this may be a list of length one, or just a node |
| if isinstance(member, list): |
| member = member[0] |
| new_name = None |
| for change in MAPPING[mod_member.value]: |
| if member.value in change[1]: |
| new_name = change[0] |
| break |
| if new_name: |
| mod_member.replace(Name(new_name, prefix=pref)) |
| else: |
| self.cannot_convert(node, |
| 'This is an invalid module element') |
| |
| # Multiple members being imported |
| else: |
| # a dictionary for replacements, order matters |
| modules = [] |
| mod_dict = {} |
| members = results.get('members') |
| for member in members: |
| member = member.value |
| # we only care about the actual members |
| if member != ',': |
| for change in MAPPING[mod_member.value]: |
| if member in change[1]: |
| if change[0] in mod_dict: |
| mod_dict[change[0]].append(member) |
| else: |
| mod_dict[change[0]] = [member] |
| modules.append(change[0]) |
| |
| new_nodes = [] |
| for module in modules: |
| elts = mod_dict[module] |
| names = [] |
| for elt in elts[:-1]: |
| names.extend([Name(elt, prefix=pref), Comma()]) |
| names.append(Name(elts[-1], prefix=pref)) |
| new_nodes.append(FromImport(module, names)) |
| if new_nodes: |
| nodes = [] |
| for new_node in new_nodes[:-1]: |
| nodes.extend([new_node, Newline()]) |
| nodes.append(new_nodes[-1]) |
| node.replace(nodes) |
| else: |
| self.cannot_convert(node, 'All module elements are invalid') |
| |
| def transform_dot(self, node, results): |
| """Transform for calls to module members in code.""" |
| module_dot = results.get('bare_with_attr') |
| member = results.get('member') |
| new_name = None |
| if isinstance(member, list): |
| member = member[0] |
| for change in MAPPING[module_dot.value]: |
| if member.value in change[1]: |
| new_name = change[0] |
| break |
| if new_name: |
| module_dot.replace(Name(new_name, |
| prefix=module_dot.prefix)) |
| else: |
| self.cannot_convert(node, 'This is an invalid module element') |
| |
| def transform(self, node, results): |
| if results.get('module'): |
| self.transform_import(node, results) |
| elif results.get('mod_member'): |
| self.transform_member(node, results) |
| elif results.get('bare_with_attr'): |
| self.transform_dot(node, results) |
| # Renaming and star imports are not supported for these modules. |
| elif results.get('module_star'): |
| self.cannot_convert(node, 'Cannot handle star imports.') |
| elif results.get('module_as'): |
| self.cannot_convert(node, 'This module is now multiple modules') |