[3.6] bpo-33041: Fixed jumping if the function contains an "async for" loop. (GH-6154). (GH-6199)

(cherry picked from commit b9744e924c)
This commit is contained in:
Serhiy Storchaka 2018-03-23 15:45:37 +02:00 committed by GitHub
parent 560ea272b0
commit 18d7edf32e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 27 deletions

View File

@ -1892,6 +1892,36 @@ class CoroutineTest(unittest.TestCase):
run_async(run_gen()),
([], [121]))
def test_comp_4_2(self):
async def f(it):
for i in it:
yield i
async def run_list():
return [i + 10 async for i in f(range(5)) if 0 < i < 4]
self.assertEqual(
run_async(run_list()),
([], [11, 12, 13]))
async def run_set():
return {i + 10 async for i in f(range(5)) if 0 < i < 4}
self.assertEqual(
run_async(run_set()),
([], {11, 12, 13}))
async def run_dict():
return {i + 10: i + 100 async for i in f(range(5)) if 0 < i < 4}
self.assertEqual(
run_async(run_dict()),
([], {11: 101, 12: 102, 13: 103}))
async def run_gen():
gen = (i + 10 async for i in f(range(5)) if 0 < i < 4)
return [g + 100 async for g in gen]
self.assertEqual(
run_async(run_gen()),
([], [111, 112, 113]))
def test_comp_5(self):
async def f(it):
for i in it:

View File

@ -597,7 +597,7 @@ Filename: (.*)
Argument count: 0
Kw-only arguments: 0
Number of locals: 2
Stack size: 17
Stack size: 16
Flags: OPTIMIZED, NEWLOCALS, NOFREE, COROUTINE
Constants:
0: None

View File

@ -32,6 +32,11 @@ class asynctracecontext:
async def __aexit__(self, *exc_info):
self.output.append(-self.value)
async def asynciter(iterable):
"""Convert an iterable to an asynchronous iterator."""
for x in iterable:
yield x
def asyncio_run(main):
import asyncio
import asyncio.events
@ -690,6 +695,23 @@ class JumpTestCase(unittest.TestCase):
output.append(6)
output.append(7)
@async_jump_test(4, 5, [3, 5])
async def test_jump_out_of_async_for_block_forwards(output):
for i in [1]:
async for i in asynciter([1, 2]):
output.append(3)
output.append(4)
output.append(5)
@async_jump_test(5, 2, [2, 4, 2, 4, 5, 6])
async def test_jump_out_of_async_for_block_backwards(output):
for i in [1]:
output.append(2)
async for i in asynciter([1]):
output.append(4)
output.append(5)
output.append(6)
@jump_test(1, 2, [3])
def test_jump_to_codeless_line(output):
output.append(1)
@ -969,6 +991,17 @@ class JumpTestCase(unittest.TestCase):
output.append(7)
output.append(8)
@async_jump_test(1, 7, [7, 8])
async def test_jump_over_async_for_block_before_else(output):
output.append(1)
if not output: # always false
async for i in asynciter([3]):
output.append(4)
else:
output.append(6)
output.append(7)
output.append(8)
# The second set of 'jump' tests are for things that are not allowed:
@jump_test(2, 3, [1], (ValueError, 'after'))
@ -1020,12 +1053,24 @@ class JumpTestCase(unittest.TestCase):
for i in 1, 2:
output.append(3)
@async_jump_test(1, 3, [], (ValueError, 'into'))
async def test_no_jump_forwards_into_async_for_block(output):
output.append(1)
async for i in asynciter([1, 2]):
output.append(3)
@jump_test(3, 2, [2, 2], (ValueError, 'into'))
def test_no_jump_backwards_into_for_block(output):
for i in 1, 2:
output.append(2)
output.append(3)
@async_jump_test(3, 2, [2, 2], (ValueError, 'into'))
async def test_no_jump_backwards_into_async_for_block(output):
async for i in asynciter([1, 2]):
output.append(2)
output.append(3)
@jump_test(2, 4, [], (ValueError, 'into'))
def test_no_jump_forwards_into_while_block(output):
i = 1
@ -1165,6 +1210,17 @@ class JumpTestCase(unittest.TestCase):
output.append(7)
output.append(8)
@async_jump_test(7, 4, [1, 6], (ValueError, 'into'))
async def test_no_jump_into_async_for_block_before_else(output):
output.append(1)
if not output: # always false
async for i in asynciter([3]):
output.append(4)
else:
output.append(6)
output.append(7)
output.append(8)
def test_no_jump_to_non_integers(self):
self.run_test(no_jump_to_non_integers, 2, "Spam", [True])

View File

@ -0,0 +1 @@
Fixed jumping when the function contains an ``async for`` loop.

View File

@ -2237,23 +2237,19 @@ compiler_async_for(struct compiler *c, stmt_ty s)
ADDOP(c, DUP_TOP);
ADDOP_O(c, LOAD_GLOBAL, stop_aiter_error, names);
ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
ADDOP_JABS(c, POP_JUMP_IF_FALSE, try_cleanup);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
ADDOP_JABS(c, JUMP_ABSOLUTE, after_loop_else);
compiler_use_next_block(c, try_cleanup);
ADDOP_JABS(c, POP_JUMP_IF_TRUE, try_cleanup);
ADDOP(c, END_FINALLY);
compiler_use_next_block(c, after_try);
VISIT_SEQ(c, stmt, s->v.AsyncFor.body);
ADDOP_JABS(c, JUMP_ABSOLUTE, try);
compiler_use_next_block(c, try_cleanup);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
ADDOP(c, POP_TOP); /* for correct calculation of stack effect */
ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
compiler_pop_fblock(c, LOOP, try);
@ -3750,7 +3746,7 @@ compiler_async_comprehension_generator(struct compiler *c,
_Py_IDENTIFIER(StopAsyncIteration);
comprehension_ty gen;
basicblock *anchor, *if_cleanup, *try,
basicblock *if_cleanup, *try,
*after_try, *except, *try_cleanup;
Py_ssize_t i, n;
@ -3761,12 +3757,11 @@ compiler_async_comprehension_generator(struct compiler *c,
try = compiler_new_block(c);
after_try = compiler_new_block(c);
try_cleanup = compiler_new_block(c);
except = compiler_new_block(c);
if_cleanup = compiler_new_block(c);
anchor = compiler_new_block(c);
try_cleanup = compiler_new_block(c);
if (if_cleanup == NULL || anchor == NULL ||
if (if_cleanup == NULL ||
try == NULL || after_try == NULL ||
except == NULL || try_cleanup == NULL) {
return 0;
@ -3807,16 +3802,7 @@ compiler_async_comprehension_generator(struct compiler *c,
ADDOP(c, DUP_TOP);
ADDOP_O(c, LOAD_GLOBAL, stop_aiter_error, names);
ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
ADDOP_JABS(c, POP_JUMP_IF_FALSE, try_cleanup);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
ADDOP_JABS(c, JUMP_ABSOLUTE, anchor);
compiler_use_next_block(c, try_cleanup);
ADDOP_JABS(c, POP_JUMP_IF_TRUE, try_cleanup);
ADDOP(c, END_FINALLY);
compiler_use_next_block(c, after_try);
@ -3865,7 +3851,12 @@ compiler_async_comprehension_generator(struct compiler *c,
}
compiler_use_next_block(c, if_cleanup);
ADDOP_JABS(c, JUMP_ABSOLUTE, try);
compiler_use_next_block(c, anchor);
compiler_use_next_block(c, try_cleanup);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
ADDOP(c, POP_TOP);
return 1;