| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0451661 commit 944f684
11 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -333,9 +333,10 @@ allows common :keyword:`try`...\ :keyword:`except`...\ :keyword:`finally` usage | |||
| 333 | 333 | patterns to be encapsulated for convenient reuse. | |
| 334 | 334 | ||
| 335 | 335 | .. productionlist:: | |
| 336 | - with_stmt: "with" `expression` ["as" `target`] ":" `suite` | ||
| 336 | + with_stmt: "with" with_item ("," with_item)* ":" `suite` | ||
| 337 | + with_item: `expression` ["as" `target`] | ||
| 337 | 338 | ||
| 338 | - The execution of the :keyword:`with` statement proceeds as follows: | ||
| 339 | + The execution of the :keyword:`with` statement with one "item" proceeds as follows: | ||
| 339 | 340 | ||
| 340 | 341 | #. The context expression is evaluated to obtain a context manager. | |
| 341 | 342 | ||
@@ -369,12 +370,27 @@ The execution of the :keyword:`with` statement proceeds as follows: | |||
| 369 | 370 | from :meth:`__exit__` is ignored, and execution proceeds at the normal location | |
| 370 | 371 | for the kind of exit that was taken. | |
| 371 | 372 | ||
| 373 | + With more than one item, the context managers are processed as if multiple | ||
| 374 | + :keyword:`with` statements were nested:: | ||
| 375 | + | ||
| 376 | + with A() as a, B() as b: | ||
| 377 | + suite | ||
| 378 | + | ||
| 379 | + is equivalent to :: | ||
| 380 | + | ||
| 381 | + with A() as a: | ||
| 382 | + with B() as b: | ||
| 383 | + suite | ||
| 384 | + | ||
| 372 | 385 | .. note:: | |
| 373 | 386 | ||
| 374 | 387 | In Python 2.5, the :keyword:`with` statement is only allowed when the | |
| 375 | 388 | ``with_statement`` feature has been enabled. It is always enabled in | |
| 376 | 389 | Python 2.6. | |
| 377 | 390 | ||
| 391 | + .. versionchanged:: 2.7 | ||
| 392 | + Support for multiple context expressions. | ||
| 393 | + | ||
| 378 | 394 | .. seealso:: | |
| 379 | 395 | ||
| 380 | 396 | :pep:`0343` - The "with" statement | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -83,8 +83,8 @@ try_stmt: ('try' ':' suite | |||
| 83 | 83 | ['else' ':' suite] | |
| 84 | 84 | ['finally' ':' suite] | | |
| 85 | 85 | 'finally' ':' suite)) | |
| 86 | - with_stmt: 'with' test [ with_var ] ':' suite | ||
| 87 | - with_var: 'as' expr | ||
| 86 | + with_stmt: 'with' with_item (',' with_item)* ':' suite | ||
| 87 | + with_item: test ['as' expr] | ||
| 88 | 88 | # NB compile.c makes sure that the default except clause is last | |
| 89 | 89 | except_clause: 'except' [test [('as' | ',') test]] | |
| 90 | 90 | suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,7 +42,7 @@ | |||
| 42 | 42 | #define for_stmt 295 | |
| 43 | 43 | #define try_stmt 296 | |
| 44 | 44 | #define with_stmt 297 | |
| 45 | - #define with_var 298 | ||
| 45 | + #define with_item 298 | ||
| 46 | 46 | #define except_clause 299 | |
| 47 | 47 | #define suite 300 | |
| 48 | 48 | #define testlist_safe 301 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -965,18 +965,22 @@ def com_try_except_finally(self, nodelist): | |||
| 965 | 965 | return try_except | |
| 966 | 966 | ||
| 967 | 967 | def com_with(self, nodelist): | |
| 968 | - # with_stmt: 'with' expr [with_var] ':' suite | ||
| 969 | - expr = self.com_node(nodelist[1]) | ||
| 968 | + # with_stmt: 'with' with_item (',' with_item)* ':' suite | ||
| 970 | 969 | body = self.com_node(nodelist[-1]) | |
| 971 | - if nodelist[2][0] == token.COLON: | ||
| 972 | - var = None | ||
| 970 | + for i in range(len(nodelist) - 3, 0, -2): | ||
| 971 | + ret = self.com_with_item(nodelist[i], body, nodelist[0][2]) | ||
| 972 | + if i == 1: | ||
| 973 | + return ret | ||
| 974 | + body = ret | ||
| 975 | + | ||
| 976 | + def com_with_item(self, nodelist, body, lineno): | ||
| 977 | + # with_item: test ['as' expr] | ||
| 978 | + if len(nodelist) == 4: | ||
| 979 | + var = self.com_assign(nodelist[3], OP_ASSIGN) | ||
| 973 | 980 | else: | |
| 974 | - var = self.com_assign(nodelist[2][2], OP_ASSIGN) | ||
| 975 | - return With(expr, var, body, lineno=nodelist[0][2]) | ||
| 976 | - | ||
| 977 | - def com_with_var(self, nodelist): | ||
| 978 | - # with_var: 'as' expr | ||
| 979 | - return self.com_node(nodelist[1]) | ||
| 981 | + var = None | ||
| 982 | + expr = self.com_node(nodelist[1]) | ||
| 983 | + return With(expr, var, body, lineno=lineno) | ||
| 980 | 984 | ||
| 981 | 985 | def com_augassign_op(self, node): | |
| 982 | 986 | assert node[0] == symbol.augassign | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -165,6 +165,27 @@ def testWithAss(self): | |||
| 165 | 165 | exec c in dct | |
| 166 | 166 | self.assertEquals(dct.get('result'), 1) | |
| 167 | 167 | ||
| 168 | + def testWithMult(self): | ||
| 169 | + events = [] | ||
| 170 | + class Ctx: | ||
| 171 | + def __init__(self, n): | ||
| 172 | + self.n = n | ||
| 173 | + def __enter__(self): | ||
| 174 | + events.append(self.n) | ||
| 175 | + def __exit__(self, *args): | ||
| 176 | + pass | ||
| 177 | + c = compiler.compile('from __future__ import with_statement\n' | ||
| 178 | + 'def f():\n' | ||
| 179 | + ' with Ctx(1) as tc, Ctx(2) as tc2:\n' | ||
| 180 | + ' return 1\n' | ||
| 181 | + 'result = f()', | ||
| 182 | + '<string>', | ||
| 183 | + 'exec' ) | ||
| 184 | + dct = {'Ctx': Ctx} | ||
| 185 | + exec c in dct | ||
| 186 | + self.assertEquals(dct.get('result'), 1) | ||
| 187 | + self.assertEquals(events, [1, 2]) | ||
| 188 | + | ||
| 168 | 189 | def testGlobal(self): | |
| 169 | 190 | code = compiler.compile('global x\nx=1', '<string>', 'exec') | |
| 170 | 191 | d1 = {'__builtins__': {}} | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -199,6 +199,7 @@ def test_assert(self): | |||
| 199 | 199 | def test_with(self): | |
| 200 | 200 | self.check_suite("with open('x'): pass\n") | |
| 201 | 201 | self.check_suite("with open('x') as f: pass\n") | |
| 202 | + self.check_suite("with open('x') as f, open('y') as g: pass\n") | ||
| 202 | 203 | ||
| 203 | 204 | def test_try_stmt(self): | |
| 204 | 205 | self.check_suite("try: pass\nexcept: pass\n") | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -654,12 +654,88 @@ def __exit__(self, t, v, tb): return False | |||
| 654 | 654 | self.fail("ZeroDivisionError should have been raised") | |
| 655 | 655 | ||
| 656 | 656 | ||
| 657 | + class NestedWith(unittest.TestCase): | ||
| 658 | + | ||
| 659 | + class Dummy(object): | ||
| 660 | + def __init__(self, value=None, gobble=False): | ||
| 661 | + if value is None: | ||
| 662 | + value = self | ||
| 663 | + self.value = value | ||
| 664 | + self.gobble = gobble | ||
| 665 | + self.enter_called = False | ||
| 666 | + self.exit_called = False | ||
| 667 | + | ||
| 668 | + def __enter__(self): | ||
| 669 | + self.enter_called = True | ||
| 670 | + return self.value | ||
| 671 | + | ||
| 672 | + def __exit__(self, *exc_info): | ||
| 673 | + self.exit_called = True | ||
| 674 | + self.exc_info = exc_info | ||
| 675 | + if self.gobble: | ||
| 676 | + return True | ||
| 677 | + | ||
| 678 | + class CtorRaises(object): | ||
| 679 | + def __init__(self): raise RuntimeError() | ||
| 680 | + | ||
| 681 | + class EnterRaises(object): | ||
| 682 | + def __enter__(self): raise RuntimeError() | ||
| 683 | + def __exit__(self, *exc_info): pass | ||
| 684 | + | ||
| 685 | + class ExitRaises(object): | ||
| 686 | + def __enter__(self): pass | ||
| 687 | + def __exit__(self, *exc_info): raise RuntimeError() | ||
| 688 | + | ||
| 689 | + def testNoExceptions(self): | ||
| 690 | + with self.Dummy() as a, self.Dummy() as b: | ||
| 691 | + self.assertTrue(a.enter_called) | ||
| 692 | + self.assertTrue(b.enter_called) | ||
| 693 | + self.assertTrue(a.exit_called) | ||
| 694 | + self.assertTrue(b.exit_called) | ||
| 695 | + | ||
| 696 | + def testExceptionInExprList(self): | ||
| 697 | + try: | ||
| 698 | + with self.Dummy() as a, self.CtorRaises(): | ||
| 699 | + pass | ||
| 700 | + except: | ||
| 701 | + pass | ||
| 702 | + self.assertTrue(a.enter_called) | ||
| 703 | + self.assertTrue(a.exit_called) | ||
| 704 | + | ||
| 705 | + def testExceptionInEnter(self): | ||
| 706 | + try: | ||
| 707 | + with self.Dummy() as a, self.EnterRaises(): | ||
| 708 | + self.fail('body of bad with executed') | ||
| 709 | + except RuntimeError: | ||
| 710 | + pass | ||
| 711 | + else: | ||
| 712 | + self.fail('RuntimeError not reraised') | ||
| 713 | + self.assertTrue(a.enter_called) | ||
| 714 | + self.assertTrue(a.exit_called) | ||
| 715 | + | ||
| 716 | + def testExceptionInExit(self): | ||
| 717 | + body_executed = False | ||
| 718 | + with self.Dummy(gobble=True) as a, self.ExitRaises(): | ||
| 719 | + body_executed = True | ||
| 720 | + self.assertTrue(a.enter_called) | ||
| 721 | + self.assertTrue(a.exit_called) | ||
| 722 | + self.assertNotEqual(a.exc_info[0], None) | ||
| 723 | + | ||
| 724 | + def testEnterReturnsTuple(self): | ||
| 725 | + with self.Dummy(value=(1,2)) as (a1, a2), \ | ||
| 726 | + self.Dummy(value=(10, 20)) as (b1, b2): | ||
| 727 | + self.assertEquals(1, a1) | ||
| 728 | + self.assertEquals(2, a2) | ||
| 729 | + self.assertEquals(10, b1) | ||
| 730 | + self.assertEquals(20, b2) | ||
| 731 | + | ||
| 657 | 732 | def test_main(): | |
| 658 | 733 | run_unittest(FailureTestCase, NonexceptionalTestCase, | |
| 659 | 734 | NestedNonexceptionalTestCase, ExceptionalTestCase, | |
| 660 | 735 | NonLocalFlowControlTestCase, | |
| 661 | 736 | AssignmentTargetTestCase, | |
| 662 | - ExitSwallowsExceptionTestCase) | ||
| 737 | + ExitSwallowsExceptionTestCase, | ||
| 738 | + NestedWith) | ||
| 663 | 739 | ||
| 664 | 740 | ||
| 665 | 741 | if __name__ == '__main__': | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,8 @@ What's New in Python 2.7 alpha 1 | |||
| 12 | 12 | Core and Builtins | |
| 13 | 13 | ----------------- | |
| 14 | 14 | ||
| 15 | + - Added support for multiple context managers in the same with statement. | ||
| 16 | + | ||
| 15 | 17 | - Issue #6101: A new opcode, SETUP_WITH, has been added to speed up the with | |
| 16 | 18 | statement and correctly lookup the __enter__ and __exit__ special methods. | |
| 17 | 19 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2618,36 +2618,39 @@ validate_decorators(node *tree) | |||
| 2618 | 2618 | return ok; | |
| 2619 | 2619 | } | |
| 2620 | 2620 | ||
| 2621 | - /* with_var | ||
| 2622 | - with_var: 'as' expr | ||
| 2621 | + /* with_item: | ||
| 2622 | + * test ['as' expr] | ||
| 2623 | 2623 | */ | |
| 2624 | 2624 | static int | |
| 2625 | - validate_with_var(node *tree) | ||
| 2625 | + validate_with_item(node *tree) | ||
| 2626 | 2626 | { | |
| 2627 | 2627 | int nch = NCH(tree); | |
| 2628 | - int ok = (validate_ntype(tree, with_var) | ||
| 2629 | - && (nch == 2) | ||
| 2630 | - && validate_name(CHILD(tree, 0), "as") | ||
| 2631 | - && validate_expr(CHILD(tree, 1))); | ||
| 2632 | - return ok; | ||
| 2628 | + int ok = (validate_ntype(tree, with_item) | ||
| 2629 | + && (nch == 1 || nch == 3) | ||
| 2630 | + && validate_test(CHILD(tree, 0))); | ||
| 2631 | + if (ok && nch == 3) | ||
| 2632 | + ok = (validate_name(CHILD(tree, 1), "as") | ||
| 2633 | + && validate_expr(CHILD(tree, 2))); | ||
| 2634 | + return ok; | ||
| 2633 | 2635 | } | |
| 2634 | 2636 | ||
| 2635 | - /* with_stmt | ||
| 2636 | - * 0 1 2 -2 -1 | ||
| 2637 | - with_stmt: 'with' test [ with_var ] ':' suite | ||
| 2637 | + /* with_stmt: | ||
| 2638 | + * 0 1 ... -2 -1 | ||
| 2639 | + * 'with' with_item (',' with_item)* ':' suite | ||
| 2638 | 2640 | */ | |
| 2639 | 2641 | static int | |
| 2640 | 2642 | validate_with_stmt(node *tree) | |
| 2641 | 2643 | { | |
| 2644 | + int i; | ||
| 2642 | 2645 | int nch = NCH(tree); | |
| 2643 | 2646 | int ok = (validate_ntype(tree, with_stmt) | |
| 2644 | - && ((nch == 4) || (nch == 5)) | ||
| 2647 | + && (nch % 2 == 0) | ||
| 2645 | 2648 | && validate_name(CHILD(tree, 0), "with") | |
| 2646 | - && validate_test(CHILD(tree, 1)) | ||
| 2647 | - && (nch == 4 || validate_with_var(CHILD(tree, 2))) | ||
| 2648 | 2649 | && validate_colon(RCHILD(tree, -2)) | |
| 2649 | 2650 | && validate_suite(RCHILD(tree, -1))); | |
| 2650 | - return ok; | ||
| 2651 | + for (i = 1; ok && i < nch - 2; i += 2) | ||
| 2652 | + ok = validate_with_item(CHILD(tree, i)); | ||
| 2653 | + return ok; | ||
| 2651 | 2654 | } | |
| 2652 | 2655 | ||
| 2653 | 2656 | /* funcdef: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3009,43 +3009,64 @@ ast_for_try_stmt(struct compiling *c, const node *n) | |||
| 3009 | 3009 | return TryFinally(body, finally, LINENO(n), n->n_col_offset, c->c_arena); | |
| 3010 | 3010 | } | |
| 3011 | 3011 | ||
| 3012 | - static expr_ty | ||
| 3013 | - ast_for_with_var(struct compiling *c, const node *n) | ||
| 3014 | - { | ||
| 3015 | - REQ(n, with_var); | ||
| 3016 | - return ast_for_expr(c, CHILD(n, 1)); | ||
| 3017 | - } | ||
| 3018 | - | ||
| 3019 | - /* with_stmt: 'with' test [ with_var ] ':' suite */ | ||
| 3012 | + /* with_item: test ['as' expr] */ | ||
| 3020 | 3013 | static stmt_ty | |
| 3021 | - ast_for_with_stmt(struct compiling *c, const node *n) | ||
| 3014 | + ast_for_with_item(struct compiling *c, const node *n, asdl_seq *content) | ||
| 3022 | 3015 | { | |
| 3023 | 3016 | expr_ty context_expr, optional_vars = NULL; | |
| 3024 | - int suite_index = 3; /* skip 'with', test, and ':' */ | ||
| 3025 | - asdl_seq *suite_seq; | ||
| 3026 | 3017 | ||
| 3027 | - assert(TYPE(n) == with_stmt); | ||
| 3028 | - context_expr = ast_for_expr(c, CHILD(n, 1)); | ||
| 3018 | + REQ(n, with_item); | ||
| 3019 | + context_expr = ast_for_expr(c, CHILD(n, 0)); | ||
| 3029 | 3020 | if (!context_expr) | |
| 3030 | 3021 | return NULL; | |
| 3031 | - if (TYPE(CHILD(n, 2)) == with_var) { | ||
| 3032 | - optional_vars = ast_for_with_var(c, CHILD(n, 2)); | ||
| 3022 | + if (NCH(n) == 3) { | ||
| 3023 | + optional_vars = ast_for_expr(c, CHILD(n, 2)); | ||
| 3033 | 3024 | ||
| 3034 | 3025 | if (!optional_vars) { | |
| 3035 | 3026 | return NULL; | |
| 3036 | 3027 | } | |
| 3037 | 3028 | if (!set_context(c, optional_vars, Store, n)) { | |
| 3038 | 3029 | return NULL; | |
| 3039 | 3030 | } | |
| 3040 | - suite_index = 4; | ||
| 3041 | 3031 | } | |
| 3042 | 3032 | ||
| 3043 | - suite_seq = ast_for_suite(c, CHILD(n, suite_index)); | ||
| 3044 | - if (!suite_seq) { | ||
| 3033 | + return With(context_expr, optional_vars, content, LINENO(n), | ||
| 3034 | + n->n_col_offset, c->c_arena); | ||
| 3035 | + } | ||
| 3036 | + | ||
| 3037 | + /* with_stmt: 'with' with_item (',' with_item)* ':' suite */ | ||
| 3038 | + static stmt_ty | ||
| 3039 | + ast_for_with_stmt(struct compiling *c, const node *n) | ||
| 3040 | + { | ||
| 3041 | + int i; | ||
| 3042 | + stmt_ty ret; | ||
| 3043 | + asdl_seq *inner; | ||
| 3044 | + | ||
| 3045 | + REQ(n, with_stmt); | ||
| 3046 | + | ||
| 3047 | + /* process the with items inside-out */ | ||
| 3048 | + i = NCH(n) - 1; | ||
| 3049 | + /* the suite of the innermost with item is the suite of the with stmt */ | ||
| 3050 | + inner = ast_for_suite(c, CHILD(n, i)); | ||
| 3051 | + if (!inner) | ||
| 3045 | 3052 | return NULL; | |
| 3053 | + | ||
| 3054 | + for (;;) { | ||
| 3055 | + i -= 2; | ||
| 3056 | + ret = ast_for_with_item(c, CHILD(n, i), inner); | ||
| 3057 | + if (!ret) | ||
| 3058 | + return NULL; | ||
| 3059 | + /* was this the last item? */ | ||
| 3060 | + if (i == 1) | ||
| 3061 | + break; | ||
| 3062 | + /* if not, wrap the result so far in a new sequence */ | ||
| 3063 | + inner = asdl_seq_new(1, c->c_arena); | ||
| 3064 | + if (!inner) | ||
| 3065 | + return NULL; | ||
| 3066 | + asdl_seq_SET(inner, 0, ret); | ||
| 3046 | 3067 | } | |
| 3047 | - return With(context_expr, optional_vars, suite_seq, LINENO(n), | ||
| 3048 | - n->n_col_offset, c->c_arena); | ||
| 3068 | + | ||
| 3069 | + return ret; | ||
| 3049 | 3070 | } | |
| 3050 | 3071 | ||
| 3051 | 3072 | static stmt_ty | |
| Back | FazBrowse Home | New Git URL |
0 commit comments