[Python-Dev] PEP 479: Change StopIteration handling inside generators

Isaac Schwabacher ischwabacher at wisc.edu
Wed Nov 26 17:24:24 CET 2014


On 11/26/14, Nick Coghlan  wrote:
> On 26 November 2014 at 04:04, Guido van Rossum <guido at python.org> wrote:
> > On Tue, Nov 25, 2014 at 9:49 AM, Chris Angelico <rosuav at gmail.com> wrote:
> >>
> >> On Wed, Nov 26, 2014 at 4:45 AM, Isaac Schwabacher
> >> <ischwabacher at wisc.edu> wrote:
> >> > Yield can also raise StopIteration, if it's thrown in. The current
> >> > interaction of generator.throw(StopIteration) with yield from can't be
> >> > emulated under the PEP's behavior, though it's not clear that that's a
> >> > problem.
> >> >
> >>
> >> Hrm. I have *absolutely* no idea when you would use that, and how
> >> you'd go about reworking it to fit this proposal. Do you have any
> >> example code (production or synthetic) which throws StopIteration into
> >> a generator?
> >
> >
> > Sounds like a good one for the obfuscated Python contest. :-)
> >
> > Unless the generator has a try/except surrounding the yield point into which
> > the exception is thrown, it will bubble right out, and PEP 479 will turn
> > this into a RuntimeError. I'll clarify this in the PEP (even though it
> > logically follows from the proposal) -- I don't think there's anything to
> > worry about.
> 
> This is actually the edge case that needs to be handled in contextlib
> - a StopIteration raised by the with statement body gets thrown into
> the generator implementing the context manager. My current porting
> recommendation is to catch the RuntimeError & look at __cause__ to see
> if it's the StopIteration instance that was thrown in, but an
> alternative option would be to just call gen.close() in that case,
> rather than gen.throw(exc).

This actually leads to a good example of why the PEP is necessary:

```
In [1]: import contextlib

In [2]: @contextlib.contextmanager
 ...: def transact():
 ...: print('setup transaction')
 ...: try:
 ...: yield from subgenerator()
 ...: except:
 ...: print('rollback transaction')
 ...: raise
 ...: else:
 ...: print('commit transaction')
 ...: finally:
 ...: print('clean up transaction')
 ...: 

In [3]: def subgenerator():
 ...: print('setup subgenerator')
 ...: try:
 ...: yield
 ...: except:
 ...: print('subgenerator failed')
 ...: raise
 ...: else:
 ...: print('subgenerator succeeded')
 ...: finally:
 ...: print('clean up subgenerator')
 ...: 

In [4]: with transact():
 ...: next(iter([]))
 ...: 
setup transaction
setup subgenerator
subgenerator failed
clean up subgenerator
commit transaction # BAD NOT GOOD BIG FAIL
clean up transaction
```

ijs


More information about the Python-Dev mailing list