Description
Feature or enhancement
Change the close()
method of generators to return the value of StopIteration
.
Pitch
If a generator handles the GeneratorExit
thrown by close()
, it can exit gracefully, raising StopIteration
with its return value.
def f():
try:
yield
except GeneratorExit:
pass
return 0
g = f()
g.send(None)
g.close() # StopIteration handled by close()
The StopIteration
is handled by close()
, but the return value is currently discarded, and close()
always returns None
.
The proposed change is to let close()
return the value of a StopIteration
it encounters after a graceful generator exit.
Every other case, including errors thrown between except GeneratorExit
and return
, is already handled by close()
and would remain unchanged. This includes repeated calls to close()
: Only the first such call might cause a generator to exit gracefully, after which it cannot raise StopIteration
any longer.
The change enables more natural use of generators as "pipelines" that process input data until ended. As a trivial example of the functionality, consider this computer of averages:
def averager():
n = 0
sum = 0.
while True:
try:
x = yield n
except GeneratorExit:
break
n += 1
sum += x
mean = sum/n
return mean
avg = averager()
avg.send(None)
for number in get_some_numbers():
avg.send(number)
mean = avg.close()
The generator processes data in an infinite loop. Once the controlling process terminates processing, the generator performs post-processing, and returns a final result. Without the return value of close()
, there is no intrinsic way of obtaining such a post-processed result.
Previous discussion
Discourse thread: Let generator.close() return StopIteration.value