A few comments on the latest PEP 380 version (rev11d)...
1) IIRC, the use of sys.exc_info() is not needed in 3.x as all exceptions have a __traceback__ attribute.
2) The expansion is not handling StopIterations raised as a result of calling _i.throw(). They should be treated just like any other StopIteration that ends the yield-from.
A simpler expansion based on 1) and 2) but otherwise identical is:
_i = iter(EXPR) try: _y = next(_i) while 1: try: _s = yield _y except GeneratorExit: _m = getattr(_i, 'close', None) if _m is not None: _m() raise except BaseException as _e: _m = getattr(_i, 'throw', None) if _m is not None: _y = _m(_e) else: raise else: if _s is None: _y = next(_i) else: _y = _i.send(_s) except StopIteration as _e: _r = _e.value RESULT = _r
3) If the subiterator has a close() but doesn't have throw() it won't be closed when throw() is called on the outer generator. This is fine with me, I am just not sure if it is intentional.
4) If the subiterator has a close() but doesn't have send() it won't be closed when a send() on the outer generator causes an AttributeError in the expansion. Again this is fine with me, I am just not sure if it is intentional.
5) The last paragraph in the "Use of StopIteration to return values" section, seems to be a leftover from an earlier draft of the PEP that used a different exception.
6) Several of the issues we have been discussing on python-ideas are not mentioned at all:
* The "initial next()" issue should at least be described and listed as out of scope. * The "what should close() do if it catches StopIteration with a value" issue I don't think we have resolved either way. Since we are not going to store the value, only the first close() would be able to return it. Under those conditions, I no longer think that returning the value is a good idea. If we are not storing or returning the value, I think close() should raise an exception. Either reraise the StopIteration, so that the caller has a chance to get the value that way, or raise a RuntimeError, because it is meaningless to return a value as response to a GeneratorExit when that value cannot later be accessed by anything and it is therefore most likely a bug. * The special-casing of StopIteration should probably be mentioned as a rejected idea. Not special-casing it does break the refactoring principle, and I think it important to mention that in some way. * There may be other issues I have forgotten at the moment.
7) By not mentioning caching, you are effectively saying the methods won't be cached. I have exactly one use for this. The fastest pure-python "full" workaround I can find for the "initial next()" issue is a wrapper using Nicks self-modifying class hack. With this the delegation cost is less than 1/3 of any other approach I have tried. (But still 13 times higher than a yield-from without the wrapper when using your patch). All that means is that adding caching later would be likely to break some code that relied on the exact semantics as described in the PEP.
Other than that, everything looks fine.
Best regards - Jacob