[pypy-svn] r23813 - in pypy/dist: lib-python/modified-2.4.1 pypy/interpreter pypy/interpreter/astcompiler pypy/interpreter/pyparser pypy/interpreter/pyparser/data pypy/interpreter/pyparser/test pypy/interpreter/pyparser/test/samples pypy/interpreter/test

stuart at codespeak.net stuart at codespeak.net
Wed Mar 1 03:09:42 CET 2006


Author: stuart
Date: Wed Mar  1 03:09:41 2006
New Revision: 23813

Added:
   pypy/dist/lib-python/modified-2.4.1/opcode.py
      - copied, changed from r23743, pypy/dist/lib-python/2.4.1/opcode.py
   pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_1.py
   pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_2.py
Modified:
   pypy/dist/pypy/interpreter/astcompiler/ast.py
   pypy/dist/pypy/interpreter/astcompiler/ast.txt
   pypy/dist/pypy/interpreter/astcompiler/pyassem.py
   pypy/dist/pypy/interpreter/astcompiler/pycodegen.py
   pypy/dist/pypy/interpreter/pyopcode.py
   pypy/dist/pypy/interpreter/pyparser/astbuilder.py
   pypy/dist/pypy/interpreter/pyparser/data/Grammar2.5a
   pypy/dist/pypy/interpreter/pyparser/pysymbol.py
   pypy/dist/pypy/interpreter/pyparser/test/test_astbuilder.py
   pypy/dist/pypy/interpreter/pyparser/test/test_samples.py
   pypy/dist/pypy/interpreter/test/test_syntax.py
Log:
(Arre, Ale, Stuart Williams)
Added with statement.  Cleaned up code to no longer depend either on
dis module for byte code opcodes or on Python 2.4.  Missing: more
tests and from __future__ import with_statement.



Modified: pypy/dist/pypy/interpreter/astcompiler/ast.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/ast.py	(original)
+++ pypy/dist/pypy/interpreter/astcompiler/ast.py	Wed Mar  1 03:09:41 2006
@@ -4195,10 +4195,19 @@
 
     def getChildren(self):
         "NOT_RPYTHON"
-        return self.expr, self.body, self.var
+        children = []
+        children.append(self.expr)
+        children.append(self.body)
+        children.append(self.var)
+        return tuple(children)
 
     def getChildNodes(self):
-        return [self.expr, self.body]
+        nodelist = []
+        nodelist.append(self.expr)
+        nodelist.append(self.body)
+        if self.var is not None:
+            nodelist.append(self.var)
+        return nodelist
 
     def __repr__(self):
         return "With(%s, %s, %s)" % (self.expr.__repr__(), self.body.__repr__(), self.var.__repr__())
@@ -4215,9 +4224,12 @@
     def fset_body( space, self, w_arg):
         self.body = space.interp_w(Node, w_arg, can_be_None=False)
     def fget_var( space, self):
-        return space.wrap(self.var)
+        if self.var is None:
+            return space.w_None
+        else:
+            return space.wrap(self.var)
     def fset_var( space, self, w_arg):
-        self.var = space.str_w(w_arg)
+        self.var = space.interp_w(Node, w_arg, can_be_None=True)
 
 def descr_With_new(space, w_subtype, w_expr, w_body, w_var, lineno=-1):
     self = space.allocate_instance(With, w_subtype)
@@ -4225,7 +4237,7 @@
     self.expr = expr
     body = space.interp_w(Node, w_body, can_be_None=False)
     self.body = body
-    var = space.str_w(w_var)
+    var = space.interp_w(Node, w_var, can_be_None=True)
     self.var = var
     self.lineno = lineno
     return space.wrap(self)

Modified: pypy/dist/pypy/interpreter/astcompiler/ast.txt
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/ast.txt	(original)
+++ pypy/dist/pypy/interpreter/astcompiler/ast.txt	Wed Mar  1 03:09:41 2006
@@ -27,7 +27,7 @@
 Break: 
 Continue: 
 For: assign, list, body, else_&
-With: expr, body, var*str
+With: expr, body, var&
 While: test, body, else_&
 If: tests!, else_&
 Exec: expr, locals&, globals&

Modified: pypy/dist/pypy/interpreter/astcompiler/pyassem.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/pyassem.py	(original)
+++ pypy/dist/pypy/interpreter/astcompiler/pyassem.py	Wed Mar  1 03:09:41 2006
@@ -1,6 +1,5 @@
 """A flow graph representation for Python bytecode"""
 
