Bug report
Bug description:
For my Python compiler Nuitka, I use CPython as the oracle of what the correct behaviour is. I am running some tests that I used to clarify the behavior from decades ago, in this case I wanted to know when exactly the iterator creation is used. I striped this test a bunch, so the regression is still visible. I first noticed the issue on GitHub Actions, where 3.13.0 got replaced with 3.13.1 for Windows and Linux, but it applies to all OSes. See below for a diff, that the same iterator is created multiple times.
""" Generator expression tests """ from __future__ import print_function import inspect print("Generator expression that demonstrates the timing:") def iteratorCreationTiming(): def getIterable(x): print("Getting iterable", x) return Iterable(x) class Iterable: def __init__(self, x): self.x = x # pylint: disable=invalid-name self.values = list(range(x)) self.count = 0 def __iter__(self): print("Giving iterator now", self.x) return self def __next__(self): print("Next of", self.x, "is", self.count) if len(self.values) > self.count: self.count += 1 return self.values[self.count - 1] else: print("Raising StopIteration for", self.x) raise StopIteration # Python2/3 compatibility. next = __next__ def __del__(self): print("Deleting", self.x) gen = ((y, z) for y in getIterable(3) for z in getIterable(2)) print("next value is", next(gen)) res = tuple(gen) print("remaining generator is", res) try: next(gen) except StopIteration: print("Usage past end gave StopIteration exception as expected.") try: print("Generator state then is", inspect.getgeneratorstate(gen)) except AttributeError: pass print("Its frame is now", gen.gi_frame) print("Early aborting generator:") gen2 = ((y, z) for y in getIterable(3) for z in getIterable(2)) del gen2 iteratorCreationTiming()
The unified diff between 3.13.0 output (and basically all Python versions before) and 3.13.1 output.
--- out-3.13.0.txt 2024-12-06 12:37:19.447115100 +0100
+++ out-3.13.1.txt 2024-12-06 12:37:23.452239500 +0100
@@ -1,9 +1,11 @@
Generator expression that demonstrates the timing:
Getting iterable 3
Giving iterator now 3
+Giving iterator now 3
Next of 3 is 0
Getting iterable 2
Giving iterator now 2
+Giving iterator now 2
Next of 2 is 0
next value is (0, 0)
Next of 2 is 1
@@ -13,6 +15,7 @@
Next of 3 is 1
Getting iterable 2
Giving iterator now 2
+Giving iterator now 2
Next of 2 is 0
Next of 2 is 1
Next of 2 is 2
@@ -21,6 +24,7 @@
Next of 3 is 2
Getting iterable 2
Giving iterator now 2
+Giving iterator now 2
Next of 2 is 0
The duplicated prints out the iterator creation are new. This is not optimal and new. I don't know if the iterator being through a slot cause cause this or what it is. I checked if generator.c changed but I think it didn't at all.
My self compiled Python 3.13.1 for Linux and the official Windows download agree in behaviour.
CPython versions tested on:
3.13
Operating systems tested on:
Linux, Windows