fix_next.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. """Fixer for it.next() -> next(it), per PEP 3114."""
  2. # Author: Collin Winter
  3. # Things that currently aren't covered:
  4. # - listcomp "next" names aren't warned
  5. # - "with" statement targets aren't checked
  6. # Local imports
  7. from ..pgen2 import token
  8. from ..pygram import python_symbols as syms
  9. from .. import fixer_base
  10. from ..fixer_util import Name, Call, find_binding
  11. bind_warning = "Calls to builtin next() possibly shadowed by global binding"
  12. class FixNext(fixer_base.BaseFix):
  13. BM_compatible = True
  14. PATTERN = """
  15. power< base=any+ trailer< '.' attr='next' > trailer< '(' ')' > >
  16. |
  17. power< head=any+ trailer< '.' attr='next' > not trailer< '(' ')' > >
  18. |
  19. classdef< 'class' any+ ':'
  20. suite< any*
  21. funcdef< 'def'
  22. name='next'
  23. parameters< '(' NAME ')' > any+ >
  24. any* > >
  25. |
  26. global=global_stmt< 'global' any* 'next' any* >
  27. """
  28. order = "pre" # Pre-order tree traversal
  29. def start_tree(self, tree, filename):
  30. super(FixNext, self).start_tree(tree, filename)
  31. n = find_binding('next', tree)
  32. if n:
  33. self.warning(n, bind_warning)
  34. self.shadowed_next = True
  35. else:
  36. self.shadowed_next = False
  37. def transform(self, node, results):
  38. assert results
  39. base = results.get("base")
  40. attr = results.get("attr")
  41. name = results.get("name")
  42. if base:
  43. if self.shadowed_next:
  44. attr.replace(Name("__next__", prefix=attr.prefix))
  45. else:
  46. base = [n.clone() for n in base]
  47. base[0].prefix = ""
  48. node.replace(Call(Name("next", prefix=node.prefix), base))
  49. elif name:
  50. n = Name("__next__", prefix=name.prefix)
  51. name.replace(n)
  52. elif attr:
  53. # We don't do this transformation if we're assigning to "x.next".
  54. # Unfortunately, it doesn't seem possible to do this in PATTERN,
  55. # so it's being done here.
  56. if is_assign_target(node):
  57. head = results["head"]
  58. if "".join([str(n) for n in head]).strip() == '__builtin__':
  59. self.warning(node, bind_warning)
  60. return
  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)