[Python-checkins] r53677 - in sandbox/trunk/2to3: example.py fixer_tests.py fixes/basefix.py fixes/fix_apply.py fixes/fix_except.py fixes/fix_exec.py fixes/fix_has_key.py fixes/fix_intern.py fixes/fix_long.py fixes/fix_print.py fixes/fix_raise.py fixes/fix_repr.py fixes/fix_sysexcinfo.py fixes/fix_throw.py fixes/macros.py pytree.py refactor.py

guido.van.rossum python-checkins at python.org
Thu Feb 8 23:42:36 CET 2007


Author: guido.van.rossum
Date: Thu Feb  8 23:42:35 2007
New Revision: 53677

Added:
   sandbox/trunk/2to3/fixes/fix_sysexcinfo.py   (contents, props changed)
   sandbox/trunk/2to3/fixes/fix_throw.py   (contents, props changed)
   sandbox/trunk/2to3/fixes/macros.py   (contents, props changed)
Modified:
   sandbox/trunk/2to3/example.py
   sandbox/trunk/2to3/fixer_tests.py
   sandbox/trunk/2to3/fixes/basefix.py
   sandbox/trunk/2to3/fixes/fix_apply.py
   sandbox/trunk/2to3/fixes/fix_except.py
   sandbox/trunk/2to3/fixes/fix_exec.py
   sandbox/trunk/2to3/fixes/fix_has_key.py
   sandbox/trunk/2to3/fixes/fix_intern.py
   sandbox/trunk/2to3/fixes/fix_long.py
   sandbox/trunk/2to3/fixes/fix_print.py
   sandbox/trunk/2to3/fixes/fix_raise.py
   sandbox/trunk/2to3/fixes/fix_repr.py
   sandbox/trunk/2to3/pytree.py
   sandbox/trunk/2to3/refactor.py
Log:
Lots of new stuff by Collin Winter:

- macros.patch is a new version of the earlier macros.patch.
- better_raise.patch allows 2to3 to convert three-argument raise
  statements. This depends on macros.patch.
- fix_throw.patch converts two- and three-argument generator.throw()
  calls much like fix_raise does.
- fix_sysexcinfo.patch adds a fixer that warns on usage of
  sys.exc_{info,type,value,traceback}.

Right now I see some test failures; I'll ask Collin to address these.




Modified: sandbox/trunk/2to3/example.py
==============================================================================
--- sandbox/trunk/2to3/example.py	(original)
+++ sandbox/trunk/2to3/example.py	Thu Feb  8 23:42:35 2007
@@ -165,7 +165,7 @@
     except Exception, (f, e):
         pass
     except ImportError, e:
-        pass
+        print e.args
     #
     try:
         pass
@@ -208,7 +208,7 @@
         pass
     except (Exception, SystemExit):
         pass
-        
+
 def raise_examples():
     raise Exception, 5
     #
@@ -223,10 +223,14 @@
     raise Exception(5, 6)
     #
     # These should produce a warning
+    # TODO: convert "raise E, V, T" to
+    #  "e = E(V); e.__traceback__ = T; raise e;"
     #
     raise Exception, 5, 6
     #
     raise Exception,5,6
+    #
+    raise Exception, (5, 6, 7), 6
 
 def long_examples():
     x = long(x)
@@ -238,6 +242,6 @@
     a = 12
     b = 0x12
     c = 3.14
-    
-    
+
+
 # This is the last line.

Modified: sandbox/trunk/2to3/fixer_tests.py
==============================================================================
--- sandbox/trunk/2to3/fixer_tests.py	(original)
+++ sandbox/trunk/2to3/fixer_tests.py	Thu Feb  8 23:42:35 2007
@@ -11,14 +11,18 @@
 import refactor
 
 
+# We wrap the RefactoringTool's fixer objects so we can intercept
+#  the call to set_filename() and so modify the fixers' logging objects.
+# This allows us to make sure that certain code chunks produce certain
+#  warnings.
 class Fixer(object):
     def __init__(self, fixer, handler):
         self.fixer = fixer
         self.handler = handler
-        
+
     def __getattr__(self, attr):
         return getattr(self.fixer, attr)
-        
+
     def set_filename(self, filename):
         self.fixer.set_filename(filename)
         self.fixer.logger.addHandler(self.handler)
@@ -27,30 +31,29 @@
     def __init__(self, **kwargs):
         for k, v in kwargs.items():
             setattr(self, k, v)
-            
+
         self.verbose = False
 
 class FixerTestCase(unittest.TestCase):
     def setUp(self):
         options = Options(fix=[self.fixer])
         self.refactor = refactor.RefactoringTool(options)
-        
+
         self.logging_stream = StringIO()
         sh = logging.StreamHandler(self.logging_stream)
         sh.setFormatter(logging.Formatter("%(message)s"))
         self.refactor.fixers = [Fixer(f, sh) for f in self.refactor.fixers]
-        
+
     def check(self, before, after):
         before += "\n"
         after += "\n"
         refactored = self.refactor_stream("<string>", StringIO(before))
         self.failUnlessEqual(after, refactored)
-        
+
     def warns(self, before, after, message):
         self.check(before, after)
-        
-        self.logging_stream.seek(0)
-        self.failUnless(message in '\n'.join(self.logging_stream))
+
+        self.failUnless(message in self.logging_stream.getvalue())
 
     def refactor_stream(self, stream_name, stream):
         try:
@@ -70,158 +73,158 @@
     def test_1(self):
         b = """if x <> y:
             pass"""
-        
+
         a = """if x != y:
             pass"""
         self.check(b, a)
-            
+
     def test_2(self):
         b = """if x<>y:
             pass"""
-            
+
         a = """if x!=y:
             pass"""
         self.check(b, a)
-            
+
     def test_3(self):
         b = """if x<>y<>z:
             pass"""
-            
+
         a = """if x!=y!=z:
             pass"""
         self.check(b, a)
-            
+
 class Test_has_key(FixerTestCase):
     fixer = "has_key"
-    
+
     def test_1(self):
         b = """x = d.has_key("x") or d.has_key("y")"""
         a = """x = "x" in d or "y" in d"""
         self.check(b, a)
-        
+
     def test_2(self):
         b = """x = a.b.c.d.has_key("x") ** 3"""
         a = """x = ("x" in a.b.c.d) ** 3"""
         self.check(b, a)
