Skip to content

Commit d87e7f3

Browse files
authored
GH-127682: Only call __iter__ once in generator expressions. (GH-132351)
1 parent bc0b94b commit d87e7f3

File tree

5 files changed

+27
-15
lines changed

5 files changed

+27
-15
lines changed

Lib/test/test_dis.py

-2
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ def bug1333982(x=[]):
204204
LOAD_CONST 1 (<code object <genexpr> at 0x..., file "%s", line %d>)
205205
MAKE_FUNCTION
206206
LOAD_FAST_BORROW 0 (x)
207-
GET_ITER
208207
CALL 0
209208
210209
%3d LOAD_SMALL_INT 1
@@ -821,7 +820,6 @@ def foo(x):
821820
MAKE_FUNCTION
822821
SET_FUNCTION_ATTRIBUTE 8 (closure)
823822
LOAD_DEREF 1 (y)
824-
GET_ITER
825823
CALL 0
826824
CALL 1
827825
RETURN_VALUE

Lib/test/test_generators.py

+22
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,28 @@ def loop():
268268
#This should not raise
269269
loop()
270270

271+
def test_genexpr_only_calls_dunder_iter_once(self):
272+
273+
class Iterator:
274+
275+
def __init__(self):
276+
self.val = 0
277+
278+
def __next__(self):
279+
if self.val == 2:
280+
raise StopIteration
281+
self.val += 1
282+
return self.val
283+
284+
# No __iter__ method
285+
286+
class C:
287+
288+
def __iter__(self):
289+
return Iterator()
290+
291+
self.assertEqual([1,2], list(i for i in C()))
292+
271293

272294
class ModifyUnderlyingIterableTest(unittest.TestCase):
273295
iterables = [

Lib/test/test_genexps.py

-9
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,6 @@
123123
>>> list(g)
124124
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
125125
126-
Verify that the outermost for-expression makes an immediate check
127-
for iterability
128-
129-
>>> (i for i in 6)
130-
Traceback (most recent call last):
131-
File "<pyshell#4>", line 1, in -toplevel-
132-
(i for i in 6)
133-
TypeError: 'int' object is not iterable
134-
135126
Verify late binding for the outermost if-expression
136127
137128
>>> include = (2,4,6,8)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
No longer call ``__iter__`` twice when creating and executing a generator expression.
2+
Creating a generator expression from a non-interable will raise only when the
3+
generator expression is executed.
4+
This brings the behavior of generator expressions in line with other generators.

Python/codegen.c

+1-4
Original file line numberDiff line numberDiff line change
@@ -4775,10 +4775,7 @@ codegen_comprehension(compiler *c, expr_ty e, int type,
47754775
}
47764776
Py_CLEAR(co);
47774777

4778-
if (codegen_comprehension_iter(c, outermost)) {
4779-
goto error;
4780-
}
4781-
4778+
VISIT(c, expr, outermost->iter);
47824779
ADDOP_I(c, loc, CALL, 0);
47834780

47844781
if (is_async_comprehension && type != COMP_GENEXP) {

0 commit comments

Comments
 (0)