[Python-Dev] PEP 479

Nick Coghlan ncoghlan at gmail.com
Sun Nov 30 10:40:44 CET 2014


On 30 November 2014 at 12:31, Chris Angelico <rosuav at gmail.com> wrote:
> On Sun, Nov 30, 2014 at 1:04 PM, Jim J. Jewett <jimjjewett at gmail.com> wrote:
>> (4)  It can happen because of "yield from" yielding from an iterator,
>> rather than a generator?
>
> No; as I understand it (though maybe I'm wrong too), "yield from" will
> yield every value the other iterator yields, and will then quietly
> emit a value if the iterator raises StopIteration, or will allow any
> other exception to propagate. The StopIteration coming from the
> iterator is absorbed by the "yield from" construct. To completely
> propagate it out, "return (yield from iter)" should cover all three
> results (yielded value, returned value, raised exception).

Chris is correct here. The key yield from related change due to PEP
479 is to eliminate a subtle difference in the interaction between
generators and StopIteration that currently exists when delegating to
a subgenerator.

If you throw StopIteration directly into a suspended generator, it
propagates back out:

>>> def gen1():
...     yield 1
...     yield 2
...
>>> g = gen1()
>>> next(g)
1
>>> g.throw(StopIteration)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in gen1
StopIteration

However, if the generator is instead suspended inside a
*subgenerator*, then the thrown in exception will terminate the
subgenerator, and execution of the delegating generator will resume:

>>> def gen_inner():
...    yield 1
...
>>> def gen_outer():
...     yield from gen_inner()
...     yield 2
...
>>> g2 = gen_outer()
>>> next(g2)
1
>>> g2.throw(StopIteration)
2

Under PEP 479, the StopIteration thrown in StopIteration will be
turned into RuntimeError regardless of whether or not a subgenerator
is involved - the difference in the subgenerator case is that the
transformation will happen when the innermost generator is terminated,
and it will then appear as RuntimeError in the delegating generator.

This change in semantics will make it possible to fix a latent defect
in contextlib.contextmanager, where using a subgenerator as part of
the context manager implementation may currently lead to inadvertently
suppressing StopIteration in the body of a covered with statement.

Regards,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list