https://github.com/python/cpython/commit/2ef78a85e4b6c033ea8aac12625b3309a6ec3679
commit: 2ef78a85e4b6c033ea8aac12625b3309a6ec3679
branch: 3.14
author: Mikhail Efimov <[email protected]>
committer: hugovk <[email protected]>
date: 2025-06-16T17:45:42+03:00
summary:
[3.14] GH-135171: Revert async generator expressions behavior (#135352)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-06-10-17-37-11.gh-issue-135171.P9UDfS.rst
M Lib/test/test_asyncgen.py
M Lib/test/test_coroutines.py
M Lib/test/test_generators.py
M Lib/test/test_listcomps.py
M Python/codegen.c
diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py
index 2c44647bf3e2f9..2f2865bad2cdb1 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -1835,6 +1835,20 @@ async def run():
res = self.loop.run_until_complete(run())
self.assertEqual(res, [i * 2 for i in range(1, 10)])
+ def test_async_gen_expression_incorrect(self):
+ async def ag():
+ yield 42
+
+ async def run(arg):
+ (x async for x in arg)
+
+ err_msg_async = "'async for' requires an object with " \
+ "__aiter__ method, got .*"
+
+ self.loop.run_until_complete(run(ag()))
+ with self.assertRaisesRegex(TypeError, err_msg_async):
+ self.loop.run_until_complete(run(None))
+
def test_asyncgen_nonstarted_hooks_are_cancellable(self):
# See https://bugs.python.org/issue38013
messages = []
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index c8360a834482e1..954003ab14df93 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -2267,7 +2267,7 @@ def c():
def test_call_aiter_once_in_comprehension(self):
- class Iterator:
+ class AsyncIterator:
def __init__(self):
self.val = 0
@@ -2283,12 +2283,17 @@ async def __anext__(self):
class C:
def __aiter__(self):
- return Iterator()
+ return AsyncIterator()
- async def run():
+ async def run_listcomp():
return [i async for i in C()]
- self.assertEqual(run_async(run()), ([], [1,2]))
+ async def run_asyncgen():
+ ag = (i async for i in C())
+ return [i async for i in ag]
+
+ self.assertEqual(run_async(run_listcomp()), ([], [1, 2]))
+ self.assertEqual(run_async(run_asyncgen()), ([], [1, 2]))
@unittest.skipIf(
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index 6c056b63f88480..cb73179160d10d 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -288,7 +288,7 @@ class C:
def __iter__(self):
return Iterator()
- self.assertEqual([1,2], list(i for i in C()))
+ self.assertEqual([1, 2], list(i for i in C()))
class ModifyUnderlyingIterableTest(unittest.TestCase):
diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py
index 38a36d3c9925a2..ef8f3d492dfc46 100644
--- a/Lib/test/test_listcomps.py
+++ b/Lib/test/test_listcomps.py
@@ -770,7 +770,7 @@ class C:
def __iter__(self):
return Iterator()
- self.assertEqual([1,2], [i for i in C()])
+ self.assertEqual([1, 2], [i for i in C()])
__test__ = {'doctests' : doctests}
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-10-17-37-11.gh-issue-135171.P9UDfS.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-10-17-37-11.gh-issue-135171.P9UDfS.rst
new file mode 100644
index 00000000000000..ae2731d393a9d0
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-10-17-37-11.gh-issue-135171.P9UDfS.rst
@@ -0,0 +1,2 @@
+Reverts the behavior of async generator expressions when created with object
+w/o __aiter__ method to the pre-3.13 behavior of raising a TypeError.
diff --git a/Python/codegen.c b/Python/codegen.c
index 93c3a21266cb7d..d708e59ed67baf 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -4411,7 +4411,6 @@ codegen_sync_comprehension_generator(compiler *c,
location loc,
comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators,
gen_index);
- int is_outer_genexpr = gen_index == 0 && type == COMP_GENEXP;
if (!iter_on_stack) {
if (gen_index == 0) {
assert(METADATA(c)->u_argcount == 1);
@@ -4442,15 +4441,13 @@ codegen_sync_comprehension_generator(compiler *c,
location loc,
}
if (IS_JUMP_TARGET_LABEL(start)) {
VISIT(c, expr, gen->iter);
+ ADDOP(c, LOC(gen->iter), GET_ITER);
}
}
}
if (IS_JUMP_TARGET_LABEL(start)) {
depth++;
- if (!is_outer_genexpr) {
- ADDOP(c, LOC(gen->iter), GET_ITER);
- }
USE_LABEL(c, start);
ADDOP_JUMP(c, LOC(gen->iter), FOR_ITER, anchor);
}
@@ -4544,9 +4541,9 @@ codegen_async_comprehension_generator(compiler *c,
location loc,
else {
/* Sub-iter - calculate on the fly */
VISIT(c, expr, gen->iter);
+ ADDOP(c, LOC(gen->iter), GET_AITER);
}
}
- ADDOP(c, LOC(gen->iter), GET_AITER);
USE_LABEL(c, start);
/* 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,
return SUCCESS;
}
+static inline int
+codegen_comprehension_iter(compiler *c, comprehension_ty comp)
+{
+ VISIT(c, expr, comp->iter);
+ if (comp->is_async) {
+ ADDOP(c, LOC(comp->iter), GET_AITER);
+ }
+ else {
+ ADDOP(c, LOC(comp->iter), GET_ITER);
+ }
+ return SUCCESS;
+}
+
static int
codegen_comprehension(compiler *c, expr_ty e, int type,
identifier name, asdl_comprehension_seq *generators,
expr_ty elt,
@@ -4776,9 +4786,10 @@ codegen_comprehension(compiler *c, expr_ty e, int type,
location loc = LOC(e);
outermost = (comprehension_ty) asdl_seq_GET(generators, 0);
- int is_sync_genexpr = type == COMP_GENEXP && !outermost->is_async;
if (is_inlined) {
- VISIT(c, expr, outermost->iter);
+ if (codegen_comprehension_iter(c, outermost)) {
+ goto error;
+ }
if (push_inlined_comprehension_state(c, loc, entry, &inline_state)) {
goto error;
}
@@ -4852,10 +4863,10 @@ codegen_comprehension(compiler *c, expr_ty e, int type,
}
Py_CLEAR(co);
- VISIT(c, expr, outermost->iter);
- if (is_sync_genexpr) {
- ADDOP(c, loc, GET_ITER);
+ if (codegen_comprehension_iter(c, outermost)) {
+ goto error;
}
+
ADDOP_I(c, loc, CALL, 0);
if (is_async_comprehension && type != COMP_GENEXP) {
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]