[Python-Dev] GeneratorExit is unintuitive and uneccessary

Igor Bukanov igor.bukanov at gmail.com
Tue Aug 22 15:17:20 CEST 2006


Consider the following example:

for i in range(3):
  try:
    print i
    break
  except:
    print "Unexpected exception!"
  finally:
    print "Finally"

When executed, it naturally prints
  0
  Finally
since break does not use exceptions to transfer the control and as
such can not be stopped using catch-all exception handler.

Now consider a similar example using generators:

def gen():
  for i in range(3):
    try:
      yield i
    except:
      print "Unexpected exception!"
    finally:
      print "Finally"

for i in gen():
  print i
  break

This example prints:
  0
  Unexpected exception!
  Finally
  Exception exceptions.RuntimeError: 'generator ignored GeneratorExit'
in <generator object at 0xb7daaa8c> ignored

Suddenly with generators a program can mess with control transfer
since it uses explicit GeneratorExit which can be caught and ignored.
This is unintuitive IMO.

This example also suggests how to fix generators. One just need to
change the close method so it would cause return executed right after
the yield instead of throw.

I.e. replace the current text from
http://www.python.org/dev/peps/pep-0342/

4. Add a close() method for generator-iterators, which raises
GeneratorExit at the point where the generator was paused.  If the
generator then raises StopIteration (by exiting normally, or due to
already being closed) or GeneratorExit (by not catching the
exception), close() returns to its caller.  If the generator yields a
value, a RuntimeError is raised.  If the generator raises any other
exception, it is propagated to the caller. close() does nothing if the
generator has already exited due to an exception or normal exit.

by simpler one:

4. Add a close() method for generator-iterators, which executes normal
return at the point where the generator was paused.  If the generator
then raises StopIteration (by exiting normally, or due to already
being closed), close() returns to its caller.  If the generator yields
a value, a RuntimeError is raised.  If the generator raises any other
exception, it is propagated to the caller. close() does nothing if the
generator has already exited due to an exception or normal exit.

This not only fixes the above discrepancy between normal flow control
and generators, removes GeneratorExit and simplifies the generator
protocol, but also bring a new feature allowing to have easy to grasp
feature table of the iterator methods:

next: continue after yield
throw: raise after yield
close: return after yield

Regards, Igor


More information about the Python-Dev mailing list