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