-        
+
     def test_3(self):
         b = """x = a.b.has_key(1 + 2).__repr__()"""
         a = """x = (1 + 2 in a.b).__repr__()"""
         self.check(b, a)
-        
+
     def test_4(self):
         b = """x = a.b.has_key(1 + 2).__repr__() ** -3 ** 4"""
         a = """x = (1 + 2 in a.b).__repr__() ** -3 ** 4"""
         self.check(b, a)
-        
+
     def test_5(self):
         b = """x = a.has_key(f or g)"""
         a = """x = (f or g) in a"""
         self.check(b, a)
-        
+
     def test_6(self):
         b = """x = a + b.has_key(c)"""
         a = """x = a + (c in b)"""
         self.check(b, a)
-        
+
     def test_7(self):
         b = """x = a.has_key(lambda: 12)"""
         a = """x = (lambda: 12) in a"""
         self.check(b, a)
-        
+
     def test_8(self):
         b = """x = a.has_key(a for a in b)"""
         a = """x = (a for a in b) in a"""
         self.check(b, a)
-        
+
     def test_9(self):
         b = """if not a.has_key(b): pass"""
         a = """if b not in a: pass"""
         self.check(b, a)
-        
+
     def test_10(self):
         b = """if not a.has_key(b).__repr__(): pass"""
         a = """if not (b in a).__repr__(): pass"""
         self.check(b, a)
-        
+
     def test_11(self):
         b = """if not a.has_key(b) ** 2: pass"""
         a = """if not (b in a) ** 2: pass"""
         self.check(b, a)
-        
+
 class Test_apply(FixerTestCase):
     fixer = "apply"
-    
+
     def test_1(self):
         b = """x = apply(f, g + h)"""
         a = """x = f(*g + h)"""
         self.check(b, a)
-    
+
     def test_2(self):
         b = """y = apply(f, g, h)"""
         a = """y = f(*g, **h)"""
         self.check(b, a)
-    
+
     def test_3(self):
         b = """z = apply(fs[0], g or h, h or g)"""
         a = """z = fs[0](*g or h, **h or g)"""
         self.check(b, a)
-    
+
     def test_4(self):
         b = """apply(f, (x, y) + t)"""
         a = """f(*(x, y) + t)"""
         self.check(b, a)
-    
+
     def test_5(self):
         b = """apply(f, args,)"""
         a = """f(*args)"""
         self.check(b, a)
-    
+
     def test_6(self):
         b = """apply(f, args, kwds,)"""
         a = """f(*args, **kwds)"""
         self.check(b, a)
-        
+
     # Test that complex functions are parenthesized
-    
+
     def test_7(self):
         b = """x = apply(f+g, args)"""
         a = """x = (f+g)(*args)"""
         self.check(b, a)
-    
+
     def test_8(self):
         b = """x = apply(f*g, args)"""
         a = """x = (f*g)(*args)"""
         self.check(b, a)
-    
+
     def test_9(self):
         b = """x = apply(f**g, args)"""
         a = """x = (f**g)(*args)"""
         self.check(b, a)
-        
+
     # But dotted names etc. not
-    
+
     def test_10(self):
         b = """x = apply(f.g, args)"""
         a = """x = f.g(*args)"""
         self.check(b, a)
-    
+
     def test_11(self):
         b = """x = apply(f[x], args)"""
         a = """x = f[x](*args)"""
         self.check(b, a)
-    
+
     def test_12(self):
         b = """x = apply(f(), args)"""
         a = """x = f()(*args)"""
         self.check(b, a)
-    
+
     # Extreme case
     def test_13(self):
         b = """x = apply(a.b.c.d.e.f, args, kwds)"""
         a = """x = a.b.c.d.e.f(*args, **kwds)"""
         self.check(b, a)
-    
+
     # XXX Comments in weird places still get lost
     def test_14(self):
         b = """apply(   # foo
@@ -229,49 +232,49 @@
           args)"""
         a = """f(*args)"""
         self.check(b, a)
-        
+
     # These should *not* be touched
-    
+
     def test_15(self):
         b = """apply()"""
         a = """apply()"""
         self.check(b, a)
-    
+
     def test_16(self):
         b = """apply(f)"""
         a = """apply(f)"""
         self.check(b, a)
-    
+
     def test_17(self):
         b = """apply(f,)"""
         a = """apply(f,)"""
         self.check(b, a)
-    
+
     def test_18(self):
         b = """apply(f, args, kwds, extras)"""
         a = """apply(f, args, kwds, extras)"""
         self.check(b, a)
-    
+
     def test_19(self):
         b = """apply(f, *args, **kwds)"""
         a = """apply(f, *args, **kwds)"""
         self.check(b, a)
-    
+
     def test_20(self):
         b = """apply(f, *args)"""
         a = """apply(f, *args)"""
         self.check(b, a)
-    
+
     def test_21(self):
         b = """apply(func=f, args=args, kwds=kwds)"""
         a = """apply(func=f, args=args, kwds=kwds)"""
         self.check(b, a)
-    
+
     def test_22(self):
         b = """apply(f, args=args, kwds=kwds)"""
         a = """apply(f, args=args, kwds=kwds)"""
         self.check(b, a)
-    
+
     def test_23(self):
         b = """apply(f, args, kwds=kwds)"""
         a = """apply(f, args, kwds=kwds)"""
@@ -280,106 +283,106 @@
 
 class Test_intern(FixerTestCase):
     fixer = "intern"
-    
+
     def test_1(self):
         b = """x = intern(a)"""
         a = """x = sys.intern(a)"""
         self.check(b, a)
-    
+
     def test_2(self):
         b = """y = intern("b" # test
               )"""
         a = """y = sys.intern("b" # test
               )"""
         self.check(b, a)
-    
+
     def test_3(self):
         b = """z = intern(a+b+c.d,)"""
         a = """z = sys.intern(a+b+c.d,)"""
         self.check(b, a)
-    
+
     def test_4(self):
         b = """intern("y%s" % 5).replace("y", "")"""
         a = """sys.intern("y%s" % 5).replace("y", "")"""
         self.check(b, a)
-        
+
     # These should not be refactored
-    
+
     def test_5(self):
         b = """intern(a=1)"""
         a = """intern(a=1)"""
         self.check(b, a)
-    
+
     def test_6(self):
         b = """intern(f, g)"""
         a = """intern(f, g)"""
         self.check(b, a)
-    
+
     def test_7(self):
         b = """intern(*h)"""
         a = """intern(*h)"""
         self.check(b, a)
