Raymond Hettinger wrote:
While Guido is thinking, could one of the proponents please enumerate the reasons for treating GeneratorExit like KeyboardInterrupt and SystemExit.
To me, they obviously should be under Exception, and not treated like KeyboardInterrupt or SystemExit, so that probably means that I'm missing something about GeneratorExit.
If GeneratorExit *isn't* special, then correct catch-all exception handling around a yield expression in a generator looks like:
def run_tasks(tasks, failed_tasks) for task in tasks: try: yield task() except GeneratorExit: # don't break gen.close() raise except Exception: failed_tasks.append((task, sys.exc_info()))
Having to explicitly reraise a terminating exception like that is the exact idiom we're trying to get rid of for SystemExit and KeyboardInterrupt, so it makes sense to me to avoid it for GeneratorExit as well.
Given that the default system-level excepthook *won't* ignore GeneratorExit (and so an error message will still be printed), I see moving it out with the other two terminating exceptions as the best option. The only way for that approach to do the wrong thing is if GeneratorExit is raised outside a generator's pseudothread, at which point I'd be blaming the code that raised it explicitly (the interpreter core only ever raises it as a result of a call to gen.close()).
Note that this argument does *not* apply to StopIteration - that should stay under Exception because it should never accidentally leak beyond the code that is calling the next() method.