[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