-    
+
     def test_8(self):
         b = """intern(**i)"""
         a = """intern(**i)"""
         self.check(b, a)
-        
+
 class Test_print(FixerTestCase):
     fixer = "print"
-    
+
     def test_1(self):
         b = """print 1, 1+1, 1+1+1"""
         a = """Print(1, 1+1, 1+1+1)"""
         self.check(b, a)
-    
+
     def test_2(self):
         b = """print 1, 2"""
         a = """Print(1, 2)"""
         self.check(b, a)
-    
+
     def test_3(self):
         b = """print"""
         a = """Print()"""
         self.check(b, a)
-        
+
     # trailing commas
-    
+
     def test_4(self):
         b = """print 1, 2, 3,"""
         a = """Print(1, 2, 3, end=' ')"""
         self.check(b, a)
-    
+
     def test_5(self):
         b = """print 1, 2,"""
         a = """Print(1, 2, end=' ')"""
         self.check(b, a)
-    
+
     def test_6(self):
         b = """print 1,"""
         a = """Print(1, end=' ')"""
         self.check(b, a)
-    
+
     # >> stuff
-    
+
     # no trailing comma
     def test_7(self):
         b = """print >>sys.stderr, 1, 2, 3"""
         a = """Print(1, 2, 3, file=sys.stderr)"""
         self.check(b, a)
-    
+
     # trailing comma
     def test_8(self):
         b = """print >>sys.stderr, 1, 2,"""
         a = """Print(1, 2, end=' ', file=sys.stderr)"""
         self.check(b, a)
-    
+
     # no trailing comma
     def test_9(self):
         b = """print >>sys.stderr, 1+1"""
         a = """Print(1+1, file=sys.stderr)"""
         self.check(b, a)
-    
+
     # spaces before sys.stderr
     def test_10(self):
         b = """print >>  sys.stderr"""
@@ -389,49 +392,49 @@
 
 class Test_exec(FixerTestCase):
     fixer = "exec"
-    
+
     def test_1(self):
         b = """exec code"""
         a = """exec(code)"""
         self.check(b, a)
-    
+
     def test_2(self):
         b = """exec code in ns"""
         a = """exec(code, ns)"""
         self.check(b, a)
-    
+
     def test_3(self):
         b = """exec code in ns1, ns2"""
         a = """exec(code, ns1, ns2)"""
         self.check(b, a)
-    
+
     def test_4(self):
         b = """exec (a.b()) in ns"""
         a = """exec((a.b()), ns)"""
         self.check(b, a)
-    
+
     def test_5(self):
         b = """exec a.b() + c in ns"""
         a = """exec(a.b() + c, ns)"""
         self.check(b, a)
-    
+
     # These should not be touched
-    
+
     def test_6(self):
         b = """exec(code)"""
         a = """exec(code)"""
         self.check(b, a)
-    
+
     def test_7(self):
         b = """exec (code)"""
         a = """exec (code)"""
         self.check(b, a)
-    
+
     def test_8(self):
         b = """exec(code, ns)"""
         a = """exec(code, ns)"""
         self.check(b, a)
-    
+
     def test_9(self):
         b = """exec(code, ns1, ns2)"""
         a = """exec(code, ns1, ns2)"""
@@ -440,40 +443,40 @@
 
 class Test_repr(FixerTestCase):
     fixer = "repr"
-    
+
     def test_1(self):
         b = """x = `1 + 2`"""
         a = """x = repr(1 + 2)"""
         self.check(b, a)
-    
+
     def test_2(self):
         b = """y = `x`"""
         a = """y = repr(x)"""
         self.check(b, a)
-    
+
     def test_3(self):
         b = """z = `y`.__repr__()"""
         a = """z = repr(y).__repr__()"""
         self.check(b, a)
-    
+
     def test_4(self):
         b = """x = `1, 2, 3`"""
         a = """x = repr((1, 2, 3))"""
         self.check(b, a)
-    
+
     def test_5(self):
         b = """x = `1 + `2``"""
         a = """x = repr(1 + repr(2))"""
         self.check(b, a)
-    
+
     def test_6(self):
         b = """x = `1, 2 + `3, 4``"""
         a = """x = repr((1, 2 + repr((3, 4))))"""
         self.check(b, a)
-        
+
 class Test_except():
     fixer = "except"
-    
+
     def test_1(self):
         b = """
             try:
@@ -482,7 +485,7 @@
                 pass
             except ImportError, e:
                 pass"""
-        
+
         a = """
             try:
                 pass
@@ -491,42 +494,42 @@
             except ImportError as e:
                 pass"""
         self.check(b, a)
-    
+
     def test_2(self):
         b = """
             try:
                 pass
             except (RuntimeError, ImportError), e:
                 pass"""
-        
+
         a = """
             try:
                 pass
             except (RuntimeError, ImportError) as e:
                 pass"""
         self.check(b, a)
-    
+
     def test_3(self):
         b = """
             try:
                 pass
             except Exception, (a, b):
                 pass"""
-        
+
         a = """
             try:
                 pass
             except Exception as (a, b):
                 pass"""
         self.check(b, a)
-    
+
     def test_4(self):
         b = """
             try:
                 pass
             except Exception, d[5]:
                 pass"""
-        
+
         a = """
             try:
                 pass
@@ -534,14 +537,14 @@
                 d[5] = xxx_todo_changeme
                 pass"""
         self.check(b, a)
-    
+
     def test_5(self):
         b = """
             try:
                 pass
             except Exception, a.foo:
                 pass"""
-        
+
         a = """
             try:
                 pass
@@ -549,14 +552,14 @@
                 a.foo = xxx_todo_changeme1
                 pass"""
         self.check(b, a)
-    
+
     def test_6(self):
         b = """
             try:
                 pass
             except Exception, a().foo:
                 pass"""
-                
+
         a = """
             try:
                 pass
@@ -564,9 +567,9 @@
                 a().foo = xxx_todo_changeme2
                 pass"""
         self.check(b, a)
-    
+
     # These should not be touched:
-    
+
     def test_7(self):
         b = """
             try:
@@ -580,7 +583,7 @@
             except:
                 pass"""
         self.check(b, a)
-    
+
     def test_8(self):
         b = """
             try:
@@ -594,7 +597,7 @@
             except Exception:
                 pass"""
         self.check(b, a)
-    
+
     def test_9(self):
         b = """
             try:
@@ -609,94 +612,303 @@
                 pass"""
         self.check(b, a)
 
