Skip to content

Commit 2ef78a8

Browse files
[3.14] GH-135171: Revert async generator expressions behavior (#135352)
1 parent 0eec8dd commit 2ef78a8

File tree

6 files changed

+48
-16
lines changed

6 files changed

+48
-16
lines changed

Lib/test/test_asyncgen.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,20 @@ async def run():
18351835
res = self.loop.run_until_complete(run())
18361836
self.assertEqual(res, [i * 2 for i in range(1, 10)])
18371837

1838+
def test_async_gen_expression_incorrect(self):
1839+
async def ag():
1840+
yield 42
1841+
1842+
async def run(arg):
1843+
(x async for x in arg)
1844+
1845+
err_msg_async = "'async for' requires an object with " \
1846+
"__aiter__ method, got .*"
1847+
1848+
self.loop.run_until_complete(run(ag()))
1849+
with self.assertRaisesRegex(TypeError, err_msg_async):
1850+
self.loop.run_until_complete(run(None))
1851+
18381852
def test_asyncgen_nonstarted_hooks_are_cancellable(self):
18391853
# See https://bugs.python.org/issue38013
18401854
messages = []

Lib/test/test_coroutines.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2267,7 +2267,7 @@ def c():
22672267

22682268
def test_call_aiter_once_in_comprehension(self):
22692269

2270-
class Iterator:
2270+
class AsyncIterator:
22712271

22722272
def __init__(self):
22732273
self.val = 0
@@ -2283,12 +2283,17 @@ async def __anext__(self):
22832283
class C:
22842284

22852285
def __aiter__(self):
2286-
return Iterator()
2286+
return AsyncIterator()
22872287

2288-
async def run():
2288+
async def run_listcomp():
22892289
return [i async for i in C()]
22902290

2291-
self.assertEqual(run_async(run()), ([], [1,2]))
2291+
async def run_asyncgen():
2292+
ag = (i async for i in C())
2293+
return [i async for i in ag]
2294+
2295+
self.assertEqual(run_async(run_listcomp()), ([], [1, 2]))
2296+
self.assertEqual(run_async(run_asyncgen()), ([], [1, 2]))
22922297

22932298

22942299
@unittest.skipIf(

Lib/test/test_generators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ class C:
288288
def __iter__(self):
289289
return Iterator()
290290

291-
self.assertEqual([1,2], list(i for i in C()))
291+
self.assertEqual([1, 2], list(i for i in C()))
292292

293293

294294
class ModifyUnderlyingIterableTest(unittest.TestCase):

Lib/test/test_listcomps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ class C:
770770
def __iter__(self):
771771
return Iterator()
772772

773-
self.assertEqual([1,2], [i for i in C()])
773+
self.assertEqual([1, 2], [i for i in C()])
774774

775775
__test__ = {'doctests' : doctests}
776776

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Reverts the behavior of async generator expressions when created with object
2+
w/o __aiter__ method to the pre-3.13 behavior of raising a TypeError.

Python/codegen.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4411,7 +4411,6 @@ codegen_sync_comprehension_generator(compiler *c, location loc,
44114411

44124412
comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators,
44134413
gen_index);
4414-
int is_outer_genexpr = gen_index == 0 && type == COMP_GENEXP;
44154414
if (!iter_on_stack) {
44164415
if (gen_index == 0) {
44174416
assert(METADATA(c)->u_argcount == 1);
@@ -4442,15 +4441,13 @@ codegen_sync_comprehension_generator(compiler *c, location loc,
44424441
}
44434442
if (IS_JUMP_TARGET_LABEL(start)) {
44444443
VISIT(c, expr, gen->iter);
4444+
ADDOP(c, LOC(gen->iter), GET_ITER);
44454445
}
44464446
}
44474447
}
44484448

44494449
if (IS_JUMP_TARGET_LABEL(start)) {
44504450
depth++;
4451-
if (!is_outer_genexpr) {
4452-
ADDOP(c, LOC(gen->iter), GET_ITER);
4453-
}
44544451
USE_LABEL(c, start);
44554452
ADDOP_JUMP(c, LOC(gen->iter), FOR_ITER, anchor);
44564453
}
@@ -4544,9 +4541,9 @@ codegen_async_comprehension_generator(compiler *c, location loc,
45444541
else {
45454542
/* Sub-iter - calculate on the fly */
45464543
VISIT(c, expr, gen->iter);
4544+
ADDOP(c, LOC(gen->iter), GET_AITER);
45474545
}
45484546
}
4549-
ADDOP(c, LOC(gen->iter), GET_AITER);
45504547

45514548
USE_LABEL(c, start);
45524549
/* Runtime will push a block here, so we need to account for that */
@@ -4758,6 +4755,19 @@ pop_inlined_comprehension_state(compiler *c, location loc,
47584755
return SUCCESS;
47594756
}
47604757

4758+
static inline int
4759+
codegen_comprehension_iter(compiler *c, comprehension_ty comp)
4760+
{
4761+
VISIT(c, expr, comp->iter);
4762+
if (comp->is_async) {
4763+
ADDOP(c, LOC(comp->iter), GET_AITER);
4764+
}
4765+
else {
4766+
ADDOP(c, LOC(comp->iter), GET_ITER);
4767+
}
4768+
return SUCCESS;
4769+
}
4770+
47614771
static int
47624772
codegen_comprehension(compiler *c, expr_ty e, int type,
47634773
identifier name, asdl_comprehension_seq *generators, expr_ty elt,
@@ -4776,9 +4786,10 @@ codegen_comprehension(compiler *c, expr_ty e, int type,
47764786
location loc = LOC(e);
47774787

47784788
outermost = (comprehension_ty) asdl_seq_GET(generators, 0);
4779-
int is_sync_genexpr = type == COMP_GENEXP && !outermost->is_async;
47804789
if (is_inlined) {
4781-
VISIT(c, expr, outermost->iter);
4790+
if (codegen_comprehension_iter(c, outermost)) {
4791+
goto error;
4792+
}
47824793
if (push_inlined_comprehension_state(c, loc, entry, &inline_state)) {
47834794
goto error;
47844795
}
@@ -4852,10 +4863,10 @@ codegen_comprehension(compiler *c, expr_ty e, int type,
48524863
}
48534864
Py_CLEAR(co);
48544865

4855-
VISIT(c, expr, outermost->iter);
4856-
if (is_sync_genexpr) {
4857-
ADDOP(c, loc, GET_ITER);
4866+
if (codegen_comprehension_iter(c, outermost)) {
4867+
goto error;
48584868
}
4869+
48594870
ADDOP_I(c, loc, CALL, 0);
48604871

48614872
if (is_async_comprehension && type != COMP_GENEXP) {

0 commit comments

Comments
 (0)