Nick Coghlan wrote:
Jacob Holm wrote:
Explicitly catching GeneratorExit and then returning is a valid use today that I wouldn't consider suspect. Catching GeneratorExit and then exiting the except block by other means than a raise or return is suspect, but has valid uses.
What are these valid uses? The PEP 342 definition made some sense originally when GeneratorExit was a subclass of Exception and hence easy to suppress accidentally, but I have serious doubts about the validity of trapping it and turning it into StopIteration now that it has been moved out to inherit directly from BaseException.
When catching and returning, the control flow is different than if you were catching and raising. Also using "return" more clearly signals the intent to leave the generator than "raise". Even with GeneratorExit inheriting directly from BaseException, it is still easier to intercept an exception than a return. The use for catching and exiting the block normally is to share some code between the cases. Of course you need to be careful when you do that, but it can save some duplication. Both these uses are valid in the sense that the generators work as advertised and follow the rules of finalization as defined by PEP 342. I don't think a proposal for changing close to not accept StopIteration is going to fly.
Regardless, unless Greg goes out of his way to change the meaning of close() in the PEP, GeneratorReturn will escape from close() (since that only traps StopIteration). That means you'll be able to catch that exception directly if you really want to, and if you don't it will bubble up out of the original close() call that was made on the outermost generator.
Hmm. I had almost forgotten about the separate GeneratorReturn exception. It would be good to see how that changes things. So far I consider it a needless complication, but I would like to read a version of the PEP that include it to see how bad it is. As for GeneratorReturn not being caught by close(), I find it really strange if returning a non-None value as a response to GeneratorExit makes close() raise a GeneratorReturn. Whereas returning None makes close finish without an exception. If you think returning a non-None value is an error, we should make it a (subclass of) RuntimeError rather than a GeneratorReturn to clearly indicate this. I am strongly in favor of changing close to return the value rather than letting the GeneratorReturn pass through or raising a RuntimeError. I think the "averager" example I just gave is a good (but simplistic) example of the kind of code I would consider using coroutines for. The need to catch an exception would make that code a lot less readable, not to mention slower. I am not about to write a separate PEP for this, but I would consider "return from generator" plus "close returns value returned from generator" to be a worthwhile addition in itself. - Jacob