-                
+
 class Test_raise(FixerTestCase):
     fixer = "raise"
-    
+
     def test_1(self):
         b = """raise Exception, 5"""
         a = """raise Exception(5)"""
         self.check(b, a)
-    
+
     def test_2(self):
         b = """raise Exception,5"""
         a = """raise Exception(5)"""
         self.check(b, a)
-    
+
     def test_3(self):
         b = """raise Exception, (5, 6, 7)"""
         a = """raise Exception((5, 6, 7))"""
         self.check(b, a)
-        
+
     # These should not be touched
-    
+
     def test_4(self):
         b = """raise Exception"""
         a = """raise Exception"""
         self.check(b, a)
-    
+
     def test_5(self):
         b = """raise Exception(5, 6)"""
         a = """raise Exception(5, 6)"""
         self.check(b, a)
-        
-    # These should produce a warning
-    
+
+    # These should result in traceback-assignment
+
+    def test_tb_1(self):
+        b = """def foo():
+                    raise Exception, 5, 6"""
+        a = """def foo():
+                    xxx_todo_changeme = Exception(5)
+                    xxx_todo_changeme.__traceback__ = 6
+                    raise xxx_todo_changeme"""
+        self.check(b, a)
+
+    def test_tb_2(self):
+        b = """def foo():
+                    a = 5
+                    raise Exception, 5, 6
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    xxx_todo_changeme1 = Exception(5)
+                    xxx_todo_changeme1.__traceback__ = 6
+                    raise xxx_todo_changeme1
+                    b = 6"""
+        self.check(b, a)
+
+    def test_tb_3(self):
+        b = """def foo():
+                    raise Exception,5,6"""
+        a = """def foo():
+                    xxx_todo_changeme2 = Exception(5)
+                    xxx_todo_changeme2.__traceback__ = 6
+                    raise xxx_todo_changeme2"""
+        self.check(b, a)
+
+    def test_tb_4(self):
+        b = """def foo():
+                    a = 5
+                    raise Exception,5,6
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    xxx_todo_changeme3 = Exception(5)
+                    xxx_todo_changeme3.__traceback__ = 6
+                    raise xxx_todo_changeme3
+                    b = 6"""
+        self.check(b, a)
+
+    def test_tb_5(self):
+        b = """def foo():
+                    raise Exception, (5, 6, 7), 6"""
+        a = """def foo():
+                    xxx_todo_changeme4 = Exception((5, 6, 7))
+                    xxx_todo_changeme4.__traceback__ = 6
+                    raise xxx_todo_changeme4"""
+        self.check(b, a)
+
+    def test_tb_6(self):
+        b = """def foo():
+                    a = 5
+                    raise Exception, (5, 6, 7), 6
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    xxx_todo_changeme5 = Exception((5, 6, 7))
+                    xxx_todo_changeme5.__traceback__ = 6
+                    raise xxx_todo_changeme5
+                    b = 6"""
+        self.check(b, a)
+
+
+class Test_throw(FixerTestCase):
+    fixer = "throw"
+
+    def test_1(self):
+        b = """g.throw(Exception, 5)"""
+        a = """g.throw(Exception(5))"""
+        self.check(b, a)
+
+    def test_2(self):
+        b = """g.throw(Exception,5)"""
+        a = """g.throw(Exception(5))"""
+        self.check(b, a)
+
+    def test_3(self):
+        b = """g.throw(Exception, (5, 6, 7))"""
+        a = """g.throw(Exception((5, 6, 7)))"""
+        self.check(b, a)
+
+    def test_4(self):
+        b = """5 + g.throw(Exception, 5)"""
+        a = """5 + g.throw(Exception(5))"""
+        self.check(b, a)
+
+    # These should not be touched
+
+    def test_5(self):
+        b = """g.throw(Exception)"""
+        a = """g.throw(Exception)"""
+        self.check(b, a)
+
     def test_6(self):
-        b = """raise Exception, 5, 6"""
-        a = """raise Exception, 5, 6"""
-        self.warns(b, a, "raise will not support providing a traceback")
-    
+        b = """g.throw(Exception(5, 6))"""
+        a = """g.throw(Exception(5, 6))"""
+        self.check(b, a)
+
     def test_7(self):
-        b = """raise Exception,5,6"""
-        a = """raise Exception,5,6"""
-        self.warns(b, a, "raise will not support providing a traceback")
-        
+        b = """5 + g.throw(Exception(5, 6))"""
+        a = """5 + g.throw(Exception(5, 6))"""
+        self.check(b, a)
+
+    # These should result in traceback-assignment
+
+    def test_tb_1(self):
+        b = """def foo():
+                    g.throw(Exception, 5, 6)"""
+        a = """def foo():
+                    xxx_todo_changeme6 = Exception(5)
+                    xxx_todo_changeme6.__traceback__ = 6
+                    g.throw(xxx_todo_changeme6)"""
+        self.check(b, a)
+
+    def test_tb_2(self):
+        b = """def foo():
+                    a = 5
+                    g.throw(Exception, 5, 6)
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    xxx_todo_changeme7 = Exception(5)
+                    xxx_todo_changeme7.__traceback__ = 6
+                    g.throw(xxx_todo_changeme7)
+                    b = 6"""
+        self.check(b, a)
+
+    def test_tb_3(self):
+        b = """def foo():
+                    g.throw(Exception,5,6)"""
+        a = """def foo():
+                    xxx_todo_changeme8 = Exception(5)
+                    xxx_todo_changeme8.__traceback__ = 6
+                    g.throw(xxx_todo_changeme8)"""
+        self.check(b, a)
+
+    def test_tb_4(self):
+        b = """def foo():
+                    a = 5
+                    g.throw(Exception,5,6)
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    xxx_todo_changeme9 = Exception(5)
+                    xxx_todo_changeme9.__traceback__ = 6
+                    g.throw(xxx_todo_changeme9)
+                    b = 6"""
+        self.check(b, a)
+
+    def test_tb_5(self):
+        b = """def foo():
+                    g.throw(Exception, (5, 6, 7), 6)"""
+        a = """def foo():
+                    xxx_todo_changeme10 = Exception((5, 6, 7))
+                    xxx_todo_changeme10.__traceback__ = 6
+                    g.throw(xxx_todo_changeme10)"""
+        self.check(b, a)
+
+    def test_tb_6(self):
+        b = """def foo():
+                    a = 5
+                    g.throw(Exception, (5, 6, 7), 6)
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    xxx_todo_changeme11 = Exception((5, 6, 7))
+                    xxx_todo_changeme11.__traceback__ = 6
+                    g.throw(xxx_todo_changeme11)
+                    b = 6"""
+        self.check(b, a)
+
+    def test_tb_7(self):
+        b = """def foo():
+                    a + g.throw(Exception, 5, 6)"""
+        a = """def foo():
+                    xxx_todo_changeme12 = Exception(5)
+                    xxx_todo_changeme12.__traceback__ = 6
+                    a + g.throw(xxx_todo_changeme12)"""
+        self.check(b, a)
+
+    def test_tb_8(self):
+        b = """def foo():
+                    a = 5
+                    a + g.throw(Exception, 5, 6)
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    xxx_todo_changeme13 = Exception(5)
+                    xxx_todo_changeme13.__traceback__ = 6
+                    a + g.throw(xxx_todo_changeme13)
+                    b = 6"""
+        self.check(b, a)
+
+
 class Test_long(FixerTestCase):
     fixer = "long"