-import dis
 import sys
 
 from pypy.interpreter.astcompiler import misc, ast
@@ -9,7 +8,15 @@
 from pypy.interpreter.pycode import PyCode
 from pypy.interpreter.baseobjspace import W_Root
 
+# load opcode.py as pythonopcode from our own lib
+def load_opcode():
+    import new, py
+    global pythonopcode
+    pythonopcode = new.module('opcode')
+    opcode_path = py.path.local(__file__).dirpath().dirpath().dirpath().dirpath('lib-python/modified-2.4.1/opcode.py')
+    execfile(str(opcode_path), pythonopcode.__dict__)
 
+load_opcode()
 
 class BlockSet:
     """A Set implementation specific to Blocks
@@ -633,11 +640,11 @@
         self.stage = FLAT
 
     hasjrel = {}
-    for i in dis.hasjrel:
-        hasjrel[dis.opname[i]] = True
+    for i in pythonopcode.hasjrel:
+        hasjrel[pythonopcode.opname[i]] = True
     hasjabs = {}
-    for i in dis.hasjabs:
-        hasjabs[dis.opname[i]] = True
+    for i in pythonopcode.hasjabs:
+        hasjabs[pythonopcode.opname[i]] = True
 
     def convertArgs(self):
         """Convert arguments from symbolic to concrete form"""
@@ -772,7 +779,7 @@
         index = self._lookupName(arg, self.closure)
         return InstrInt(inst.op, index)
     
-    _cmp = list(dis.cmp_op)
+    _cmp = list(pythonopcode.cmp_op)
     def _convert_COMPARE_OP(self, inst):
         assert isinstance(inst, InstrName)
         arg = inst.name                        
@@ -817,8 +824,9 @@
         self.stage = DONE
 
     opnum = {}
-    for num in range(len(dis.opname)):
-        opnum[dis.opname[num]] = num
+    for num in range(len(pythonopcode.opname)):
+        opnum[pythonopcode.opname[num]] = num
+        # This seems to duplicate dis.opmap from opcode.opmap
     del num
 
     def newCodeObject(self):
@@ -1048,6 +1056,7 @@
         'SETUP_EXCEPT': 3,
         'SETUP_FINALLY': 3,
         'FOR_ITER': 1,
+        'WITH_CLEANUP': 3,
         }
     # use pattern match
     patterns = [

Modified: pypy/dist/pypy/interpreter/astcompiler/pycodegen.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/pycodegen.py	(original)
+++ pypy/dist/pypy/interpreter/astcompiler/pycodegen.py	Wed Mar  1 03:09:41 2006
@@ -543,6 +543,50 @@
 
         self.nextBlock(end)
 
+    __with_count = 0
+
+    def visitWith(self, node):
+        node.expr.accept(self)
+        self.emitop('LOAD_ATTR', '__context__')
+        self.emitop_int('CALL_FUNCTION', 0)
+        self.emit('DUP_TOP')
+
+        ## exit = ctx.__exit__
+        self.emitop('LOAD_ATTR', '__exit__')
+        exit = "$exit%d" % self.__with_count
+        var = "$var%d" % self.__with_count
+        self.__with_count = self.__with_count + 1
+        self._implicitNameOp('STORE', exit)
+
+        self.emitop('LOAD_ATTR', '__enter__')
+        self.emitop_int('CALL_FUNCTION', 0)
+        finally_block = self.newBlock()
+
+        if node.var is not None:        # VAR is present
+            self._implicitNameOp('STORE', var)
+            self.emitop_block('SETUP_FINALLY', finally_block)
+            self._implicitNameOp('LOAD', var)
+            self._implicitNameOp('DELETE', var)
+            node.var.accept(self)
+        else:
+            self.emit('POP_TOP')
+            self.emitop_block('SETUP_FINALLY', finally_block)
+
+        node.body.accept(self)
+        
+        self.emit('POP_BLOCK')
+        self.emitop_obj('LOAD_CONST', self.space.w_None) # WITH_CLEANUP checks for normal exit
+        self.nextBlock(finally_block)
+
+        # find local variable with is context.__exit__
+        self._implicitNameOp('LOAD', exit)
+        self._implicitNameOp('DELETE', exit)
+
+        self.emit('WITH_CLEANUP')
+        self.emitop_int('CALL_FUNCTION', 3)
+        self.emit('POP_TOP')
+        self.emit('END_FINALLY')
+
     def visitCompare(self, node):
         node.expr.accept( self )
         cleanup = self.newBlock()

Modified: pypy/dist/pypy/interpreter/pyopcode.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyopcode.py	(original)
+++ pypy/dist/pypy/interpreter/pyopcode.py	Wed Mar  1 03:09:41 2006
@@ -20,7 +20,7 @@
     import new, py
     global pythonopcode
     pythonopcode = new.module('opcode')
-    opcode_path = py.path.local(__file__).dirpath().dirpath().dirpath('lib-python/2.4.1/opcode.py')
+    opcode_path = py.path.local(__file__).dirpath().dirpath().dirpath('lib-python/modified-2.4.1/opcode.py')
     execfile(str(opcode_path), pythonopcode.__dict__)
 
 load_opcode()
@@ -647,6 +647,25 @@
         block = pyframe.FinallyBlock(f, f.next_instr + offsettoend)
         f.blockstack.push(block)
 
+    def WITH_CLEANUP(f):
+        x = f.valuestack.top()
+        u = f.valuestack.top(1)
+        if (f.space.is_w(f.space.type(u), f.space.w_int)
+            or f.space.is_w(u, f.space.w_None)):
+            u = v = w = f.space.w_None
+        else:
+            v = f.valuestack.top(2)
+            w = f.valuestack.top(3)
+            f.valuestack.pop()
+            f.valuestack.pop()
+            f.valuestack.pop()
+            f.valuestack.pop()
+            f.valuestack.push(f.space.w_None)
+            f.valuestack.push(x)
+        f.valuestack.push(u)
+        f.valuestack.push(v)
+        f.valuestack.push(w)
+
     def call_function(f, oparg, w_star=None, w_starstar=None):
         n_arguments = oparg & 0xff
         n_keywords = (oparg>>8) & 0xff

Modified: pypy/dist/pypy/interpreter/pyparser/astbuilder.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/astbuilder.py	(original)
+++ pypy/dist/pypy/interpreter/pyparser/astbuilder.py	Wed Mar  1 03:09:41 2006
@@ -1236,9 +1236,9 @@
         else_ = atoms[6]
     builder.push(ast.While(test, body, else_, atoms[0].lineno))
 
-
 def build_with_stmt(builder, nb):
-    """with_stmt: 'with' test [ 'as' NAME ] ':' suite"""
+    """with_stmt: 'with' test [ NAME expr ] ':' suite"""
+
     atoms = get_atoms(builder, nb)
     # skip 'with'
     test =  atoms[1]
@@ -1247,11 +1247,15 @@
         var = None
     # if there is an "as" clause
     else:
-        var = atoms[3]
+        token = atoms[2]
+        assert isinstance(token, TokenObject)
+        if token.get_value() != 'as':
+            raise SyntaxError("invalid syntax", token.lineno, token.col)
+        varexpr = atoms[3]
+        var = to_lvalue(varexpr, consts.OP_ASSIGN)
         body = atoms[5]
     builder.push(ast.With(test, body, var, atoms[0].lineno))
 
-
 def build_import_name(builder, nb):
     """import_name: 'import' dotted_as_names
 
