From 9000bd4321946d74720ab86c16a916f10f257aed Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 27 May 2025 12:29:48 +0100 Subject: [PATCH] [3.14] GH-128161: Remove redundant GET_ITER from list comprehension code (GH-134778) --- Lib/test/test_coroutines.py | 25 +++++++++++++++++++ Lib/test/test_listcomps.py | 22 ++++++++++++++++ ...-05-27-09-19-21.gh-issue-127682.9WwFrM.rst | 2 ++ Python/compile.c | 19 ++------------ 4 files changed, 51 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_coroutines.py b/Lib/test/test_coroutines.py index f705f4f5bfbd88..77cdbf98067e8c 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2252,6 +2252,31 @@ 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, diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py index 45644d6c092782..23fa5ef4f43810 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/compile.c b/Python/compile.c index e8d19d2dc42744..dba10237a2a735 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5501,9 +5501,9 @@ compiler_async_comprehension_generator(struct 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 */ @@ -5790,19 +5790,6 @@ pop_inlined_comprehension_state(struct compiler *c, location loc, return SUCCESS; } -static inline int -compiler_comprehension_iter(struct 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 compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, asdl_comprehension_seq *generators, expr_ty elt, @@ -5824,9 +5811,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, outermost = (comprehension_ty) asdl_seq_GET(generators, 0); if (is_inlined) { - if (compiler_comprehension_iter(c, outermost)) { - goto error; - } + VISIT(c, expr, outermost->iter); if (push_inlined_comprehension_state(c, loc, entry, &inline_state)) { goto error; }