On 2 November 2014 19:53, Nick Coghlan
On 2 November 2014 02:50, Guido van Rossum
wrote: I think you're on to something. But I think both your examples have a problem, even though your second one "works".
If we weren't forced by backward compatibility I would have made it much harder for StopIteration to "leak out". Currently a generator can either return or raise StopIteration to signal it is done, but I think it would have been better if StopIteration was treated as some kind of error in this case. Basically I think any time a StopIteration isn't caught by a for-loop or an explicit try/except StopIteraton, I feel there is a bug in the program, or at least it is hard to debug.
I'm afraid that ship has sailed, though...
The closest existing example of this kind of generator instance specific StopIteration handling that I can think of is the special case handling of StopIteration in contexlib._GeneratorContextManager.__exit__() (https://hg.python.org/cpython/file/3.4/Lib/contextlib.py#l63). There, the exception handling differentiates between "a specific StopIteration instance that we just threw into the subgenerator" (which it will allow to propagate) and "any other StopIteration instance, which indicates that the wrapped generator iterator terminated as expected" (which it will suppress). We had that wrong initially - if I recall correctly, it was PJE that noticed the problem before 2.5 was released. However, the only reason we were able to make it work is that we knew the exact identity of the exception we were throwing in, rather than just its type - we don't have that luxury in the general case.
Getting back to the behaviour that prompted the thread, like a lot of exception handling quirks, it gets back to being very careful about the scope of exception handlers. In this case, the "next(it)" call is inside a generator expression, and hence inside the scope of the expression's StopIteration handling. By contrast, the comprehension version doesn't *have* any implicit exception handling, so the StopIteration escapes to terminate the containing generator.
Bah, I should have fully read Terry's reply before responding. He's right, it's the tuple call that's suppressing the exception, not the generator expression itself. That changes the possible solution, by tweaking it to be an optional extension to the iterator protocol, allowing iterators to make the terminating exception configurable. def __setiterexc__(exc): """Specify the exception instance to raise when the iterator is exhausted""" ... Iterator consumers (like tuple) could then check for that method and use it to set a specific StopIteration instance, allowing all others to escape. I believe actually doing this would be adding too much complexity for too little gain, but it *is* possible. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia