Merged revisions 72924 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r72924 | georg.brandl | 2009-05-25 23:02:56 +0200 (Mo, 25 Mai 2009) | 6 lines

  Allow multiple context managers in one with statement, as proposed
  in http://codereview.appspot.com/53094 and accepted by Guido.

  The construct is transformed into multiple With AST nodes so that
  there should be no problems with the semantics.
........
This commit is contained in:
Georg Brandl 2009-05-25 21:10:36 +00:00
parent 0c1829b919
commit 0c31562a91
9 changed files with 181 additions and 61 deletions

View File

@ -347,9 +347,10 @@ This allows common :keyword:`try`...\ :keyword:`except`...\ :keyword:`finally`
usage patterns to be encapsulated for convenient reuse. usage patterns to be encapsulated for convenient reuse.
.. productionlist:: .. productionlist::
with_stmt: "with" `expression` ["as" `target`] ":" `suite` with_stmt: "with" with_item ("," with_item)* ":" `suite`
with_item: `expression` ["as" `target`]
The execution of the :keyword:`with` statement proceeds as follows: The execution of the :keyword:`with` statement with one "item" proceeds as follows:
#. The context expression is evaluated to obtain a context manager. #. The context expression is evaluated to obtain a context manager.
@ -382,6 +383,21 @@ The execution of the :keyword:`with` statement proceeds as follows:
value from :meth:`__exit__` is ignored, and execution proceeds at the normal value from :meth:`__exit__` is ignored, and execution proceeds at the normal
location for the kind of exit that was taken. location for the kind of exit that was taken.
With more than one item, the context managers are processed as if multiple
:keyword:`with` statements were nested::
with A() as a, B() as b:
suite
is equivalent to ::
with A() as a:
with B() as b:
suite
.. versionchanged:: 3.1
Support for multiple context expressions.
.. seealso:: .. seealso::
:pep:`0343` - The "with" statement :pep:`0343` - The "with" statement

View File

@ -73,8 +73,8 @@ try_stmt: ('try' ':' suite
['else' ':' suite] ['else' ':' suite]
['finally' ':' suite] | ['finally' ':' suite] |
'finally' ':' suite)) 'finally' ':' suite))
with_stmt: 'with' test [ with_var ] ':' suite with_stmt: 'with' with_item (',' with_item)* ':' suite
with_var: 'as' expr with_item: test ['as' expr]
# NB compile.c makes sure that the default except clause is last # NB compile.c makes sure that the default except clause is last
except_clause: 'except' [test ['as' NAME]] except_clause: 'except' [test ['as' NAME]]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

View File

@ -42,7 +42,7 @@
#define for_stmt 295 #define for_stmt 295
#define try_stmt 296 #define try_stmt 296
#define with_stmt 297 #define with_stmt 297
#define with_var 298 #define with_item 298
#define except_clause 299 #define except_clause 299
#define suite 300 #define suite 300
#define test 301 #define test 301

View File

@ -193,6 +193,7 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase):
def test_with(self): def test_with(self):
self.check_suite("with open('x'): pass\n") self.check_suite("with open('x'): pass\n")
self.check_suite("with open('x') as f: pass\n") self.check_suite("with open('x') as f: pass\n")
self.check_suite("with open('x') as f, open('y') as g: pass\n")
def test_try_stmt(self): def test_try_stmt(self):
self.check_suite("try: pass\nexcept: pass\n") self.check_suite("try: pass\nexcept: pass\n")

View File