-    
+
     def test_1(self):
         b = """x = long(x)"""
         a = """x = int(x)"""
         self.check(b, a)
-    
+
     def test_2(self):
         b = """y = isinstance(x, long)"""
         a = """y = isinstance(x, int)"""
         self.check(b, a)
-    
+
     def test_3(self):
         b = """z = type(x) in (int, long)"""
         a = """z = type(x) in (int, int)"""
         self.check(b, a)
-    
+
     def test_4(self):
         b = """a = 12L"""
         a = """a = 12"""
         self.check(b, a)
-    
+
     def test_5(self):
         b = """b = 0x12l"""
         a = """b = 0x12"""
         self.check(b, a)
-        
+
     # These should not be touched
-    
+
     def test_6(self):
         b = """a = 12"""
         a = """a = 12"""
         self.check(b, a)
-    
+
     def test_7(self):
         b = """b = 0x12"""
         a = """b = 0x12"""
         self.check(b, a)
-    
+
     def test_8(self):
         b = """c = 3.14"""
         a = """c = 3.14"""
         self.check(b, a)
-        
+
+
+class Test_sysexcinfo(FixerTestCase):
+    fixer = "sysexcinfo"
+
+    def test_1(self):
+        s = """sys.exc_info()"""
+        self.warns(s, s, "This function is going away")
+
+    def test_2(self):
+        s = """if sys.exc_info()[1] == 1:
+                    pass"""
+
+        self.warns(s, s, "This function is going away")
+
+    def test_3(self):
+        s = """f = sys.exc_info"""
+        self.warns(s, s, "This function is going away")
+
+    def test_4(self):
+        s = """f = sys.exc_type + ":" + sys.exc_value"""
+        self.warns(s, s, "This attribute is going away")
 
 if __name__ == "__main__":
     import sys

Modified: sandbox/trunk/2to3/fixes/basefix.py
==============================================================================
--- sandbox/trunk/2to3/fixes/basefix.py	(original)
+++ sandbox/trunk/2to3/fixes/basefix.py	Thu Feb  8 23:42:35 2007
@@ -86,6 +86,10 @@
         return pygram.parenthesize(node)
 
     def new_name(self, template="xxx_todo_changeme"):
+        """Return a string suitable for use as an identifier
+
+        The new name is guaranteed not to conflict with other identifiers.
+        """
         name = template
         while name in self.used_names:
             name = template + str(numbers.next())
@@ -93,6 +97,12 @@
         return name
 
     def cannot_convert(self, node, reason=None):
+        """Warn the user that a given chunk of code is not valid Python 3,
+        but that it cannot be converted automatically.
+
+        First argument is the top-level node for the code in question.
+        Optional second argument is why it can't be converted.
+        """
         lineno = node.get_lineno()
         for_output = node.clone()
         for_output.set_prefix("")

Modified: sandbox/trunk/2to3/fixes/fix_apply.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_apply.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_apply.py	Thu Feb  8 23:42:35 2007
@@ -9,7 +9,7 @@
 # Local imports
 import pytree
 from fixes import basefix
-
+from fixes.macros import Call, Comma
 
 class FixApply(basefix.BaseFix):
 
@@ -49,17 +49,13 @@
             kwds.set_prefix("")
         l_newargs = [pytree.Leaf(token.STAR, "*"), args]
         if kwds is not None:
