diff -r 4c03e1aebbc7 Include/opcode.h --- a/Include/opcode.h Tue May 12 19:16:20 2015 -0400 +++ b/Include/opcode.h Tue May 12 23:26:11 2015 -0400 @@ -7,73 +7,74 @@ /* Instruction opcodes for compiled code */ -#define POP_TOP 1 -#define ROT_TWO 2 -#define ROT_THREE 3 -#define DUP_TOP 4 -#define DUP_TOP_TWO 5 -#define NOP 9 -#define UNARY_POSITIVE 10 -#define UNARY_NEGATIVE 11 -#define UNARY_NOT 12 -#define UNARY_INVERT 15 -#define BINARY_MATRIX_MULTIPLY 16 -#define INPLACE_MATRIX_MULTIPLY 17 -#define BINARY_POWER 19 -#define BINARY_MULTIPLY 20 -#define BINARY_MODULO 22 -#define BINARY_ADD 23 -#define BINARY_SUBTRACT 24 -#define BINARY_SUBSCR 25 -#define BINARY_FLOOR_DIVIDE 26 -#define BINARY_TRUE_DIVIDE 27 -#define INPLACE_FLOOR_DIVIDE 28 -#define INPLACE_TRUE_DIVIDE 29 -#define GET_AITER 50 -#define GET_ANEXT 51 -#define BEFORE_ASYNC_WITH 52 -#define STORE_MAP 54 -#define INPLACE_ADD 55 -#define INPLACE_SUBTRACT 56 -#define INPLACE_MULTIPLY 57 -#define INPLACE_MODULO 59 -#define STORE_SUBSCR 60 -#define DELETE_SUBSCR 61 -#define BINARY_LSHIFT 62 -#define BINARY_RSHIFT 63 -#define BINARY_AND 64 -#define BINARY_XOR 65 -#define BINARY_OR 66 -#define INPLACE_POWER 67 -#define GET_ITER 68 -#define PRINT_EXPR 70 -#define LOAD_BUILD_CLASS 71 -#define YIELD_FROM 72 -#define GET_AWAITABLE 73 -#define INPLACE_LSHIFT 75 -#define INPLACE_RSHIFT 76 -#define INPLACE_AND 77 -#define INPLACE_XOR 78 -#define INPLACE_OR 79 -#define BREAK_LOOP 80 -#define WITH_CLEANUP_START 81 -#define WITH_CLEANUP_FINISH 82 -#define RETURN_VALUE 83 -#define IMPORT_STAR 84 -#define YIELD_VALUE 86 -#define POP_BLOCK 87 -#define END_FINALLY 88 -#define POP_EXCEPT 89 -#define HAVE_ARGUMENT 90 -#define STORE_NAME 90 -#define DELETE_NAME 91 -#define UNPACK_SEQUENCE 92 -#define FOR_ITER 93 -#define UNPACK_EX 94 -#define STORE_ATTR 95 -#define DELETE_ATTR 96 -#define STORE_GLOBAL 97 -#define DELETE_GLOBAL 98 +#define POP_TOP 1 +#define ROT_TWO 2 +#define ROT_THREE 3 +#define DUP_TOP 4 +#define DUP_TOP_TWO 5 +#define NOP 9 +#define UNARY_POSITIVE 10 +#define UNARY_NEGATIVE 11 +#define UNARY_NOT 12 +#define UNARY_INVERT 15 +#define BINARY_MATRIX_MULTIPLY 16 +#define INPLACE_MATRIX_MULTIPLY 17 +#define BINARY_POWER 19 +#define BINARY_MULTIPLY 20 +#define BINARY_MODULO 22 +#define BINARY_ADD 23 +#define BINARY_SUBTRACT 24 +#define BINARY_SUBSCR 25 +#define BINARY_FLOOR_DIVIDE 26 +#define BINARY_TRUE_DIVIDE 27 +#define INPLACE_FLOOR_DIVIDE 28 +#define INPLACE_TRUE_DIVIDE 29 +#define GET_AITER 50 +#define GET_ANEXT 51 +#define BEFORE_ASYNC_WITH 52 +#define ASYNC_WITH_CLEANUP_EXCEPT 53 +#define STORE_MAP 54 +#define INPLACE_ADD 55 +#define INPLACE_SUBTRACT 56 +#define INPLACE_MULTIPLY 57 +#define INPLACE_MODULO 59 +#define STORE_SUBSCR 60 +#define DELETE_SUBSCR 61 +#define BINARY_LSHIFT 62 +#define BINARY_RSHIFT 63 +#define BINARY_AND 64 +#define BINARY_XOR 65 +#define BINARY_OR 66 +#define INPLACE_POWER 67 +#define GET_ITER 68 +#define PRINT_EXPR 70 +#define LOAD_BUILD_CLASS 71 +#define YIELD_FROM 72 +#define GET_AWAITABLE 73 +#define INPLACE_LSHIFT 75 +#define INPLACE_RSHIFT 76 +#define INPLACE_AND 77 +#define INPLACE_XOR 78 +#define INPLACE_OR 79 +#define BREAK_LOOP 80 +#define WITH_CLEANUP_START 81 +#define WITH_CLEANUP_FINISH 82 +#define RETURN_VALUE 83 +#define IMPORT_STAR 84 +#define YIELD_VALUE 86 +#define POP_BLOCK 87 +#define END_FINALLY 88 +#define POP_EXCEPT 89 +#define HAVE_ARGUMENT 90 +#define STORE_NAME 90 +#define DELETE_NAME 91 +#define UNPACK_SEQUENCE 92 +#define FOR_ITER 93 +#define UNPACK_EX 94 +#define STORE_ATTR 95 +#define DELETE_ATTR 96 +#define STORE_GLOBAL 97 +#define DELETE_GLOBAL 98 #define LOAD_CONST 100 #define LOAD_NAME 101 #define BUILD_TUPLE 102 diff -r 4c03e1aebbc7 Lib/opcode.py --- a/Lib/opcode.py Tue May 12 19:16:20 2015 -0400 +++ b/Lib/opcode.py Tue May 12 23:26:11 2015 -0400 @@ -88,6 +88,7 @@ def_op('GET_AITER', 50) def_op('GET_ANEXT', 51) def_op('BEFORE_ASYNC_WITH', 52) +def_op('ASYNC_WITH_CLEANUP_EXCEPT', 53) def_op('STORE_MAP', 54) def_op('INPLACE_ADD', 55) diff -r 4c03e1aebbc7 Lib/test/test_coroutines.py --- a/Lib/test/test_coroutines.py Tue May 12 19:16:20 2015 -0400 +++ b/Lib/test/test_coroutines.py Tue May 12 23:26:11 2015 -0400 @@ -497,17 +497,133 @@ return self def __aexit__(self, *e): + return 444 + + async def foo(): + async with CM(): + 1/0 + + try: + run_async(foo()) + except TypeError as exc: + self.assertRegex( + exc.args[0], "object int can't be used in 'await' expression") + self.assertTrue(exc.__context__ is not None) + self.assertTrue(isinstance(exc.__context__, ZeroDivisionError)) + else: + self.fail('invalid asynchronous context manager did not fail') + + + def test_with_8(self): + CNT = 0 + + class CM: + async def __aenter__(self): + return self + + def __aexit__(self, *e): return 456 async def foo(): + nonlocal CNT async with CM(): - pass + CNT += 1 + with self.assertRaisesRegex( TypeError, "object int can't be used in 'await' expression"): run_async(foo()) + self.assertEqual(CNT, 1) + + + def test_with_9(self): + CNT = 0 + + class CM: + async def __aenter__(self): + return self + + async def __aexit__(self, *e): + 1/0 + + async def foo(): + nonlocal CNT + async with CM(): + CNT += 1 + + with self.assertRaises(ZeroDivisionError): + run_async(foo()) + + self.assertEqual(CNT, 1) + + def test_with_10(self): + CNT = 0 + + class CM: + async def __aenter__(self): + return self + + async def __aexit__(self, *e): + 1/0 + + async def foo(): + nonlocal CNT + async with CM(): + async with CM(): + raise RuntimeError + + try: + run_async(foo()) + except ZeroDivisionError as exc: + self.assertTrue(exc.__context__ is not None) + self.assertTrue(isinstance(exc.__context__, ZeroDivisionError)) + self.assertTrue(isinstance(exc.__context__.__context__, + RuntimeError)) + else: + self.fail('exception from __aexit__ did not propagate') + + def test_with_11(self): + CNT = 0 + + class CM: + async def __aenter__(self): + raise NotImplementedError + + async def __aexit__(self, *e): + 1/0 + + async def foo(): + nonlocal CNT + async with CM(): + raise RuntimeError + + try: + run_async(foo()) + except NotImplementedError as exc: + self.assertTrue(exc.__context__ is None) + else: + self.fail('exception from __aenter__ did not propagate') + + def test_with_12(self): + CNT = 0 + + class CM: + async def __aenter__(self): + return self + + async def __aexit__(self, *e): + return True + + async def foo(): + nonlocal CNT + async with CM() as cm: + self.assertIs(cm.__class__, CM) + raise RuntimeError + + run_async(foo()) + def test_for_1(self): aiter_calls = 0 diff -r 4c03e1aebbc7 Python/ceval.c --- a/Python/ceval.c Tue May 12 19:16:20 2015 -0400 +++ b/Python/ceval.c Tue May 12 23:26:11 2015 -0400 @@ -3162,6 +3162,34 @@ DISPATCH(); } + TARGET(ASYNC_WITH_CLEANUP_EXCEPT) { + /* An exception occurred somewhere during 'await __aexit__()' */ + PyTryBlock *b; + PyObject *exctype, *exc, *tb; + + b = PyFrame_BlockPop(f); + if (b->b_type != EXCEPT_HANDLER) { + PyErr_SetString(PyExc_SystemError, + "popped block is not an except handler"); + goto error; + } + + exctype = POP(); + exc = POP(); + tb = POP(); + + /* pop EXCEPT block */ + UNWIND_EXCEPT_HANDLER(b); + + /* TOP() is "res"; SECOND() is "exc". + We want to save "exc" from Py_DECREF in UNWIND_BLOCK. */ + SET_SECOND(NULL); + + /* Restore error */ + PyErr_Restore(exctype, exc, tb); + goto error; + } + PREDICTED(WITH_CLEANUP_FINISH); TARGET(WITH_CLEANUP_FINISH) { PyObject *res = POP(); diff -r 4c03e1aebbc7 Python/compile.c --- a/Python/compile.c Tue May 12 19:16:20 2015 -0400 +++ b/Python/compile.c Tue May 12 23:26:11 2015 -0400 @@ -1066,6 +1066,8 @@ return 0; case GET_ANEXT: return 1; + case ASYNC_WITH_CLEANUP_EXCEPT: + return -2; default: return PY_INVALID_STACK_EFFECT; } @@ -3614,41 +3616,19 @@ } -/* - Implements the async with statement. - - The semantics outlined in that PEP are as follows: - - async with EXPR as VAR: - BLOCK - - It is implemented roughly as: - - context = EXPR - exit = context.__aexit__ # not calling it - value = await context.__aenter__() - try: - VAR = value # if VAR present in the syntax - BLOCK - finally: - if an exception was raised: - exc = copy of (exception, instance, traceback) - else: - exc = (None, None, None) - if not (await exit(*exc)): - raise - */ static int compiler_async_with(struct compiler *c, stmt_ty s, int pos) { - basicblock *block, *finally; + basicblock *block, *finally, *except, *afterexcept; withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos); assert(s->kind == AsyncWith_kind); block = compiler_new_block(c); finally = compiler_new_block(c); - if (!block || !finally) + except = compiler_new_block(c); + afterexcept = compiler_new_block(c); + if (!block || !finally || !except || !afterexcept) return 0; /* Evaluate EXPR */ @@ -3661,7 +3641,6 @@ ADDOP_JREL(c, SETUP_ASYNC_WITH, finally); - /* SETUP_ASYNC_WITH pushes a finally block. */ compiler_use_next_block(c, block); if (!compiler_push_fblock(c, FINALLY_TRY, block)) { return 0; @@ -3682,7 +3661,6 @@ else if (!compiler_async_with(c, s, pos)) return 0; - /* End of try block; start the finally block */ ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, FINALLY_TRY, block); @@ -3691,17 +3669,20 @@ if (!compiler_push_fblock(c, FINALLY_END, finally)) return 0; - /* Finally block starts; context.__exit__ is on the stack under - the exception or return information. Just issue our magic - opcode. */ ADDOP(c, WITH_CLEANUP_START); + ADDOP_JREL(c, SETUP_EXCEPT, except); ADDOP(c, GET_AWAITABLE); ADDOP_O(c, LOAD_CONST, Py_None, consts); ADDOP(c, YIELD_FROM); - ADDOP(c, WITH_CLEANUP_FINISH); - + ADDOP(c, POP_BLOCK); + ADDOP_JREL(c, JUMP_FORWARD, afterexcept); + + compiler_use_next_block(c, except); + ADDOP(c, ASYNC_WITH_CLEANUP_EXCEPT); + + compiler_use_next_block(c, afterexcept); /* Finally block ends. */ ADDOP(c, END_FINALLY); compiler_pop_fblock(c, FINALLY_END, finally); diff -r 4c03e1aebbc7 Python/opcode_targets.h --- a/Python/opcode_targets.h Tue May 12 19:16:20 2015 -0400 +++ b/Python/opcode_targets.h Tue May 12 23:26:11 2015 -0400 @@ -52,7 +52,7 @@ &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, &&TARGET_BEFORE_ASYNC_WITH, - &&_unknown_opcode, + &&TARGET_ASYNC_WITH_CLEANUP_EXCEPT, &&TARGET_STORE_MAP, &&TARGET_INPLACE_ADD, &&TARGET_INPLACE_SUBTRACT,