Skip to content

Commit 18d7edf

Browse files
[3.6] bpo-33041: Fixed jumping if the function contains an "async for" loop. (GH-6154). (GH-6199)
(cherry picked from commit b9744e9)
1 parent 560ea27 commit 18d7edf

5 files changed

Lines changed: 105 additions & 27 deletions

File tree

Lib/test/test_coroutines.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,36 @@ async def run_gen():
18921892
run_async(run_gen()),
18931893
([], [121]))
18941894

1895+
def test_comp_4_2(self):
1896+
async def f(it):
1897+
for i in it:
1898+
yield i
1899+
1900+
async def run_list():
1901+
return [i + 10 async for i in f(range(5)) if 0 < i < 4]
1902+
self.assertEqual(
1903+
run_async(run_list()),
1904+
([], [11, 12, 13]))
1905+
1906+
async def run_set():
1907+
return {i + 10 async for i in f(range(5)) if 0 < i < 4}
1908+
self.assertEqual(
1909+
run_async(run_set()),
1910+
([], {11, 12, 13}))
1911+
1912+
async def run_dict():
1913+
return {i + 10: i + 100 async for i in f(range(5)) if 0 < i < 4}
1914+
self.assertEqual(
1915+
run_async(run_dict()),
1916+
([], {11: 101, 12: 102, 13: 103}))
1917+
1918+
async def run_gen():
1919+
gen = (i + 10 async for i in f(range(5)) if 0 < i < 4)
1920+
return [g + 100 async for g in gen]
1921+
self.assertEqual(
1922+
run_async(run_gen()),
1923+
([], [111, 112, 113]))
1924+
18951925
def test_comp_5(self):
18961926
async def f(it):
18971927
for i in it:

Lib/test/test_dis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ async def async_def():
597597
Argument count: 0
598598
Kw-only arguments: 0
599599
Number of locals: 2
600-
Stack size: 17
600+
Stack size: 16
601601
Flags: OPTIMIZED, NEWLOCALS, NOFREE, COROUTINE
602602
Constants:
603603
0: None

Lib/test/test_sys_settrace.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ async def __aenter__(self):
3232
async def __aexit__(self, *exc_info):
3333
self.output.append(-self.value)
3434

35+
async def asynciter(iterable):
36+
"""Convert an iterable to an asynchronous iterator."""
37+
for x in iterable:
38+
yield x
39+
3540
def asyncio_run(main):
3641
import asyncio
3742
import asyncio.events
@@ -690,6 +695,23 @@ def test_jump_out_of_block_backwards(output):
690695
output.append(6)
691696
output.append(7)
692697

698+
@async_jump_test(4, 5, [3, 5])
699+
async def test_jump_out_of_async_for_block_forwards(output):
700+
for i in [1]:
701+
async for i in asynciter([1, 2]):
702+
output.append(3)
703+
output.append(4)
704+
output.append(5)
705+
706+
@async_jump_test(5, 2, [2, 4, 2, 4, 5, 6])
707+
async def test_jump_out_of_async_for_block_backwards(output):
708+
for i in [1]:
709+
output.append(2)
710+
async for i in asynciter([1]):
711+
output.append(4)
712+
output.append(5)
713+
output.append(6)
714+
693715
@jump_test(1, 2, [3])
694716
def test_jump_to_codeless_line(output):
695717
output.append(1)
@@ -969,6 +991,17 @@ def test_jump_over_for_block_before_else(output):
969991
output.append(7)
970992
output.append(8)
971993

994+
@async_jump_test(1, 7, [7, 8])
995+
async def test_jump_over_async_for_block_before_else(output):
996+
output.append(1)
997+
if not output: # always false
998+
async for i in asynciter([3]):
999+
output.append(4)
1000+
else:
1001+
output.append(6)
1002+
output.append(7)
1003+
output.append(8)
1004+
9721005
# The second set of 'jump' tests are for things that are not allowed:
9731006

9741007
@jump_test(2, 3, [1], (ValueError, 'after'))
@@ -1020,12 +1053,24 @@ def test_no_jump_forwards_into_for_block(output):
10201053
for i in 1, 2:
10211054
output.append(3)
10221055

1056+
@async_jump_test(1, 3, [], (ValueError, 'into'))
1057+
async def test_no_jump_forwards_into_async_for_block(output):
1058+
output.append(1)
1059+
async for i in asynciter([1, 2]):
1060+
output.append(3)
1061+
10231062
@jump_test(3, 2, [2, 2], (ValueError, 'into'))
10241063
def test_no_jump_backwards_into_for_block(output):
10251064
for i in 1, 2:
10261065
output.append(2)
10271066
output.append(3)
10281067

1068+
@async_jump_test(3, 2, [2, 2], (ValueError, 'into'))
1069+
async def test_no_jump_backwards_into_async_for_block(output):
1070+
async for i in asynciter([1, 2]):
1071+
output.append(2)
1072+
output.append(3)
1073+
10291074
@jump_test(2, 4, [], (ValueError, 'into'))
10301075
def test_no_jump_forwards_into_while_block(output):
10311076
i = 1
@@ -1165,6 +1210,17 @@ def test_no_jump_into_for_block_before_else(output):
11651210
output.append(7)
11661211
output.append(8)
11671212

