[Python-checkins] r54442 - in sandbox/trunk/2to3: README fixes/fix_next.py tests/test_fixers.py

collin.winter python-checkins at python.org
Mon Mar 19 21:24:37 CET 2007


Author: collin.winter
Date: Mon Mar 19 21:24:35 2007
New Revision: 54442

Added:
   sandbox/trunk/2to3/fixes/fix_next.py   (contents, props changed)
Modified:
   sandbox/trunk/2to3/README
   sandbox/trunk/2to3/tests/test_fixers.py
Log:
Add a beta version of the next() fixer for PEP 3114.

Modified: sandbox/trunk/2to3/README
==============================================================================
--- sandbox/trunk/2to3/README	(original)
+++ sandbox/trunk/2to3/README	Mon Mar 19 21:24:35 2007
@@ -50,6 +50,8 @@
 
 * **fix_ne** - convert the "<>" operator to "!=".
 
+* **fix_next** - fixer for it.next() -> next(it), per PEP 3114.
+
 * **fix_print** - convert "print" statements to print() function calls.
 
 * **fix_raise** - convert "raise" statements to Python 3 syntax.

Added: sandbox/trunk/2to3/fixes/fix_next.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/2to3/fixes/fix_next.py	Mon Mar 19 21:24:35 2007
@@ -0,0 +1,183 @@
+"""Fixer for it.next() -> next(it), per PEP 3114."""
+# Author: Collin Winter
+
+# Things that currently aren't covered:
+#   - listcomp "next" names aren't warned
+#   - "with" statement targets aren't checked
+
+# Local imports
+import pytree
+from pgen2 import token
+from fixes import basefix
+from fixes.macros import Name, Call
+
+bind_warning = "Calls to builtin next() possibly shadowed by global binding"
+
+class DelayedStrNode(object):
+    def __init__(self, type, base):
+        self.parent = None
+        self.shadowed_next = False
+        self.base = base
+        self.type = type
+        self.value = ""
+        
+    def __str__(self):
+        b = "".join([str(n) for n in self.base])
+        if self.shadowed_next:
+            return "%s.__next__()" % b
+        else:
+            return "next(%s)" % b
+
+class FixNext(basefix.BaseFix):
+    PATTERN = """
+    power< base=any+ trailer< '.' 'next' > trailer< '(' ')' > >
+    |
+    power< head=any+ trailer< '.' attr='next' > not trailer< '(' ')' > >
+    |
+    classdef< 'class' any+ ':'
+              suite< any*
+                     funcdef< 'def'
+                              name='next'
+                              parameters< '(' NAME ')' > any+ >
+                     any* > >
+    |
+    global=global_stmt< 'global' any* 'next' any* >
+    |
+    mod=file_input< any+ >
+    """
+    
+    def start_tree(self, tree, filename):
+        super(FixNext, self).start_tree(tree, filename)
+        self.shadowed_next = False
+        self.delayed = []
+    
+    def transform(self, node):
+        syms = self.syms
+        results = self.match(node)
+        assert results
+        
+        base = results.get("base")
+        attr = results.get("attr")
+        name = results.get("name")
+        mod = results.get("mod")
+        
+        if base:
+            n = DelayedStrNode(syms.power, base)
+            node.replace(n)
+            self.delayed.append(n)
+        elif name:
+            n = Name("__next__")
+            n.set_prefix(name.get_prefix())
+            name.replace(n)
+        elif attr:
+            # We don't do this transformation if we're assignment to "x.next".
+            # Unfortunately, it doesn't seem possible to do this in PATTERN,
+            #  so it's being done here.
+            if is_assign_target(syms, node):
+                head = results["head"]
+                if "".join([str(n) for n in head]).strip() == '__builtin__':
+                    self.warning(node, bind_warning)
+                return
+            attr.replace(Name("__next__"))
+        elif "global" in results:
+            self.warning(node, bind_warning)
+            self.shadowed_next = True
+        elif mod:
+            n = find_binding(syms, 'next', mod)
+            if n:
+                self.warning(n, bind_warning)
+                self.shadowed_next = True
+                
+    def finish_tree(self, tree, filename):
+        super(FixNext, self).finish_tree(tree, filename)
+        if self.shadowed_next:
+            for node in self.delayed:
+                node.shadowed_next = True
+
+
+### The following functions are to find module-level bindings
+
+def find_binding(syms, name, file_input):
+    for child in file_input.children:
+        if child.type == syms.for_stmt:
+            if find(name, child.children[1]):
+                return child
+        elif child.type == syms.funcdef and child.children[1].value == name:
+            return child
+        elif is_import_binding(syms, child, name):
+            return child
+        elif child.type == syms.simple_stmt:
+            if child.children[0].type == syms.expr_stmt:
+                n = find(name, child.children[0].children[0])
+                if n:
+                    return n
+
+def find(name, node):
+    nodes = [node]
+    while nodes:
+        node = nodes.pop()
+        if isinstance(node, pytree.Node):
+            nodes.extend(node.children)
+        elif node.type == token.NAME and node.value == name:
+            return node
+    return None
+
+def is_import_binding(syms, node, name):
+    if node.type == syms.simple_stmt:
+        i = node.children[0]
+        if i.type == syms.import_name:
+            imp = i.children[1]
+            if imp.type == syms.dotted_as_names:
+                for child in imp.children:
+                    if child.type == syms.dotted_as_name:
+                        if child.children[2].value == name:
+                            return i
+            elif imp.type == syms.dotted_as_name:
+                last = imp.children[-1]
+                if last.type == token.NAME and last.value == name:
+                    return i
+        elif i.type == syms.import_from:
+            n = i.children[3]
+            if n.type == syms.import_as_names:
+                if find(name, n):
+                    return i
+            elif n.type == token.NAME and n.value == name:
+                return i
+    return None
+
+
+### The following functions help test if node is part of an assignment
+###  target.
+
+try:
+    any
+except NameError:
+    def any(l):
+        for o in l:
+            if o:
+                return True
+        return False
+
+def is_assign_target(syms, node):
+    assign = find_assign(syms, node)    
+    if assign is None:
+        return False
+            
+    for child in assign.children:
+        if child.type == token.EQUAL:
+            return False
+        elif is_subtree(child, node):
+            return True
+    return False
+    
+def find_assign(syms, node):
+    if node.type == syms.expr_stmt:
+        return node
+    if node.type == syms.simple_stmt or node.parent is None:
+        return None
+    return find_assign(syms, node.parent)
+
+def is_subtree(root, node):
+    if root == node:
+        return True
+    return any([is_subtree(c, node) for c in root.children])