@@ -1500,7 +1504,7 @@
     sym['break_stmt'] : build_break_stmt,
     sym['for_stmt'] : build_for_stmt,
     sym['while_stmt'] : build_while_stmt,
-#    sym['with_stmt'] : build_with_stmt,
+    sym['with_stmt'] : build_with_stmt,
     sym['import_name'] : build_import_name,
     sym['import_from'] : build_import_from,
     sym['yield_stmt'] : build_yield_stmt,

Modified: pypy/dist/pypy/interpreter/pyparser/data/Grammar2.5a
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/data/Grammar2.5a	(original)
+++ pypy/dist/pypy/interpreter/pyparser/data/Grammar2.5a	Wed Mar  1 03:09:41 2006
@@ -63,12 +63,13 @@
 exec_stmt: 'exec' expr ['in' test [',' test]]
 assert_stmt: 'assert' test [',' test]
 
-compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
+compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef
 if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
 while_stmt: 'while' test ':' suite ['else' ':' suite]
 for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
 try_stmt: ('try' ':' suite (except_clause ':' suite)+ #diagram:break
            ['else' ':' suite] | 'try' ':' suite 'finally' ':' suite)
+with_stmt: 'with' test [ NAME expr ] ':' suite
 # NB compile.c makes sure that the default except clause is last
 except_clause: 'except' [test [',' test]]
 suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

