summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2018-03-10 16:22:34 (GMT)
committerGitHub <noreply@github.com>2018-03-10 16:22:34 (GMT)
commit24d3201eb7f0b39a7eaf2a5b2a2ceca10ad1f8eb (patch)
tree873738d8e87c4929a165e5541e11b29a69dd1e99
parent5e80a71ab67045fecec46573a1892e240b569ace (diff)
downloadcpython-24d3201eb7f0b39a7eaf2a5b2a2ceca10ad1f8eb.zip
cpython-24d3201eb7f0b39a7eaf2a5b2a2ceca10ad1f8eb.tar.gz
cpython-24d3201eb7f0b39a7eaf2a5b2a2ceca10ad1f8eb.tar.bz2
bpo-33041: Fixed bytecode generation for "async for" with a complex target. (#6052)
A StopAsyncIteration raised on assigning or unpacking will be now propagated instead of stopping the iteration.
-rw-r--r--Lib/test/test_coroutines.py65
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-03-10-15-16-40.bpo-33041.-ak5Fk.rst3
-rw-r--r--Python/compile.c4
3 files changed, 70 insertions, 2 deletions
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index b37b61b..f4a9d2a 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -1949,6 +1949,71 @@ class CoroutineTest(unittest.TestCase):
support.gc_collect()
self.assertIn("was never awaited", stderr.getvalue())
+ def test_for_assign_raising_stop_async_iteration(self):
+ class BadTarget:
+ def __setitem__(self, key, value):
+ raise StopAsyncIteration(42)
+ tgt = BadTarget()
+ async def source():
+ yield 10
+
+ async def run_for():
+ with self.assertRaises(StopAsyncIteration) as cm:
+ async for tgt[0] in source():
+ pass
+ self.assertEqual(cm.exception.args, (42,))
+ return 'end'
+ self.assertEqual(run_async(run_for()), ([], 'end'))
+
+ async def run_list():
+ with self.assertRaises(StopAsyncIteration) as cm:
+ return [0 async for tgt[0] in source()]
+ self.assertEqual(cm.exception.args, (42,))
+ return 'end'
+ self.assertEqual(run_async(run_list()), ([], 'end'))
+
+ async def run_gen():
+ gen = (0 async for tgt[0] in source())
+ a = gen.asend(None)
+ with self.assertRaises(RuntimeError) as cm:
+ await a
+ self.assertIsInstance(cm.exception.__cause__, StopAsyncIteration)
+ self.assertEqual(cm.exception.__cause__.args, (42,))
+ return 'end'
+ self.assertEqual(run_async(run_gen()), ([], 'end'))
+
+ def test_for_assign_raising_stop_async_iteration_2(self):
+ class BadIterable:
+ def __iter__(self):
+ raise StopAsyncIteration(42)
+ async def badpairs():
+ yield BadIterable()
+
+ async def run_for():
+ with self.assertRaises(StopAsyncIteration) as cm:
+ async for i, j in badpairs():
+ pass
+ self.assertEqual(cm.exception.args, (42,))
+ return 'end'
+ self.assertEqual(run_async(run_for()), ([], 'end'))
+
+ async def run_list():
+ with self.assertRaises(StopAsyncIteration) as cm:
+ return [0 async for i, j in badpairs()]
+ self.assertEqual(cm.exception.args, (42,))
+ return 'end'
+ self.assertEqual(run_async(run_list()), ([], 'end'))
+
+ async def run_gen():
+ gen = (0 async for i, j in badpairs())
+ a = gen.asend(None)
+ with self.assertRaises(RuntimeError) as cm:
+ await a
+ self.assertIsInstance(cm.exception.__cause__, StopAsyncIteration)
+ self.assertEqual(cm.exception.__cause__.args, (42,))
+ return 'end'
+ self.assertEqual(run_async(run_gen()), ([], 'end'))
+
class CoroAsyncIOCompatTest(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-03-10-15-16-40.bpo-33041.-ak5Fk.rst b/Misc/NEWS.d/next/Core and Builtins/2018-03-10-15-16-40.bpo-33041.-ak5Fk.rst
new file mode 100644
index 0000000..af9ccfd
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-03-10-15-16-40.bpo-33041.-ak5Fk.rst
@@ -0,0 +1,3 @@
+Fixed bytecode generation for "async for" with a complex target. A
+StopAsyncIteration raised on assigning or unpacking will be now propagated
+instead of stopping the iteration.
diff --git a/Python/compile.c b/Python/compile.c
index f14647b..6c9e795 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -2473,8 +2473,8 @@ compiler_async_for(struct compiler *c, stmt_ty s)
ADDOP(c, GET_ANEXT);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
ADDOP(c, YIELD_FROM);
- VISIT(c, expr, s->v.AsyncFor.target);
ADDOP(c, POP_BLOCK); /* for SETUP_FINALLY */
+ VISIT(c, expr, s->v.AsyncFor.target);
compiler_pop_fblock(c, EXCEPT, try);
ADDOP_JREL(c, JUMP_FORWARD, after_try);
@@ -4060,8 +4060,8 @@ compiler_async_comprehension_generator(struct compiler *c,
ADDOP(c, GET_ANEXT);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
ADDOP(c, YIELD_FROM);
- VISIT(c, expr, gen->target);
ADDOP(c, POP_BLOCK);
+ VISIT(c, expr, gen->target);
compiler_pop_fblock(c, EXCEPT, try);
ADDOP_JREL(c, JUMP_FORWARD, after_try);