Modified: sandbox/trunk/2to3/tests/test_fixers.py
==============================================================================
--- sandbox/trunk/2to3/tests/test_fixers.py	(original)
+++ sandbox/trunk/2to3/tests/test_fixers.py	Mon Mar 19 21:24:35 2007
@@ -1228,6 +1228,365 @@
                 (d, e, f) = xxx_todo_changeme1
                 x = 5"""
         self.check(b, a)
+        
+class Test_next(FixerTestCase):
+    fixer = "next"
+    
+    def test_1(self):
+        b = """it.next()"""
+        a = """next(it)"""
+        self.check(b, a)
+    
+    def test_2(self):
+        b = """a.b.c.d.next()"""
+        a = """next(a.b.c.d)"""
+        self.check(b, a)
+    
+    def test_3(self):
+        b = """(a + b).next()"""
+        a = """next((a + b))"""
+        self.check(b, a)
+    
+    def test_4(self):
+        b = """a().next()"""
+        a = """next(a())"""
+        self.check(b, a)
+    
+    def test_5(self):
+        b = """a().next() + b"""
+        a = """next(a()) + b"""
+        self.check(b, a)
+        
+    def test_method_1(self):
+        b = """
+            class A:
+                def next(self):
+                    pass
+            """
+        a = """
+            class A:
+                def __next__(self):
+                    pass
+            """
+        self.check(b, a)
+        
+    def test_method_2(self):
+        b = """
+            class A(object):
+                def next(self):
+                    pass
+            """
+        a = """
+            class A(object):
+                def __next__(self):
+                    pass
+            """
+        self.check(b, a)
+        
+    def test_method_3(self):
+        b = """
+            class A:
+                def next(x):
+                    pass
+            """
+        a = """
+            class A:
+                def __next__(x):
+                    pass
+            """
+        self.check(b, a)
+        
+    def test_method_4(self):
+        b = """
+            class A:
+                def __init__(self, foo):
+                    self.foo = foo
+            
+                def next(self):
+                    pass
+                    
+                def __iter__(self):
+                    return self
+            """
+        a = """
+            class A:
+                def __init__(self, foo):
+                    self.foo = foo
+            
+                def __next__(self):
+                    pass
+                    
+                def __iter__(self):
+                    return self
+            """
+        self.check(b, a)
+        
+    def test_method_unchanged(self):
+        s = """
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.check(s, s)
+        
+    def test_shadowing_assign_simple(self):
+        s = """
+            next = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_assign_tuple_1(self):
+        s = """
+            (next, a) = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_assign_tuple_2(self):
+        s = """
+            (a, (b, (next, c)), a) = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_assign_list_1(self):
+        s = """
+            [next, a] = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_assign_list_2(self):
+        s = """
+            [a, [b, [next, c]], a] = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+    
+    def test_builtin_assign(self):
+        s = """
+            def foo():
+                __builtin__.next = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+    
+    def test_builtin_assign_in_tuple(self):
+        s = """
+            def foo():
+                (a, __builtin__.next) = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+    
+    def test_assign_to_next(self):
+        s = """
+            def foo():
+                A.next = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.check(s, s)
+    
+    def test_assign_to_next_in_tuple(self):
+        s = """
+            def foo():
+                (a, A.next) = foo
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.check(s, s)
+        
+    def test_shadowing_import_1(self):
+        s = """
+            import foo.bar as next
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_import_2(self):
+        s = """
+            import bar, bar.foo as next
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_import_3(self):
+        s = """
+            import bar, bar.foo as next, baz
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_import_from_1(self):
+        s = """
+            from x import next
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_import_from_2(self):
+        s = """
+            from x.a import next
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_import_from_3(self):
+        s = """
+            from x import a, next, b
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_import_from_4(self):
+        s = """
+            from x.a import a, next, b
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_funcdef_1(self):
+        s = """
+            def next(a):
+                pass
+            
+            class A:
+                def next(self, a, b):
+                    pass
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_shadowing_funcdef_2(self):
+        b = """
+            def next(a):
+                pass
+            
+            class A:
+                def next(self):
+                    pass
+                    
+            it.next()
+            """
+        a = """
+            def next(a):
+                pass
+            
+            class A:
+                def __next__(self):
+                    pass
+                    
+            it.__next__()
+            """
+        self.warns(b, a, "Calls to builtin next() possibly shadowed")
+    
+    def test_shadowing_global_1(self):
+        s = """
+            def f():
+                global next
+                next = 5
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+    
+    def test_shadowing_global_2(self):
+        s = """
+            def f():
+                global a, next, b
+                next = 5
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+    
+    def test_shadowing_for_simple(self):
+        s = """
+            for next in it():
+                pass
+        
+            b = 5
+            c = 6
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+    
+    def test_shadowing_for_tuple_1(self):
+        s = """
+            for next, b in it():
+                pass
+        
+            b = 5
+            c = 6
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+    
+    def test_shadowing_for_tuple_2(self):
+        s = """
+            for a, (next, c), b in it():
+                pass
+        
+            b = 5
+            c = 6
+            """
+        self.warns(s, s, "Calls to builtin next() possibly shadowed")
+        
+    def test_noncall_access_1(self):
+        b = """gnext = g.next"""
+        a = """gnext = g.__next__"""
+        self.check(b, a)
+        
+    def test_noncall_access_2(self):
+        b = """f(g.next + 5)"""
+        a = """f(g.__next__ + 5)"""
+        self.check(b, a)
+        
+    def test_noncall_access_3(self):
+        b = """f(g().next + 5)"""
+        a = """f(g().__next__ + 5)"""
+        self.check(b, a)
 
 
 if __name__ == "__main__":


More information about the Python-checkins mailing list