Modified: pypy/dist/pypy/interpreter/pyparser/pysymbol.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/pysymbol.py	(original)
+++ pypy/dist/pypy/interpreter/pyparser/pysymbol.py	Wed Mar  1 03:09:41 2006
@@ -49,19 +49,6 @@
 
 _cpython_symbols = SymbolMapper( symbol.sym_name )
 
-
-### prepopulate symbol table from symbols used by CPython
-##for _value, _name in _cpython_symbols.sym_name.items():
-##    globals()[_name] = _value
-
-    
-### (This function does not seen to be called right now)
-##def update_symbols( parser ):
-##    """Update the symbol module according to rules
-##    in PythonParser instance : parser"""
-##    for rule in parser.rules:
-##        _cpython_symbols.add_symbol( rule )
-
 # There is no symbol in this module until the grammar is loaded
 # once loaded the grammar parser will fill the mappings with the
 # grammar symbols

Added: pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_1.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_1.py	Wed Mar  1 03:09:41 2006
@@ -0,0 +1,4 @@
+# EXPECT: Module(None, Stmt([With(Name('acontext'), Stmt([Pass()]), None)]))
+with acontext:
+   pass
+

Added: pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_2.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_2.py	Wed Mar  1 03:09:41 2006
@@ -0,0 +1,4 @@
+# EXPECT: Module(None, Stmt([With(Name('acontext'), Stmt([Pass()]), AssName('avariable', OP_ASSIGN))]))
+with acontext as avariable:
+   pass
+

Modified: pypy/dist/pypy/interpreter/pyparser/test/test_astbuilder.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/test/test_astbuilder.py	(original)
+++ pypy/dist/pypy/interpreter/pyparser/test/test_astbuilder.py	Wed Mar  1 03:09:41 2006
@@ -745,8 +745,12 @@
         for expr in family:
             yield check_expression, expr, 'exec'
 
+NEW_GRAMMAR_SNIPPETS = [    
+    'snippet_with_1.py',
+    'snippet_with_2.py',
+    ]
+
 SNIPPETS = [    
-#    'snippet_with_1.py',
     'snippet_1.py',
     'snippet_several_statements.py',
     'snippet_simple_function.py',
@@ -788,7 +792,7 @@
     ]
 
 def test_snippets():
-    for snippet_name in SNIPPETS:
+    for snippet_name in SNIPPETS + NEW_GRAMMAR_SNIPPETS:
         filepath = os.path.join(os.path.dirname(__file__), 'samples', snippet_name)
         source = file(filepath).read()
         # To avoid using the stable compiler we pull an explicit AST out of the snippet

Modified: pypy/dist/pypy/interpreter/pyparser/test/test_samples.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/test/test_samples.py	(original)
+++ pypy/dist/pypy/interpreter/pyparser/test/test_samples.py	Wed Mar  1 03:09:41 2006
@@ -21,6 +21,10 @@
     #"snippet_import_statements.py",
     "snippet_decorators.py",
 ]
+SKIP_ALWAYS = [
+    "snippet_with_1.py",
+    "snippet_with_2.py",
+]
 REAL_EXPECTED_OUTPUT = {
     # for snippets that show bugs of Python's compiler package
     "snippet_transformer_bug.py":
@@ -73,6 +77,8 @@
         for fname in os.listdir(samples_dir):
             if not fname.endswith('.py'):
                 continue
+            if fname in SKIP_ALWAYS:
+                continue
             if GRAMMAR_MISMATCH and fname in SKIP_IF_NOT_NATIVE:
                 print "Skipping", fname
                 continue

Modified: pypy/dist/pypy/interpreter/test/test_syntax.py
==============================================================================
--- pypy/dist/pypy/interpreter/test/test_syntax.py	(original)
+++ pypy/dist/pypy/interpreter/test/test_syntax.py	Wed Mar  1 03:09:41 2006
@@ -256,6 +256,30 @@
             exec s
             assert x == expected
 
+class AppTestWith:
+    def test_with(self):
+
+        s = """if 1:
+        # from __future__ import with_statement
+        class Context:
+            def __init__(self):
+                self.calls = list()
+            def __context__(self):
+                self.calls.append('__context__')
+                return self
+            def __enter__(self):
+                self.calls.append('__enter__')
+                pass
+            def __exit__(self, exc_type, exc_value, exc_tb):
+                self.calls.append('__exit__')
+                pass
+        acontext = Context()
+        with acontext:
+            pass
+        """
+        exec s
+
+        assert acontext.calls == '__context__ __enter__ __exit__'.split()
         
 if __name__ == '__main__':
     # only to check on top of CPython (you need 2.4)



More information about the Pypy-commit mailing list