@ -656,12 +656,88 @@ class ExitSwallowsExceptionTestCase(unittest.TestCase):
self.fail("ZeroDivisionError should have been raised") self.fail("ZeroDivisionError should have been raised")
class NestedWith(unittest.TestCase):
class Dummy(object):
def __init__(self, value=None, gobble=False):
if value is None:
value = self
self.value = value
self.gobble = gobble
self.enter_called = False
self.exit_called = False
def __enter__(self):
self.enter_called = True
return self.value
def __exit__(self, *exc_info):
self.exit_called = True
self.exc_info = exc_info
if self.gobble:
return True
class CtorRaises(object):
def __init__(self): raise RuntimeError()
class EnterRaises(object):
def __enter__(self): raise RuntimeError()
def __exit__(self, *exc_info): pass
class ExitRaises(object):
def __enter__(self): pass
def __exit__(self, *exc_info): raise RuntimeError()
def testNoExceptions(self):
with self.Dummy() as a, self.Dummy() as b:
self.assertTrue(a.enter_called)
self.assertTrue(b.enter_called)
self.assertTrue(a.exit_called)
self.assertTrue(b.exit_called)
def testExceptionInExprList(self):
try:
with self.Dummy() as a, self.CtorRaises():
pass
except:
pass
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
def testExceptionInEnter(self):
try:
with self.Dummy() as a, self.EnterRaises():
self.fail('body of bad with executed')
except RuntimeError:
pass
else:
self.fail('RuntimeError not reraised')
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
def testExceptionInExit(self):
body_executed = False
with self.Dummy(gobble=True) as a, self.ExitRaises():
body_executed = True
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
self.assertNotEqual(a.exc_info[0], None)
def testEnterReturnsTuple(self):
with self.Dummy(value=(1,2)) as (a1, a2), \
self.Dummy(value=(10, 20)) as (b1, b2):
self.assertEquals(1, a1)
self.assertEquals(2, a2)
self.assertEquals(10, b1)
self.assertEquals(20, b2)
def test_main(): def test_main():
run_unittest(FailureTestCase, NonexceptionalTestCase, run_unittest(FailureTestCase, NonexceptionalTestCase,
NestedNonexceptionalTestCase, ExceptionalTestCase, NestedNonexceptionalTestCase, ExceptionalTestCase,
NonLocalFlowControlTestCase, NonLocalFlowControlTestCase,
AssignmentTargetTestCase, AssignmentTargetTestCase,
ExitSwallowsExceptionTestCase) ExitSwallowsExceptionTestCase,
NestedWith)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -15,6 +15,8 @@ Core and Builtins
- Issue #6089: Fixed str.format with certain invalid field specifiers - Issue #6089: Fixed str.format with certain invalid field specifiers
that would raise SystemError. that would raise SystemError.
- Added support for multiple context managers in the same with statement.
- Issue #5829: complex("1e500") no longer raises OverflowError. This - Issue #5829: complex("1e500") no longer raises OverflowError. This
makes it consistent with float("1e500") and interpretation of real makes it consistent with float("1e500") and interpretation of real
and imaginary literals. and imaginary literals.

View File

