Search code examples
pythonruntime-errorstopiteration

Python PEP479 Change StopIteration handling inside generators


Could someone help me understand what PEP479 is about? I was reading the doc and couldn't get my head around it.

The abstract says:

This PEP proposes a change to generators: when StopIteration is raised inside a generator, it is replaced it with RuntimeError. (More precisely, this happens when the exception is about to bubble out of the generator's stack frame.)

So for example, does a loop like so still work?

it = iter([1,2,3])
try:
    i = next(it)
    while True:
        i = next(it)
except StopIteration:
    pass

Or does it mean that if I have a generator definition like so:

def gen():
    yield from range(5)
    raise StopIteration

the StopIteration is going to be replaced with RuntimeError?

I would really appreciate if someone could shed some light on this.


Solution

  • Your first loop should still work -- StopIteration will still be raised when a generator is exhausted.

    The difference is that there was ambiguity when a StopIteration was raised in a generator. Did it get raised (implicitly) because the generator ran out of things to yield -- Or did it get raised because a delegate generator ran out of things to yield (maybe due to a next call) and the exception wasn't handled properly? PEP-0479 tries to address that ambiguity. Now if you get a StopIteration, it means that the generator you are working with ran out items to yield. Said another way, it means that a delegate generator didn't get mis-handled when running out of items.

    To support this change, your generator should return instead of raising StopIteration explicitly.

    def gen():
        yield from range(5)
        return
    

    Here's what happens if you try it with the StopIteration and generator_stop enabled (which will become the default when python3.7 comes around):

    >>> from __future__ import generator_stop
    >>> def gen():
    ...     yield from range(5)
    ...     raise StopIteration
    ... 
    >>> list(gen())
    Traceback (most recent call last):
      File "<stdin>", line 3, in gen
    StopIteration
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    RuntimeError: generator raised StopIteration