fix_next_call.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. """
  2. Based on fix_next.py by Collin Winter.
  3. Replaces it.next() -> next(it), per PEP 3114.
  4. Unlike fix_next.py, this fixer doesn't replace the name of a next method with __next__,
  5. which would break Python 2 compatibility without further help from fixers in
  6. stage 2.
  7. """
  8. # Local imports
  9. from lib2to3.pgen2 import token
  10. from lib2to3.pygram import python_symbols as syms
  11. from lib2to3 import fixer_base
  12. from lib2to3.fixer_util import Name, Call, find_binding
  13. bind_warning = "Calls to builtin next() possibly shadowed by global binding"
  14. class FixNextCall(fixer_base.BaseFix):
  15. BM_compatible = True
  16. PATTERN = """
  17. power< base=any+ trailer< '.' attr='next' > trailer< '(' ')' > >
  18. |
  19. power< head=any+ trailer< '.' attr='next' > not trailer< '(' ')' > >
  20. |
  21. global=global_stmt< 'global' any* 'next' any* >
  22. """
  23. order = "pre" # Pre-order tree traversal
  24. def start_tree(self, tree, filename):
  25. super(FixNextCall, self).start_tree(tree, filename)
  26. n = find_binding('next', tree)
  27. if n:
  28. self.warning(n, bind_warning)
  29. self.shadowed_next = True
  30. else:
  31. self.shadowed_next = False
  32. def transform(self, node, results):
  33. assert results
  34. base = results.get("base")
  35. attr = results.get("attr")
  36. name = results.get("name")
  37. if base:
  38. if self.shadowed_next:
  39. # Omit this:
  40. # attr.replace(Name("__next__", prefix=attr.prefix))
  41. pass
  42. else:
  43. base = [n.clone() for n in base]
  44. base[0].prefix = ""
  45. node.replace(Call(Name("next", prefix=node.prefix), base))
  46. elif name:
  47. # Omit this:
  48. # n = Name("__next__", prefix=name.prefix)
  49. # name.replace(n)
  50. pass
  51. elif attr:
  52. # We don't do this transformation if we're assigning to "x.next".
  53. # Unfortunately, it doesn't seem possible to do this in PATTERN,
  54. # so it's being done here.
  55. if is_assign_target(node):
  56. head = results["head"]
  57. if "".join([str(n) for n in head]).strip() == '__builtin__':
  58. self.warning(node, bind_warning)
  59. return
  60. # Omit this:
  61. # attr.replace(Name("__next__"))
  62. elif "global" in results:
  63. self.warning(node, bind_warning)
  64. self.shadowed_next = True
  65. ### The following functions help test if node is part of an assignment
  66. ### target.
  67. def is_assign_target(node):
  68. assign = find_assign(node)
  69. if assign is None:
  70. return False
  71. for child in assign.children:
  72. if child.type == token.EQUAL:
  73. return False
  74. elif is_subtree(child, node):
  75. return True
  76. return False
  77. def find_assign(node):
  78. if node.type == syms.expr_stmt:
  79. return node
  80. if node.type == syms.simple_stmt or node.parent is None:
  81. return None
  82. return find_assign(node.parent)
  83. def is_subtree(root, node):
  84. if root == node:
  85. return True
  86. return any(is_subtree(c, node) for c in root.children)