Author: Amaury Forgeot d'Arc <[email protected]>
Branch: py3.3
Changeset: r70819:0461ffa6c86b
Date: 2014-04-21 17:58 +0200
http://bitbucket.org/pypy/pypy/changeset/0461ffa6c86b/

Log:    Reflect "with" statements with multiple items in the AST. (CPython
        issue #12106)

diff --git a/pypy/interpreter/astcompiler/ast.py 
b/pypy/interpreter/astcompiler/ast.py
--- a/pypy/interpreter/astcompiler/ast.py
+++ b/pypy/interpreter/astcompiler/ast.py
@@ -683,34 +683,39 @@
 
 class With(stmt):
 
-    def __init__(self, context_expr, optional_vars, body, lineno, col_offset):
-        self.context_expr = context_expr
-        self.optional_vars = optional_vars
+    def __init__(self, items, body, lineno, col_offset):
+        self.items = items
+        self.w_items = None
         self.body = body
         self.w_body = None
         stmt.__init__(self, lineno, col_offset)
-        self.initialization_state = 31
+        self.initialization_state = 15
 
     def walkabout(self, visitor):
         visitor.visit_With(self)
 
     def mutate_over(self, visitor):
-        self.context_expr = self.context_expr.mutate_over(visitor)
-        if self.optional_vars:
-            self.optional_vars = self.optional_vars.mutate_over(visitor)
+        if self.items:
+            visitor._mutate_sequence(self.items)
         if self.body:
             visitor._mutate_sequence(self.body)
         return visitor.visit_With(self)
 
     def sync_app_attrs(self, space):
-        if (self.initialization_state & ~8) ^ 23:
-            self.missing_field(space, ['lineno', 'col_offset', 'context_expr', 
None, 'body'], 'With')
+        if (self.initialization_state & ~0) ^ 15:
+            self.missing_field(space, ['lineno', 'col_offset', 'items', 
'body'], 'With')
         else:
-            if not self.initialization_state & 8:
-                self.optional_vars = None
-        self.context_expr.sync_app_attrs(space)
-        if self.optional_vars:
-            self.optional_vars.sync_app_attrs(space)
+            pass
+        w_list = self.w_items
+        if w_list is not None:
+            list_w = space.listview(w_list)
+            if list_w:
+                self.items = [space.interp_w(withitem, w_obj) for w_obj in 
list_w]
+            else:
+                self.items = None
+        if self.items is not None:
+            for node in self.items:
+                node.sync_app_attrs(space)
         w_list = self.w_body
         if w_list is not None:
             list_w = space.listview(w_list)
@@ -2506,6 +2511,32 @@
             if not self.initialization_state & 2:
                 self.asname = None
 
+class withitem(AST):
+
+    def __init__(self, context_expr, optional_vars):
+        self.context_expr = context_expr
+        self.optional_vars = optional_vars
+        self.initialization_state = 3
+
+    def mutate_over(self, visitor):
+        self.context_expr = self.context_expr.mutate_over(visitor)
+        if self.optional_vars:
+            self.optional_vars = self.optional_vars.mutate_over(visitor)
+        return visitor.visit_withitem(self)
+
+    def walkabout(self, visitor):
+        visitor.visit_withitem(self)
+
+    def sync_app_attrs(self, space):
+        if (self.initialization_state & ~2) ^ 1:
+            self.missing_field(space, ['context_expr', None], 'withitem')
+        else:
+            if not self.initialization_state & 2:
+                self.optional_vars = None
+        self.context_expr.sync_app_attrs(space)
+        if self.optional_vars:
+            self.optional_vars.sync_app_attrs(space)
+
 class ASTVisitor(object):
 
     def visit_sequence(self, seq):
@@ -2649,6 +2680,8 @@
         return self.default_visitor(node)
     def visit_alias(self, node):
         return self.default_visitor(node)
+    def visit_withitem(self, node):
+        return self.default_visitor(node)
 
 class GenericASTVisitor(ASTVisitor):
 
@@ -2713,9 +2746,7 @@
         self.visit_sequence(node.orelse)
 
     def visit_With(self, node):
-        node.context_expr.walkabout(self)
-        if node.optional_vars:
-            node.optional_vars.walkabout(self)
+        self.visit_sequence(node.items)
         self.visit_sequence(node.body)
 
     def visit_Raise(self, node):
@@ -2903,6 +2934,11 @@
     def visit_alias(self, node):
         pass
 
+    def visit_withitem(self, node):
+        node.context_expr.walkabout(self)
+        if node.optional_vars:
+            node.optional_vars.walkabout(self)
+
 
 mod.typedef = typedef.TypeDef("mod",
     AST.typedef,
@@ -4163,66 +4199,30 @@
     __init__=interp2app(If_init),
 )
 
-def With_get_context_expr(space, w_self):
-    if w_self.w_dict is not None:
-        w_obj = w_self.getdictvalue(space, 'context_expr')
-        if w_obj is not None:
-            return w_obj
+def With_get_items(space, w_self):
     if not w_self.initialization_state & 4:
-        raise_attriberr(space, w_self, 'context_expr')
-    return space.wrap(w_self.context_expr)
-
-def With_set_context_expr(space, w_self, w_new_value):
-    try:
-        w_self.context_expr = space.interp_w(expr, w_new_value, False)
-        if type(w_self.context_expr) is expr:
-            raise OperationError(space.w_TypeError, space.w_None)
-    except OperationError, e:
-        if not e.match(space, space.w_TypeError):
-            raise
-        w_self.setdictvalue(space, 'context_expr', w_new_value)
-        w_self.initialization_state &= ~4
-        return
-    w_self.deldictvalue(space, 'context_expr')
+        raise_attriberr(space, w_self, 'items')
+    if w_self.w_items is None:
+        if w_self.items is None:
+            list_w = []
+        else:
+            list_w = [space.wrap(node) for node in w_self.items]
+        w_list = space.newlist(list_w)
+        w_self.w_items = w_list
+    return w_self.w_items
+
+def With_set_items(space, w_self, w_new_value):
+    w_self.w_items = w_new_value
     w_self.initialization_state |= 4
 
-def With_del_context_expr(space, w_self):
-    # Check if the element exists, raise appropriate exceptions
-    With_get_context_expr(space, w_self)
-    w_self.deldictvalue(space, 'context_expr')
+def With_del_items(space, w_self):
+    # Check if the element exists, raise appropriate exceptions
+    With_get_items(space, w_self)
+    w_self.deldictvalue(space, 'items')
     w_self.initialization_state &= ~4
 
-def With_get_optional_vars(space, w_self):
-    if w_self.w_dict is not None:
-        w_obj = w_self.getdictvalue(space, 'optional_vars')
-        if w_obj is not None:
-            return w_obj
+def With_get_body(space, w_self):
     if not w_self.initialization_state & 8:
-        raise_attriberr(space, w_self, 'optional_vars')
-    return space.wrap(w_self.optional_vars)
-
-def With_set_optional_vars(space, w_self, w_new_value):
-    try:
-        w_self.optional_vars = space.interp_w(expr, w_new_value, True)
-        if type(w_self.optional_vars) is expr:
-            raise OperationError(space.w_TypeError, space.w_None)
-    except OperationError, e:
-        if not e.match(space, space.w_TypeError):
-            raise
-        w_self.setdictvalue(space, 'optional_vars', w_new_value)
-        w_self.initialization_state &= ~8
-        return
-    w_self.deldictvalue(space, 'optional_vars')
-    w_self.initialization_state |= 8
-
-def With_del_optional_vars(space, w_self):
-    # Check if the element exists, raise appropriate exceptions
-    With_get_optional_vars(space, w_self)
-    w_self.deldictvalue(space, 'optional_vars')
-    w_self.initialization_state &= ~8
-
-def With_get_body(space, w_self):
-    if not w_self.initialization_state & 16:
         raise_attriberr(space, w_self, 'body')
     if w_self.w_body is None:
         if w_self.body is None:
@@ -4235,22 +4235,23 @@
 
 def With_set_body(space, w_self, w_new_value):
     w_self.w_body = w_new_value
-    w_self.initialization_state |= 16
+    w_self.initialization_state |= 8
 
 def With_del_body(space, w_self):
     # Check if the element exists, raise appropriate exceptions
     With_get_body(space, w_self)
     w_self.deldictvalue(space, 'body')
-    w_self.initialization_state &= ~16
-
-_With_field_unroller = unrolling_iterable(['context_expr', 'optional_vars', 
'body'])
+    w_self.initialization_state &= ~8
+
+_With_field_unroller = unrolling_iterable(['items', 'body'])
 def With_init(space, w_self, __args__):
     w_self = space.descr_self_interp_w(With, w_self)
+    w_self.w_items = None
     w_self.w_body = None
     args_w, kwargs_w = __args__.unpack()
     if args_w:
-        if len(args_w) != 3:
-            w_err = space.wrap("With constructor takes either 0 or 3 
positional arguments")
+        if len(args_w) != 2:
+            w_err = space.wrap("With constructor takes either 0 or 2 
positional arguments")
             raise OperationError(space.w_TypeError, w_err)
         i = 0
         for field in _With_field_unroller:
@@ -4262,9 +4263,8 @@
 With.typedef = typedef.TypeDef("With",
     stmt.typedef,
     __module__='_ast',
-    _fields=_FieldsWrapper(['context_expr', 'optional_vars', 'body']),
-    context_expr=typedef.GetSetProperty(With_get_context_expr, 
With_set_context_expr, With_del_context_expr, cls=With),
-    optional_vars=typedef.GetSetProperty(With_get_optional_vars, 
With_set_optional_vars, With_del_optional_vars, cls=With),
+    _fields=_FieldsWrapper(['items', 'body']),
+    items=typedef.GetSetProperty(With_get_items, With_set_items, 
With_del_items, cls=With),
     body=typedef.GetSetProperty(With_get_body, With_set_body, With_del_body, 
cls=With),
     __new__=interp2app(get_AST_new(With)),
     __init__=interp2app(With_init),
@@ -8365,3 +8365,86 @@
     __init__=interp2app(alias_init),
 )
 
+def withitem_get_context_expr(space, w_self):
+    if w_self.w_dict is not None:
+        w_obj = w_self.getdictvalue(space, 'context_expr')
+        if w_obj is not None:
+            return w_obj
+    if not w_self.initialization_state & 1:
+        raise_attriberr(space, w_self, 'context_expr')
+    return space.wrap(w_self.context_expr)
+
+def withitem_set_context_expr(space, w_self, w_new_value):
+    try:
+        w_self.context_expr = space.interp_w(expr, w_new_value, False)
+        if type(w_self.context_expr) is expr:
+            raise OperationError(space.w_TypeError, space.w_None)
+    except OperationError, e:
+        if not e.match(space, space.w_TypeError):
+            raise
+        w_self.setdictvalue(space, 'context_expr', w_new_value)
+        w_self.initialization_state &= ~1
+        return
+    w_self.deldictvalue(space, 'context_expr')
+    w_self.initialization_state |= 1
+
+def withitem_del_context_expr(space, w_self):
+    # Check if the element exists, raise appropriate exceptions
+    withitem_get_context_expr(space, w_self)
+    w_self.deldictvalue(space, 'context_expr')
+    w_self.initialization_state &= ~1
+
+def withitem_get_optional_vars(space, w_self):
+    if w_self.w_dict is not None:
+        w_obj = w_self.getdictvalue(space, 'optional_vars')
+        if w_obj is not None:
+            return w_obj
+    if not w_self.initialization_state & 2:
+        raise_attriberr(space, w_self, 'optional_vars')
+    return space.wrap(w_self.optional_vars)
+
+def withitem_set_optional_vars(space, w_self, w_new_value):
+    try:
+        w_self.optional_vars = space.interp_w(expr, w_new_value, True)
+        if type(w_self.optional_vars) is expr:
+            raise OperationError(space.w_TypeError, space.w_None)
+    except OperationError, e:
+        if not e.match(space, space.w_TypeError):
+            raise
+        w_self.setdictvalue(space, 'optional_vars', w_new_value)
+        w_self.initialization_state &= ~2
+        return
+    w_self.deldictvalue(space, 'optional_vars')
+    w_self.initialization_state |= 2
+
+def withitem_del_optional_vars(space, w_self):
+    # Check if the element exists, raise appropriate exceptions
+    withitem_get_optional_vars(space, w_self)
+    w_self.deldictvalue(space, 'optional_vars')
+    w_self.initialization_state &= ~2
+
+_withitem_field_unroller = unrolling_iterable(['context_expr', 
'optional_vars'])
+def withitem_init(space, w_self, __args__):
+    w_self = space.descr_self_interp_w(withitem, w_self)
+    args_w, kwargs_w = __args__.unpack()
+    if args_w:
+        if len(args_w) != 2:
+            w_err = space.wrap("withitem constructor takes either 0 or 2 
positional arguments")
+            raise OperationError(space.w_TypeError, w_err)
+        i = 0
+        for field in _withitem_field_unroller:
+            space.setattr(w_self, space.wrap(field), args_w[i])
+            i += 1
+    for field, w_value in kwargs_w.iteritems():
+        space.setattr(w_self, space.wrap(field), w_value)
+
+withitem.typedef = typedef.TypeDef("withitem",
+    AST.typedef,
+    __module__='_ast',
+    _fields=_FieldsWrapper(['context_expr', 'optional_vars']),
+    context_expr=typedef.GetSetProperty(withitem_get_context_expr, 
withitem_set_context_expr, withitem_del_context_expr, cls=withitem),
+    optional_vars=typedef.GetSetProperty(withitem_get_optional_vars, 
withitem_set_optional_vars, withitem_del_optional_vars, cls=withitem),
+    __new__=interp2app(get_AST_new(withitem)),
+    __init__=interp2app(withitem_init),
+)
+
diff --git a/pypy/interpreter/astcompiler/astbuilder.py 
b/pypy/interpreter/astcompiler/astbuilder.py
--- a/pypy/interpreter/astcompiler/astbuilder.py
+++ b/pypy/interpreter/astcompiler/astbuilder.py
@@ -433,6 +433,21 @@
             body = [wi]
         return wi
 
+    def handle_with_item(self, item_node):
+        test = self.handle_expr(item_node.children[0])
+        if len(item_node.children) == 3:
+            target = self.handle_expr(item_node.children[2])
+            self.set_context(target, ast.Store)
+        else:
+            target = None
+        return ast.withitem(test, target)
+
+    def handle_with_stmt(self, with_node):
+        body = self.handle_suite(with_node.children[-1])
+        items = [self.handle_with_item(with_node.children[i])
+                 for i in range(1, len(with_node.children)-2, 2)]
+        return ast.With(items, body, with_node.lineno, with_node.column)
+
     def handle_classdef(self, classdef_node, decorators=None):
         name_node = classdef_node.children[1]
         name = self.new_identifier(name_node.value)
diff --git a/pypy/interpreter/astcompiler/codegen.py 
b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -803,17 +803,24 @@
 
     def visit_With(self, wih):
         self.update_position(wih.lineno, True)
+        self.handle_withitem(wih, 0)
+
+    def handle_withitem(self, wih, pos):
         body_block = self.new_block()
         cleanup = self.new_block()
-        wih.context_expr.walkabout(self)
+        witem = wih.items[pos]
+        witem.context_expr.walkabout(self)
         self.emit_jump(ops.SETUP_WITH, cleanup)
         self.use_next_block(body_block)
         self.push_frame_block(F_BLOCK_FINALLY, body_block)
-        if wih.optional_vars:
-            wih.optional_vars.walkabout(self)
+        if witem.optional_vars:
+            witem.optional_vars.walkabout(self)
         else:
             self.emit_op(ops.POP_TOP)
-        self.visit_sequence(wih.body)
+        if pos == len(wih.items) - 1:
+            self.visit_sequence(wih.body)
+        else:
+            self.handle_withitem(wih, pos + 1)
         self.emit_op(ops.POP_BLOCK)
         self.pop_frame_block(F_BLOCK_FINALLY, body_block)
         self.load_const(self.space.w_None)
diff --git a/pypy/interpreter/astcompiler/symtable.py 
b/pypy/interpreter/astcompiler/symtable.py
--- a/pypy/interpreter/astcompiler/symtable.py
+++ b/pypy/interpreter/astcompiler/symtable.py
@@ -499,15 +499,16 @@
 
     def visit_With(self, wih):
         self.scope.new_temporary_name()
-        if wih.optional_vars:
-            self.scope.new_temporary_name()
-        wih.context_expr.walkabout(self)
-        if wih.optional_vars:
-            wih.optional_vars.walkabout(self)
+        self.visit_sequence(wih.items)
         self.scope.note_try_start(wih)
         self.visit_sequence(wih.body)
         self.scope.note_try_end(wih)
 
+    def visit_withitem(self, witem):
+        witem.context_expr.walkabout(self)
+        if witem.optional_vars:
+            witem.optional_vars.walkabout(self)
+
     def visit_arguments(self, arguments):
         scope = self.scope
         assert isinstance(scope, FunctionScope) # Annotator hint.
diff --git a/pypy/interpreter/astcompiler/test/test_astbuilder.py 
b/pypy/interpreter/astcompiler/test/test_astbuilder.py
--- a/pypy/interpreter/astcompiler/test/test_astbuilder.py
+++ b/pypy/interpreter/astcompiler/test/test_astbuilder.py
@@ -405,33 +405,33 @@
     def test_with(self):
         wi = self.get_first_stmt("with x: pass")
         assert isinstance(wi, ast.With)
-        assert isinstance(wi.context_expr, ast.Name)
+        assert len(wi.items) == 1
+        assert isinstance(wi.items[0], ast.withitem)
+        assert isinstance(wi.items[0].context_expr, ast.Name)
+        assert wi.items[0].optional_vars is None
         assert len(wi.body) == 1
-        assert wi.optional_vars is None
         wi = self.get_first_stmt("with x as y: pass")
-        assert isinstance(wi.context_expr, ast.Name)
+        assert isinstance(wi.items[0].context_expr, ast.Name)
         assert len(wi.body) == 1
-        assert isinstance(wi.optional_vars, ast.Name)
-        assert wi.optional_vars.ctx == ast.Store
+        assert isinstance(wi.items[0].optional_vars, ast.Name)
+        assert wi.items[0].optional_vars.ctx == ast.Store
         wi = self.get_first_stmt("with x as (y,): pass")
-        assert isinstance(wi.optional_vars, ast.Tuple)
-        assert len(wi.optional_vars.elts) == 1
-        assert wi.optional_vars.ctx == ast.Store
-        assert wi.optional_vars.elts[0].ctx == ast.Store
+        assert isinstance(wi.items[0].optional_vars, ast.Tuple)
+        assert len(wi.items[0].optional_vars.elts) == 1
+        assert wi.items[0].optional_vars.ctx == ast.Store
+        assert wi.items[0].optional_vars.elts[0].ctx == ast.Store
         input = "with x hi y: pass"
         exc = py.test.raises(SyntaxError, self.get_ast, input).value
         wi = self.get_first_stmt("with x as y, b: pass")
         assert isinstance(wi, ast.With)
-        assert isinstance(wi.context_expr, ast.Name)
-        assert wi.context_expr.id == "x"
-        assert isinstance(wi.optional_vars, ast.Name)
-        assert wi.optional_vars.id == "y"
-        assert len(wi.body) == 1
-        wi = wi.body[0]
-        assert isinstance(wi, ast.With)
-        assert isinstance(wi.context_expr, ast.Name)
-        assert wi.context_expr.id == "b"
-        assert wi.optional_vars is None
+        assert len(wi.items) == 2
+        assert isinstance(wi.items[0].context_expr, ast.Name)
+        assert wi.items[0].context_expr.id == "x"
+        assert isinstance(wi.items[0].optional_vars, ast.Name)
+        assert wi.items[0].optional_vars.id == "y"
+        assert isinstance(wi.items[1].context_expr, ast.Name)
+        assert wi.items[1].context_expr.id == "b"
+        assert wi.items[1].optional_vars is None
         assert len(wi.body) == 1
         assert isinstance(wi.body[0], ast.Pass)
 
diff --git a/pypy/interpreter/astcompiler/test/test_validate.py 
b/pypy/interpreter/astcompiler/test/test_validate.py
--- a/pypy/interpreter/astcompiler/test/test_validate.py
+++ b/pypy/interpreter/astcompiler/test/test_validate.py
@@ -170,16 +170,15 @@
                    [ast.Expr(ast.Name("x", ast.Store, 0, 0), 0, 0)], 0, 0)
         self.stmt(i, "must have Load context")
 
-    @skip("enable when parser uses the new With construct")
     def test_with(self):
         p = ast.Pass(0, 0)
-        self.stmt(ast.With([], [p]), "empty items on With")
+        self.stmt(ast.With([], [p], 0, 0), "empty items on With")
         i = ast.withitem(ast.Num(self.space.wrap(3), 0, 0), None)
-        self.stmt(ast.With([i], []), "empty body on With")
+        self.stmt(ast.With([i], [], 0, 0), "empty body on With")
         i = ast.withitem(ast.Name("x", ast.Store, 0, 0), None)
-        self.stmt(ast.With([i], [p]), "must have Load context")
+        self.stmt(ast.With([i], [p], 0, 0), "must have Load context")
         i = ast.withitem(ast.Num(self.space.wrap(3), 0, 0), ast.Name("x", 
ast.Load, 0, 0))
-        self.stmt(ast.With([i], [p]), "must have Store context")
+        self.stmt(ast.With([i], [p], 0, 0), "must have Store context")
 
     def test_raise(self):
         r = ast.Raise(None, ast.Num(self.space.wrap(3), 0, 0), 0, 0)
@@ -189,8 +188,8 @@
         r = ast.Raise(ast.Num(self.space.wrap(4), 0, 0), ast.Name("x", 
ast.Store, 0, 0), 0, 0)
         self.stmt(r, "must have Load context")
 
-    @skip("enable when parser uses the new Try construct")
     def test_try(self):
+        skip("enable when parser uses the new Try construct")
         p = ast.Pass(0, 0)
         t = ast.Try([], [], [], [p])
         self.stmt(t, "empty body on Try")
diff --git a/pypy/interpreter/astcompiler/tools/Python.asdl 
b/pypy/interpreter/astcompiler/tools/Python.asdl
--- a/pypy/interpreter/astcompiler/tools/Python.asdl
+++ b/pypy/interpreter/astcompiler/tools/Python.asdl
@@ -28,7 +28,7 @@
           | For(expr target, expr iter, stmt* body, stmt* orelse)
           | While(expr test, stmt* body, stmt* orelse)
           | If(expr test, stmt* body, stmt* orelse)
-          | With(expr context_expr, expr? optional_vars, stmt* body)
+          | With(withitem* items, stmt* body)
 
           | Raise(expr? exc, expr? cause)
           | TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
@@ -118,5 +118,7 @@
 
     -- import name with optional 'as' alias.
     alias = (identifier name, identifier? asname)
+
+    withitem = (expr context_expr, expr? optional_vars)
 }
 
diff --git a/pypy/interpreter/astcompiler/validate.py 
b/pypy/interpreter/astcompiler/validate.py
--- a/pypy/interpreter/astcompiler/validate.py
+++ b/pypy/interpreter/astcompiler/validate.py
@@ -218,10 +218,14 @@
         self._validate_body(node.body, "If")
         self._validate_stmts(node.orelse)
 
-    def visit_With(self, node):
+    def visit_withitem(self, node):
         self._validate_expr(node.context_expr)
         if node.optional_vars:
             self._validate_expr(node.optional_vars, ast.Store)
+
+    def visit_With(self, node):
+        self._validate_nonempty_seq(node.items, "items", "With")
+        self.visit_sequence(node.items)
         self._validate_body(node.body, "With")
 
     def visit_Raise(self, node):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to