From: Mark Shannon Date: Tue, 27 May 2025 13:12:04 +0000 (+0100) Subject: [3.13] gh-128161: Remove redundant GET_ITER from list comprehension code (backport... X-Git-Tag: v3.13.4~36 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=814ac0d58789fd544855a6c1afb9d89a690a0c3b;p=thirdparty%2FPython%2Fcpython.git [3.13] gh-128161: Remove redundant GET_ITER from list comprehension code (backport of GH-134778) (GH-134788) --- diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index f705f4f5bfbd..77cdbf98067e 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2252,6 +2252,31 @@ class CoroutineTest(unittest.TestCase): # 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 45644d6c0927..23fa5ef4f438 100644 --- a/Lib/test/test_listcomps.py +++ b/Lib/test/test_listcomps.py @@ -750,6 +750,28 @@ class ListComprehensionTest(unittest.TestCase): 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 000000000000..ca0a694dfaae --- /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 e8d19d2dc427..dba10237a2a7 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; }