From 0e39bd3b293f7be1dbee4ec0edce47f696dba355 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 27 May 2025 09:19:45 +0100 Subject: [PATCH 1/3] Remove redundant GET_ITER from list comprehension code --- Lib/test/test_listcomps.py | 22 +++++++++++++++++++ ...-05-27-09-19-21.gh-issue-127682.9WwFrM.rst | 2 ++ Python/codegen.c | 19 ++-------------- 3 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-09-19-21.gh-issue-127682.9WwFrM.rst diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py index cffdeeacc5d73b..38a36d3c9925a2 100644 --- a/Lib/test/test_listcomps.py +++ b/Lib/test/test_listcomps.py @@ -750,6 +750,28 @@ def iter_raises(): self.assertEqual(f.line[f.colno - indent : f.end_colno - indent], expected) + def test_only_calls_dunder_iter_once(self): + + class Iterator: + + def __init__(self): + self.val = 0 + + def __next__(self): + if self.val == 2: + raise StopIteration + self.val += 1 + return self.val + + # No __iter__ method + + class C: + + def __iter__(self): + return Iterator() + + self.assertEqual([1,2], [i for i in C()]) + __test__ = {'doctests' : doctests} def load_tests(loader, tests, pattern): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-09-19-21.gh-issue-127682.9WwFrM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-09-19-21.gh-issue-127682.9WwFrM.rst new file mode 100644 index 00000000000000..ca0a694dfaaef8 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-09-19-21.gh-issue-127682.9WwFrM.rst @@ -0,0 +1,2 @@ +No longer call ``__iter__`` twice in list comprehensions. This brings the +behavior of list comprehensions in line with other forms of iteration diff --git a/Python/codegen.c b/Python/codegen.c index 683601103ec99d..1672b655fa9423 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -4543,9 +4543,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 */ @@ -4757,19 +4757,6 @@ 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, @@ -4789,9 +4776,7 @@ codegen_comprehension(compiler *c, expr_ty e, int type, outermost = (comprehension_ty) asdl_seq_GET(generators, 0); if (is_inlined) { - if (codegen_comprehension_iter(c, outermost)) { - goto error; - } + VISIT(c, expr, outermost->iter); if (push_inlined_comprehension_state(c, loc, entry, &inline_state)) { goto error; } From 7a05f9aa9874ac8737d9bdb4a0c522452fd7eef5 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 27 May 2025 11:29:42 +0100 Subject: [PATCH 2/3] Add test for async list comprehension --- Lib/test/test_coroutines.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 4755046fe1904d..ac9ecf1549a5dc 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2265,6 +2265,30 @@ def c(): # before fixing, visible stack from throw would be shorter than from send. self.assertEqual(len_send, len_throw) + def test_call_aiter_once_in_comprehension(self): + + class Iterator: + + def __init__(self): + self.val = 0 + + async def __anext__(self): + if self.val == 2: + raise StopAsyncIteration + self.val += 1 + return self.val + + # No __aiter__ method + + class C: + + def __aiter__(self): + return Iterator() + + async def run(): + return [i async for i in C()] + + self.assertEqual(run_async(run()), ([], [1,2])) @unittest.skipIf( support.is_emscripten or support.is_wasi, From 76ac2992033b43994aedb60b69e9e0e9616f9197 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 27 May 2025 11:30:50 +0100 Subject: [PATCH 3/3] Fix whitespace --- Lib/test/test_coroutines.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index ac9ecf1549a5dc..c8360a834482e1 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2290,6 +2290,7 @@ async def run(): self.assertEqual(run_async(run()), ([], [1,2])) + @unittest.skipIf( support.is_emscripten or support.is_wasi, "asyncio does not work under Emscripten/WASI yet."