Jacob Holm wrote:
Greg Ewing wrote:
Jacob Holm wrote:
But if you throw another exception and it is converted to a StopIteration by the subiterator, this should definitely stop the subiterator and get a return value.
Not if it simply raises a StopIteration from the throw call. It would have to mark itself as completed, return normally from the throw and then raise StopIteration on the next call to next() or send().
One of us must be missing something... If the subiterator is exhausted before the throw, there won't *be* a value to return from the call so the only options for the throw method are to raise StopIteraton, or to raise some other exception.
I agree with Jacob here - contextlib.contextmanager contains a similar check in its __exit__ method. The thing to check for is the throw method call raising StopIteration and that StopIteration instance being a *different* exception from the one that was thrown in. (This matters more in the contextmanager case, since it is quite legitimate for a generator to finish and raise StopIteration from inside a with statement, so the contextmanager needs to avoid accidentally suppressing that exception). Avoiding the problem of suppressing thrown in StopIteration instances means we still need multiple inner try/except blocks rather than a large outer one. There is also another special case to consider: since a permitted response to "throw(GeneratorExit)" is for the iterator to just terminate instead of reraising GeneratorExit, the thrown in exception should be reraised unconditionally in that situation. So the semantics would then become: _i = iter(EXPR) try: _u = _i.next() except StopIteration as _e: _r = _e.value else: while 1: try: _v = yield _u except: _m = getattr(_i, 'throw', None) if _m is not None: _et, _ev, _tb = sys.exc_info() try: _u = _m(_et, _ev, _tb) except StopIteration as _e: if _e is _ev or _et is GeneratorExit: # Don't suppress a thrown in # StopIteration and handle the # case where a subiterator # handles GeneratorExit by # terminating rather than # reraising the exception raise # The thrown in exception # terminated the iterator # gracefully _r = _e.value else: raise else: try: if _v is None: _u = _i.next() else: _u = _i.send(_v) except StopIteration as _e: _r = _e.value break RESULT = _r -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------