]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] GH-135171: Revert async generator expressions behavior (#135352)
authorMikhail Efimov <efimov.mikhail@gmail.com>
Mon, 16 Jun 2025 14:45:42 +0000 (17:45 +0300)
committerGitHub <noreply@github.com>
Mon, 16 Jun 2025 14:45:42 +0000 (17:45 +0300)
Lib/test/test_asyncgen.py
Lib/test/test_coroutines.py
Lib/test/test_generators.py
Lib/test/test_listcomps.py
Misc/NEWS.d/next/Core_and_Builtins/2025-06-10-17-37-11.gh-issue-135171.P9UDfS.rst [new file with mode: 0644]
Python/codegen.c

index 2c44647bf3e2f97ee95b86a09c22465cfa4394de..2f2865bad2cdb1fc15bc7e8a62ef45f94bb6ad80 100644 (file)
@@ -1835,6 +1835,20 @@ class AsyncGenAsyncioTest(unittest.TestCase):
         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 = []
index c8360a834482e1d067711fa1811b7c11b1b336e6..954003ab14df9394ef9ab1ee3e99892648f1ccf5 100644 (file)
@@ -2267,7 +2267,7 @@ class CoroutineTest(unittest.TestCase):
 
     def test_call_aiter_once_in_comprehension(self):
 
-        class Iterator:
+        class AsyncIterator:
 
             def __init__(self):
                 self.val = 0
@@ -2283,12 +2283,17 @@ class CoroutineTest(unittest.TestCase):
         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(
index 6c056b63f88480b354fab126156e8d0eb9cfaed6..cb73179160d10d559565fd3b228f2ff38315d29b 100644 (file)
@@ -288,7 +288,7 @@ class GeneratorTest(unittest.TestCase):
             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):
index 38a36d3c9925a20c5f9b7d539ef3c023ef458f54..ef8f3d492dfc46d86e36fca5622e4196a741b52f 100644 (file)
@@ -770,7 +770,7 @@ class ListComprehensionTest(unittest.TestCase):
             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 (file)
index 0000000..ae2731d
--- /dev/null
@@ -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.
index 93c3a21266cb7d7f85ad4ffb98ebe7d3b17a71fa..d708e59ed67bafd4098b5ddeedc1deee567e5b5a 100644 (file)
@@ -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) {