@ -2446,36 +2446,39 @@ validate_decorators(node *tree)
return ok; return ok;
} }
/* with_var /* with_item:
with_var: 'as' expr * test ['as' expr]
*/ */
static int static int
validate_with_var(node *tree) validate_with_item(node *tree)
{ {
int nch = NCH(tree); int nch = NCH(tree);
int ok = (validate_ntype(tree, with_var) int ok = (validate_ntype(tree, with_item)
&& (nch == 2) && (nch == 1 || nch == 3)
&& validate_name(CHILD(tree, 0), "as") && validate_test(CHILD(tree, 0)));
&& validate_expr(CHILD(tree, 1))); if (ok && nch == 3)
return ok; ok = (validate_name(CHILD(tree, 1), "as")
&& validate_expr(CHILD(tree, 2)));
return ok;
} }
/* with_stmt /* with_stmt:
* 0 1 2 -2 -1 * 0 1 ... -2 -1
with_stmt: 'with' test [ with_var ] ':' suite * 'with' with_item (',' with_item)* ':' suite
*/ */
static int static int
validate_with_stmt(node *tree) validate_with_stmt(node *tree)
{ {
int i;
int nch = NCH(tree); int nch = NCH(tree);
int ok = (validate_ntype(tree, with_stmt) int ok = (validate_ntype(tree, with_stmt)
&& ((nch == 4) || (nch == 5)) && (nch % 2 == 0)
&& validate_name(CHILD(tree, 0), "with") && validate_name(CHILD(tree, 0), "with")
&& validate_test(CHILD(tree, 1))
&& (nch == 4 || validate_with_var(CHILD(tree, 2)))
&& validate_colon(RCHILD(tree, -2)) && validate_colon(RCHILD(tree, -2))
&& validate_suite(RCHILD(tree, -1))); && validate_suite(RCHILD(tree, -1)));
return ok; for (i = 1; ok && i < nch - 2; i += 2)
ok = validate_with_item(CHILD(tree, i));
return ok;
} }
/* funcdef: /* funcdef:

View File

@ -2959,25 +2959,16 @@ ast_for_try_stmt(struct compiling *c, const node *n)
return TryFinally(body, finally, LINENO(n), n->n_col_offset, c->c_arena); return TryFinally(body, finally, LINENO(n), n->n_col_offset, c->c_arena);
} }
static expr_ty /* with_item: test ['as' expr] */
ast_for_with_var(struct compiling *c, const node *n)
{
REQ(n, with_var);
return ast_for_expr(c, CHILD(n, 1));
}
/* with_stmt: 'with' test [ with_var ] ':' suite */
static stmt_ty static stmt_ty
ast_for_with_stmt(struct compiling *c, const node *n) ast_for_with_item(struct compiling *c, const node *n, asdl_seq *content)
{ {
expr_ty context_expr, optional_vars = NULL; expr_ty context_expr, optional_vars = NULL;
int suite_index = 3; /* skip 'with', test, and ':' */
asdl_seq *suite_seq;
assert(TYPE(n) == with_stmt); REQ(n, with_item);
context_expr = ast_for_expr(c, CHILD(n, 1)); context_expr = ast_for_expr(c, CHILD(n, 0));
if (TYPE(CHILD(n, 2)) == with_var) { if (NCH(n) == 3) {
optional_vars = ast_for_with_var(c, CHILD(n, 2)); optional_vars = ast_for_expr(c, CHILD(n, 2));
if (!optional_vars) { if (!optional_vars) {
return NULL; return NULL;
@ -2985,17 +2976,47 @@ ast_for_with_stmt(struct compiling *c, const node *n)
if (!set_context(c, optional_vars, Store, n)) { if (!set_context(c, optional_vars, Store, n)) {
return NULL; return NULL;
} }
suite_index = 4;
} }
suite_seq = ast_for_suite(c, CHILD(n, suite_index)); return With(context_expr, optional_vars, content, LINENO(n),
if (!suite_seq) {
return NULL;
}
return With(context_expr, optional_vars, suite_seq, LINENO(n),
n->n_col_offset, c->c_arena); n->n_col_offset, c->c_arena);
} }
/* with_stmt: 'with' with_item (',' with_item)* ':' suite */
static stmt_ty
ast_for_with_stmt(struct compiling *c, const node *n)
{
int i;
stmt_ty ret;
asdl_seq *inner;
REQ(n, with_stmt);
/* process the with items inside-out */
i = NCH(n) - 1;
/* the suite of the innermost with item is the suite of the with stmt */
inner = ast_for_suite(c, CHILD(n, i));
if (!inner)
return NULL;
for (;;) {
i -= 2;
ret = ast_for_with_item(c, CHILD(n, i), inner);
if (!ret)
return NULL;
/* was this the last item? */
if (i == 1)
break;
/* if not, wrap the result so far in a new sequence */
inner = asdl_seq_new(1, c->c_arena);
if (!inner)
return NULL;
asdl_seq_SET(inner, 0, ret);
}
return ret;
}
static stmt_ty static stmt_ty
ast_for_classdef(struct compiling *c, const node *n, asdl_seq *decorator_seq) ast_for_classdef(struct compiling *c, const node *n, asdl_seq *decorator_seq)
{ {

View File

@ -911,42 +911,43 @@ static arc arcs_41_0[1] = {
{99, 1}, {99, 1},
}; };
static arc arcs_41_1[1] = { static arc arcs_41_1[1] = {
{24, 2}, {100, 2},
}; };
static arc arcs_41_2[2] = { static arc arcs_41_2[2] = {
{100, 3}, {30, 1},
{25, 4}, {25, 3},
}; };
static arc arcs_41_3[1] = { static arc arcs_41_3[1] = {
{25, 4}, {26, 4},
}; };
static arc arcs_41_4[1] = { static arc arcs_41_4[1] = {
{26, 5}, {0, 4},
}; };
static arc arcs_41_5[1] = { static state states_41[5] = {
{0, 5},
};
static state states_41[6] = {
{1, arcs_41_0}, {1, arcs_41_0},
{1, arcs_41_1}, {1, arcs_41_1},
{2, arcs_41_2}, {2, arcs_41_2},
{1, arcs_41_3}, {1, arcs_41_3},
{1, arcs_41_4}, {1, arcs_41_4},
{1, arcs_41_5},
}; };
static arc arcs_42_0[1] = { static arc arcs_42_0[1] = {
{80, 1}, {24, 1},
}; };
static arc arcs_42_1[1] = { static arc arcs_42_1[2] = {
{101, 2}, {80, 2},
{0, 1},
}; };
static arc arcs_42_2[1] = { static arc arcs_42_2[1] = {
{0, 2}, {101, 3},
}; };
static state states_42[3] = { static arc arcs_42_3[1] = {
{0, 3},
};
static state states_42[4] = {
{1, arcs_42_0}, {1, arcs_42_0},
{1, arcs_42_1}, {2, arcs_42_1},
{1, arcs_42_2}, {1, arcs_42_2},
{1, arcs_42_3},
}; };
static arc arcs_43_0[1] = { static arc arcs_43_0[1] = {
{102, 1}, {102, 1},
@ -1810,10 +1811,10 @@ static dfa dfas[81] = {
"\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000"}, "\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000"},
{296, "try_stmt", 0, 13, states_40, {296, "try_stmt", 0, 13, states_40,
"\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000"}, "\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000"},
{297, "with_stmt", 0, 6, states_41, {297, "with_stmt", 0, 5, states_41,
"\000\000\000\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000"}, "\000\000\000\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000"},
{298, "with_var", 0, 3, states_42, {298, "with_item", 0, 4, states_42,
"\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000"}, "\000\040\040\200\000\000\000\000\000\040\000\000\000\040\004\000\000\103\050\037\000"},
{299, "except_clause", 0, 5, states_43, {299, "except_clause", 0, 5, states_43,
"\000\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000"}, "\000\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000"},
{300, "suite", 0, 5, states_44, {300, "suite", 0, 5, states_44,