diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 4755046fe1904d..c8360a834482e1 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2265,6 +2265,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 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; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy