[3.13] GH-127682: Backport GH-132351 (GH-132384)

Only call `__iter__` once in generator expressions
This commit is contained in:
Mark Shannon 2025-04-29 09:35:29 +01:00 committed by GitHub
parent 7ffef8d07b
commit 132bdf6990
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 27 additions and 14 deletions

View file

@ -184,7 +184,6 @@ def bug1333982(x=[]):
LOAD_CONST 1 (<code object <genexpr> at 0x..., file "%s", line %d>) LOAD_CONST 1 (<code object <genexpr> at 0x..., file "%s", line %d>)
MAKE_FUNCTION MAKE_FUNCTION
LOAD_FAST 0 (x) LOAD_FAST 0 (x)
GET_ITER
CALL 0 CALL 0
%3d LOAD_CONST 2 (1) %3d LOAD_CONST 2 (1)
@ -765,7 +764,6 @@ def foo(x):
MAKE_FUNCTION MAKE_FUNCTION
SET_FUNCTION_ATTRIBUTE 8 (closure) SET_FUNCTION_ATTRIBUTE 8 (closure)
LOAD_DEREF 1 (y) LOAD_DEREF 1 (y)
GET_ITER
CALL 0 CALL 0
CALL 1 CALL 1
RETURN_VALUE RETURN_VALUE

View file

@ -246,6 +246,28 @@ def loop():
#This should not raise #This should not raise
loop() loop()
def test_genexpr_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], list(i for i in C()))
class ModifyUnderlyingIterableTest(unittest.TestCase): class ModifyUnderlyingIterableTest(unittest.TestCase):
iterables = [ iterables = [

View file

@ -123,15 +123,6 @@
>>> list(g) >>> list(g)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Verify that the outermost for-expression makes an immediate check
for iterability
>>> (i for i in 6)
Traceback (most recent call last):
File "<pyshell#4>", line 1, in -toplevel-
(i for i in 6)
TypeError: 'int' object is not iterable
Verify late binding for the outermost if-expression Verify late binding for the outermost if-expression
>>> include = (2,4,6,8) >>> include = (2,4,6,8)

View file

@ -0,0 +1,4 @@
No longer call ``__iter__`` twice when creating and executing a generator expression.
Creating a generator expression from a non-interable will raise only when the
generator expression is executed.
This brings the behavior of generator expressions in line with other generators.

View file

@ -5910,9 +5910,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
} }
Py_CLEAR(co); Py_CLEAR(co);
if (compiler_comprehension_iter(c, outermost)) { VISIT(c, expr, outermost->iter);
goto error;
}
ADDOP_I(c, loc, CALL, 0); ADDOP_I(c, loc, CALL, 0);