-            l_newargs.extend([pytree.Leaf(token.COMMA, ","),
+            l_newargs.extend([Comma(),
                               pytree.Leaf(token.DOUBLESTAR, "**"),
                               kwds])
             l_newargs[-2].set_prefix(" ") # that's the ** token
         # XXX Sometimes we could be cleverer, e.g. apply(f, (x, y) + t)
         # can be translated into f(x, y, *t) instead of f(*(x, y) + t)
-        new = pytree.Node(syms.power,
-                          (func,
-                           pytree.Node(syms.trailer,
-                                       (pytree.Leaf(token.LPAR, "("),
-                                        pytree.Node(syms.arglist, l_newargs),
-                                        pytree.Leaf(token.RPAR, ")")))))
+        #new = pytree.Node(syms.power, (func, ArgList(l_newargs)))
+        new = Call(func, l_newargs)
         new.set_prefix(prefix)
         return new

Modified: sandbox/trunk/2to3/fixes/fix_except.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_except.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_except.py	Thu Feb  8 23:42:35 2007
@@ -7,6 +7,7 @@
 # Local imports
 import pytree
 from fixes import basefix
+from fixes.macros import Assign, Attr, Name
 
 def find_excepts(nodes):
     for i in range(len(nodes)):
@@ -19,9 +20,6 @@
 as_leaf = pytree.Leaf(token.NAME, "as")
 as_leaf.set_prefix(" ")
 
-ass_leaf = pytree.Leaf(token.EQUAL, "=")
-ass_leaf.set_prefix(" ")
-
 tuple_reason = "exception unpacking is going away"
 
 class FixExcept(basefix.BaseFix):
@@ -32,42 +30,44 @@
                                                       ['finally' ':' suite]
 	                       | 'finally' ':' suite) >
     """
-    
+
     def transform(self, node):
         syms = self.syms
         results = self.match(node)
         assert results
-        
+
         try_cleanup = [ch.clone() for ch in results['cleanup']]
         for except_clause, e_suite in find_excepts(try_cleanup):
             if len(except_clause.children) == 4:
                 (E, comma, N) = except_clause.children[1:4]
                 comma.replace(as_leaf.clone())
-                if str(N).strip()[0] == '(':
-                    # We're dealing with a tuple
-                    self.cannot_convert(N, tuple_reason)
-                elif N.type != token.NAME:
+                if N.type != token.NAME:
                     # Generate a new N for the except clause
-                    new_N = pytree.Leaf(token.NAME, self.new_name())
+                    new_N = Name(self.new_name())
                     new_N.set_prefix(" ")
                     target = N.clone()
                     target.set_prefix("")
                     N.replace(new_N)
-                    
+                    new_N = new_N.clone()
+
                     # Insert "old_N = new_N" as the first statement in
-                    #  the except body
+                    #  the except body. This loop skips leading whitespace
+                    #  and indents
                     suite_stmts = list(e_suite.children)
                     for i, stmt in enumerate(suite_stmts):
                         if isinstance(stmt, pytree.Node):
                             break
-                    assign = pytree.Node(syms.atom,
-                                         [target,
-                                          ass_leaf.clone(),
-                                          new_N.clone()])
-                    
-                    assign.parent = e_suite                      
+
+                    # The assignment is different if old_N is a tuple
+                    # In that case, the assignment is old_N = new_N.message
+                    if str(N).strip()[0] == '(':
+                        assign = Assign(target, Attr(new_N, Name('message')))
+                    else:
+                        assign = Assign(target, new_N)
+
+                    assign.parent = e_suite
                     suite_stmts = suite_stmts[:i] + [assign] + suite_stmts
                     e_suite.children = tuple(suite_stmts)
-        
+
         children = [c.clone() for c in node.children[:3]] + try_cleanup
         return pytree.Node(node.type, children)

Modified: sandbox/trunk/2to3/fixes/fix_exec.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_exec.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_exec.py	Thu Feb  8 23:42:35 2007
@@ -9,6 +9,7 @@
 # Local imports
 import pytree
 from fixes import basefix
+from fixes.macros import Comma, Name, Call
 
 
 class FixExec(basefix.BaseFix):
@@ -29,14 +30,10 @@
         args = [a.clone()]
         args[0].set_prefix("")
         if b is not None:
-            args.extend([pytree.Leaf(token.COMMA, ","), b.clone()])
+            args.extend([Comma(), b.clone()])
         if c is not None:
-            args.extend([pytree.Leaf(token.COMMA, ","), c.clone()])
-        new = pytree.Node(syms.factor,
-                          [pytree.Leaf(token.NAME, "exec"),
-                           pytree.Node(syms.trailer,
-                                       [pytree.Leaf(token.LPAR, "("),
-                                        pytree.Node(syms.arglist, args),
-                                        pytree.Leaf(token.RPAR, ")")])])
+            args.extend([Comma(), c.clone()])
+
+        new = Call(Name("exec"), args)
         new.set_prefix(node.get_prefix())
         return new

Modified: sandbox/trunk/2to3/fixes/fix_has_key.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_has_key.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_has_key.py	Thu Feb  8 23:42:35 2007
@@ -9,6 +9,7 @@
 # Local imports
 import pytree
 from fixes import basefix
+from fixes.macros import Name
 
 
 class FixHasKey(basefix.BaseFix):
@@ -68,10 +69,10 @@
         else:
             before = pytree.Node(syms.power, before)
         before.set_prefix(" ")
-        n_op = pytree.Leaf(token.NAME, "in")
+        n_op = Name("in")
         n_op.set_prefix(" ")
         if negation:
-            n_not = pytree.Leaf(token.NAME, "not")
+            n_not = Name("not")
             n_not.set_prefix(" ")
             n_op = pytree.Node(syms.comp_op, (n_not, n_op))
         new = pytree.Node(syms.comparison, (arg, n_op, before))

Modified: sandbox/trunk/2to3/fixes/fix_intern.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_intern.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_intern.py	Thu Feb  8 23:42:35 2007
@@ -9,6 +9,7 @@
 # Local imports
 import pytree
 from fixes import basefix
+from fixes.macros import Name, Attr
 
 
 class FixIntern(basefix.BaseFix):
@@ -36,14 +37,11 @@
         if after:
             after = tuple(n.clone() for n in after)
         new = pytree.Node(syms.power,
-                          (pytree.Leaf(token.NAME, "sys"),
-                           pytree.Node(syms.trailer,
-                                       [pytree.Leaf(token.DOT, "."),
-                                        pytree.Leaf(token.NAME, "intern")]),
-                           pytree.Node(syms.trailer,
+                          Attr(Name("sys"), Name("intern")) +
+                          (pytree.Node(syms.trailer,
                                        [results["lpar"].clone(),
                                         newarglist,
-                                        results["rpar"].clone()]))
+                                        results["rpar"].clone()]),)
                           + after)
         new.set_prefix(node.get_prefix())
         return new

Modified: sandbox/trunk/2to3/fixes/fix_long.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_long.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_long.py	Thu Feb  8 23:42:35 2007
@@ -12,6 +12,7 @@
 # Local imports
 import pytree
 from fixes import basefix
+from fixes.macros import Name
 
 
 class FixLong(basefix.BaseFix):
@@ -20,8 +21,8 @@
     (long_type = 'long' | number = NUMBER)
     """
 
-    static_long = pytree.Leaf(token.NAME, "long")
-    static_int = pytree.Leaf(token.NAME, "int")
+    static_long = Name("long")
+    static_int = Name("int")
 
     def transform(self, node):
         results = self.match(node)

Modified: sandbox/trunk/2to3/fixes/fix_print.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_print.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_print.py	Thu Feb  8 23:42:35 2007
@@ -16,6 +16,7 @@
 # Local imports
 import pytree
 from fixes import basefix
+from fixes.macros import Name, Call, Comma
 
 
 class FixPrint(basefix.BaseFix):
@@ -36,19 +37,15 @@
         results = self.match(node)
         assert results
 
-        if node == pytree.Leaf(token.NAME, "print"):
+        if node == Name("print"):
             # Special-case print all by itself
-            new = pytree.Node(syms.power,
-                              (pytree.Leaf(token.NAME, "Print"),
-                               pytree.Node(syms.trailer,
-                                           (pytree.Leaf(token.LPAR, "("),
-                                            pytree.Leaf(token.RPAR, ")")))))
+            new = Call(Name("Print"), [])
             new.set_prefix(node.get_prefix())
             return new
-        assert node.children[0] == pytree.Leaf(token.NAME, "print")
+        assert node.children[0] == Name("print")
         args = node.children[1:]
         sep = end = file = None
-        if args and args[-1] == pytree.Leaf(token.COMMA, ","):
+        if args and args[-1] == Comma():
             args = args[:-1]
             end = " "
         if args and args[0] == pytree.Leaf(token.RIGHTSHIFT, ">>"):
@@ -56,7 +53,6 @@
             file = args[1].clone()
             args = args[3:] # Strip a possible comma after the file expression
         # Now synthesize a Print(args, sep=..., end=..., file=...) node.
-        n_print = pytree.Leaf(token.NAME, "Print") # XXX -> "print"
         l_args = [arg.clone() for arg in args]
         if l_args:
             l_args[0].set_prefix("")
@@ -69,15 +65,7 @@
                                pytree.Leaf(token.STRING, repr(end)))
             if file is not None:
                 self.add_kwarg(l_args, "file", file)