1213+
@async_jump_test(7, 4, [1, 6], (ValueError, 'into'))
1214+
async def test_no_jump_into_async_for_block_before_else(output):
1215+
output.append(1)
1216+
if not output: # always false
1217+
async for i in asynciter([3]):
1218+
output.append(4)
1219+
else:
1220+
output.append(6)
1221+
output.append(7)
1222+
output.append(8)
1223+
11681224
def test_no_jump_to_non_integers(self):
11691225
self.run_test(no_jump_to_non_integers, 2, "Spam", [True])
11701226

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed jumping when the function contains an ``async for`` loop.

Python/compile.c

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2237,23 +2237,19 @@ compiler_async_for(struct compiler *c, stmt_ty s)
22372237
ADDOP(c, DUP_TOP);
22382238
ADDOP_O(c, LOAD_GLOBAL, stop_aiter_error, names);
22392239
ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
2240-
ADDOP_JABS(c, POP_JUMP_IF_FALSE, try_cleanup);
2241-
2242-
ADDOP(c, POP_TOP);
2243-
ADDOP(c, POP_TOP);
2244-
ADDOP(c, POP_TOP);
2245-
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
2246-
ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
2247-
ADDOP_JABS(c, JUMP_ABSOLUTE, after_loop_else);
2248-
2249-
2250-
compiler_use_next_block(c, try_cleanup);
2240+
ADDOP_JABS(c, POP_JUMP_IF_TRUE, try_cleanup);
22512241
ADDOP(c, END_FINALLY);
22522242

22532243
compiler_use_next_block(c, after_try);
22542244
VISIT_SEQ(c, stmt, s->v.AsyncFor.body);
22552245
ADDOP_JABS(c, JUMP_ABSOLUTE, try);
22562246

2247+
compiler_use_next_block(c, try_cleanup);
2248+
ADDOP(c, POP_TOP);
2249+
ADDOP(c, POP_TOP);
2250+
ADDOP(c, POP_TOP);
2251+
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
2252+
ADDOP(c, POP_TOP); /* for correct calculation of stack effect */
22572253
ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
22582254
compiler_pop_fblock(c, LOOP, try);
22592255

@@ -3750,7 +3746,7 @@ compiler_async_comprehension_generator(struct compiler *c,
37503746
_Py_IDENTIFIER(StopAsyncIteration);
37513747

37523748
comprehension_ty gen;
3753-
basicblock *anchor, *if_cleanup, *try,
3749+
basicblock *if_cleanup, *try,
37543750
*after_try, *except, *try_cleanup;
37553751
Py_ssize_t i, n;
37563752

@@ -3761,12 +3757,11 @@ compiler_async_comprehension_generator(struct compiler *c,
37613757

37623758
try = compiler_new_block(c);
37633759
after_try = compiler_new_block(c);
3764-
try_cleanup = compiler_new_block(c);
37653760
except = compiler_new_block(c);
37663761
if_cleanup = compiler_new_block(c);
3767-
anchor = compiler_new_block(c);
3762+
try_cleanup = compiler_new_block(c);
37683763

3769-
if (if_cleanup == NULL || anchor == NULL ||
3764+
if (if_cleanup == NULL ||
37703765
try == NULL || after_try == NULL ||
37713766
except == NULL || try_cleanup == NULL) {
37723767
return 0;
@@ -3807,16 +3802,7 @@ compiler_async_comprehension_generator(struct compiler *c,
38073802
ADDOP(c, DUP_TOP);
38083803
ADDOP_O(c, LOAD_GLOBAL, stop_aiter_error, names);
38093804
ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
3810-
ADDOP_JABS(c, POP_JUMP_IF_FALSE, try_cleanup);
3811-
3812-
ADDOP(c, POP_TOP);
3813-
ADDOP(c, POP_TOP);
3814-
ADDOP(c, POP_TOP);
3815-
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
3816-
ADDOP_JABS(c, JUMP_ABSOLUTE, anchor);
3817-
3818-
3819-
compiler_use_next_block(c, try_cleanup);
3805+
ADDOP_JABS(c, POP_JUMP_IF_TRUE, try_cleanup);
38203806
ADDOP(c, END_FINALLY);
38213807

38223808
compiler_use_next_block(c, after_try);
@@ -3865,7 +3851,12 @@ compiler_async_comprehension_generator(struct compiler *c,
38653851
}
38663852
compiler_use_next_block(c, if_cleanup);
38673853
ADDOP_JABS(c, JUMP_ABSOLUTE, try);
3868-
compiler_use_next_block(c, anchor);
3854+
3855+
compiler_use_next_block(c, try_cleanup);
3856+
ADDOP(c, POP_TOP);
3857+
ADDOP(c, POP_TOP);
3858+
ADDOP(c, POP_TOP);
3859+
ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
38693860
ADDOP(c, POP_TOP);
38703861

38713862
return 1;

0 commit comments

Comments
 (0)