fix_urllib.py 8.2 KB

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