-        if l_args:
-            n_arglist = pytree.Node(syms.arglist, l_args)
-        else:
-            n_arglist = None
-        l_args = [pytree.Leaf(token.LPAR, "("), pytree.Leaf(token.RPAR, ")")]
-        if n_arglist:
-            l_args.insert(1, n_arglist)
-        n_trailer = pytree.Node(syms.trailer, l_args)
-        n_stmt = pytree.Node(syms.power, (n_print, n_trailer))
+        n_stmt = Call(Name("Print"), l_args)
         n_stmt.set_prefix(node.get_prefix())
         return n_stmt
 
@@ -85,10 +73,10 @@
         # XXX All this prefix-setting may lose comments (though rarely)
         n_expr.set_prefix("")
         n_argument = pytree.Node(self.syms.argument,
-                                 (pytree.Leaf(token.NAME, s_kwd),
+                                 (Name(s_kwd),
                                   pytree.Leaf(token.EQUAL, "="),
                                   n_expr))
         if l_nodes:
-            l_nodes.append(pytree.Leaf(token.COMMA, ","))
+            l_nodes.append(Comma())
             n_argument.set_prefix(" ")
         l_nodes.append(n_argument)

Modified: sandbox/trunk/2to3/fixes/fix_raise.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_raise.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_raise.py	Thu Feb  8 23:42:35 2007
@@ -1,4 +1,4 @@
-"""Fixer for 'raise E, a1, a2, ...'"""
+"""Fixer for 'raise E, V, T'"""
 # Author: Collin Winter
 
 # Python imports
@@ -7,35 +7,57 @@
 # Local imports
 import pytree
 from fixes import basefix
-
-reason = "Python 3's raise will not support providing a traceback"
+from fixes.macros import Name, Call, Assign, Newline, Attr
 
 class FixRaise(basefix.BaseFix):
 
     PATTERN = """
-    raise_stmt< 'raise' exc=any ',' a1=any [',' a2=any] >
+    raise_stmt< 'raise' exc=any ',' val=any [',' tb=any] >
     """
 
     def transform(self, node):
         syms = self.syms
         results = self.match(node)
         assert results
-        
+
         exc = results["exc"].clone()
-        args = [results["a1"].clone()]
+        args = [results["val"].clone()]
         args[0].set_prefix("")
-        
-        arg2 = results.get("a2")
-        if arg2 is not None:
-            self.cannot_convert(node, reason)
-            return node
-        
-        new = pytree.Node(syms.raise_stmt,
-                          [pytree.Leaf(token.NAME, "raise"),
-                           exc,
-                           pytree.Node(syms.trailer,
-                                       [pytree.Leaf(token.LPAR, "("),
-                                        pytree.Node(syms.arglist, args),
-                                        pytree.Leaf(token.RPAR, ")")])])
-        new.set_prefix(node.get_prefix())
-        return new
+
+        if "tb" in results:
+            tb = results["tb"].clone()
+            name = Name(self.new_name())
+            children = list(node.parent.parent.children)
+            i = children.index(node.parent)
+            indent = children[1].value
+
+            # Instance the exception
+            build_e = pytree.Node(syms.simple_stmt,
+                                  [Assign(name.clone(), Call(exc, args)),
+                                   Newline()])
+            build_e.parent = node.parent.parent
+            if node.get_prefix():
+                # Over-indents otherwise
+                build_e.set_prefix(indent)
+
+            # Assign the traceback
+            set_tb = pytree.Node(syms.simple_stmt,
+                                 [Assign(Attr(name.clone(),
+                                              Name("__traceback__")), tb),
+                                  Newline()])
+            set_tb.set_prefix(indent)
+            set_tb.parent = node.parent.parent
+
+            # Insert into the suite
+            children[i:i] = [build_e, set_tb]
+            node.parent.parent.children = tuple(children)
+
+            name.set_prefix(" ")
+            new = pytree.Node(syms.simple_stmt, [Name("raise"), name])
+            new.set_prefix(indent)
+            return new
+        else:
+            new = pytree.Node(syms.raise_stmt,
+                              [Name("raise"), Call(exc, args)])
+            new.set_prefix(node.get_prefix())
+            return new

Modified: sandbox/trunk/2to3/fixes/fix_repr.py
==============================================================================
--- sandbox/trunk/2to3/fixes/fix_repr.py	(original)
+++ sandbox/trunk/2to3/fixes/fix_repr.py	Thu Feb  8 23:42:35 2007
@@ -9,6 +9,7 @@
 # Local imports
 import pytree
 from fixes import basefix
+from fixes.macros import Call, Name
 
 
 class FixRepr(basefix.BaseFix):
@@ -23,11 +24,6 @@
       expr = results["expr"].clone()
       if expr.type == self.syms.testlist1:
         expr = self.parenthesize(expr)
-      new = pytree.Node(self.syms.power,
-                        (pytree.Leaf(token.NAME, "repr"),
-                         pytree.Node(self.syms.trailer,
-                                     (pytree.Leaf(token.LPAR, "("),
-                                      expr,
-                                      pytree.Leaf(token.RPAR, ")")))))
+      new = Call(Name("repr"), [expr])
       new.set_prefix(node.get_prefix())
       return new

Added: sandbox/trunk/2to3/fixes/fix_sysexcinfo.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/2to3/fixes/fix_sysexcinfo.py	Thu Feb  8 23:42:35 2007
@@ -0,0 +1,32 @@
+"""Fixer/warner for sys.exc_{info,value,type,traceback}"""
+# Author: Collin Winter
+
+# Python imports
+import token
+
+# Local imports
+from pytree import Leaf
+from fixes import basefix
+
+
+class FixSysexcinfo(basefix.BaseFix):
+
+    PATTERN = """
+    power< 'sys' trailer< '.' attr='exc_info'> any* >
+    |
+    power< 'sys'
+           trailer< '.' attr=('exc_value' | 'exc_traceback' | 'exc_type')>
+           any* >
+    """
+
+    def transform(self, node):
+        results = self.match(node)
+        assert results
+        attr = results['attr']
+
+        if isinstance(attr, Leaf) and attr.value == 'exc_info':
+            self.cannot_convert(node,
+                                "This function is going away in Python 3")
+        else:
+            self.cannot_convert(node,
+                                "This attribute is going away in Python 3")

Added: sandbox/trunk/2to3/fixes/fix_throw.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/2to3/fixes/fix_throw.py	Thu Feb  8 23:42:35 2007
@@ -0,0 +1,80 @@
+"""Fixer for generator.throw(E, V, T)"""
+# Author: Collin Winter
+
+# Python imports
+import token
+
+# Local imports
+import pytree
+from fixes import basefix
+from fixes.macros import Name, Call, Assign, Newline, Attr
+
+class FixThrow(basefix.BaseFix):
+
+    PATTERN = """
+    power< any trailer< '.' 'throw' >
+           trailer< '(' args=arglist< exc=any ',' val=any [',' tb=any] > ')' >
+         >
+    """
+
+    def transform(self, node):
+        syms = self.syms
+        results = self.match(node)
+        assert results
+
+        throw_args = results["args"]
+        exc = results["exc"].clone()
+        args = [results["val"].clone()]
+        args[0].set_prefix("")
+
+        if "tb" in results:
+            tb = results["tb"].clone()
+            name = Name(self.new_name())
+            suite = find_parent_suite(node)
+            stmts = list(suite.children)
+            node_stmt = find_stmt(stmts, node)
+            i = stmts.index(node_stmt)
+            indent = stmts[1].value
+
+            # Instance the exception
+            build_e = pytree.Node(syms.simple_stmt,
+                                  [Assign(name.clone(), Call(exc, args)),
+                                   Newline()])
+            build_e.parent = node.parent.parent
+            if node_stmt.get_prefix():
+                # Over-indents otherwise
+                build_e.set_prefix(indent)
+
+            # Assign the traceback
+            tb.set_prefix(" ")
+            set_tb = pytree.Node(syms.simple_stmt,
+                                 [Assign(Attr(name.clone(),
+                                              Name("__traceback__")), tb),
+                                  Newline()])
+            set_tb.set_prefix(indent)
+            set_tb.parent = node.parent.parent
+
+            # Insert into the suite
+            stmts[i:i] = [build_e, set_tb]
+            suite.children = tuple(stmts)
+
+            throw_args.replace(name)
+            if not node_stmt.get_prefix():
+                node_stmt.set_prefix(indent)
+            # No return
+        else:
+            throw_args.replace(Call(exc, args))
+            # No return
+
+
+def find_parent_suite(node):
+    parent = node.parent
+    while parent:
+        if len(parent.children) > 2 and parent.children[0].value == "\n":
+            return parent
+        parent = parent.parent
+
+def find_stmt(stmts, node):
+    while node not in stmts:
+        node = node.parent
+    return node

Added: sandbox/trunk/2to3/fixes/macros.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/2to3/fixes/macros.py	Thu Feb  8 23:42:35 2007
@@ -0,0 +1,57 @@
+"""Abstract away often-used node construction routines."""
+# Author: Collin Winter
+
+# Python imports
+import token
+
+# Local imports
+from pytree import Leaf, Node
+from pygram import python_symbols as syms
+
+
+### Constant nodes
+ass_leaf = Leaf(token.EQUAL, "=")
+ass_leaf.set_prefix(" ")
+
+comma_leaf = Leaf(token.COMMA, ",")
+lparen_leaf = Leaf(token.LPAR, "(")
+rparen_leaf = Leaf(token.RPAR, ")")
+
+def Assign(target, source):
+    """Build an assignment statement"""
+    if not isinstance(target, tuple):
+        target = (target,)
+    if not isinstance(source, tuple):
+        source.set_prefix(" ")
+        source = (source,)
+
+    return Node(syms.atom, target + (ass_leaf.clone(),) + source)
+
+def Name(name):
+    """Return a NAME leaf"""
+    return Leaf(token.NAME, name)
+
+def Attr(obj, attr):
+    """A node tuple for obj.attr"""
+    return (obj,
+            Node(syms.trailer, [Leaf(token.DOT, '.'),
+                                attr]))
+
+def Comma():
+    """A comma leaf"""
+    return comma_leaf.clone()
+
+def ArgList(args, lparen=lparen_leaf, rparen=rparen_leaf):
+    """A parenthesised argument list, used by Call()"""
+    return Node(syms.trailer,
+                [lparen.clone(),
+                 Node(syms.arglist, args),
+                 rparen.clone()])
+
+def Call(func_name, args):
+    """A function call"""
+    return Node(syms.power, [func_name, ArgList(args)])
+
+def Newline():
+    """A newline literal"""
+    return Leaf(token.NEWLINE, "\n")

Modified: sandbox/trunk/2to3/pytree.py
==============================================================================
--- sandbox/trunk/2to3/pytree.py	(original)
+++ sandbox/trunk/2to3/pytree.py	Thu Feb  8 23:42:35 2007
@@ -115,8 +115,9 @@
         if new is not None:
             new.parent = self.parent
         self.parent = None
-        
+
     def get_lineno(self):
+        """Returns the line number which generated the invocant node."""
         node = self
         while not isinstance(node, Leaf):
             if not node.children:

Modified: sandbox/trunk/2to3/refactor.py
==============================================================================
--- sandbox/trunk/2to3/refactor.py	(original)
+++ sandbox/trunk/2to3/refactor.py	Thu Feb  8 23:42:35 2007
@@ -24,6 +24,7 @@
 import patcomp
 from pgen2 import driver
 import fixes
+import fixes.macros
 import pygram
 
 logging.basicConfig(format='%(name)s: %(message)s', level=logging.INFO)


More information about the Python-checkins mailing list