Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation of generator.close() with exception handling

I am reading the python doc https://docs.python.org/3/reference/expressions.html about generator.close().

My translation of the documentation is:

##generator.close()

Raises a GeneratorExit at the point where the generator function was paused.

  1. If the generator function then exits gracefully :

1.1 is already closed,
1.2 or raises GeneratorExit (by not catching the exception),

close returns to its caller.

  1. If the generator yields a value, a RuntimeError is raised.

  2. 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.


I don't understand how the close() behavior corresponds to the documentation.

>>> def echo(value=None):
...     print("Execution starts when 'next()' is called for the first time.")
...     try:
...         while True:
...             try:
...                 value = (yield value)
...             except Exception as e:
...                 value = e
...     finally:
...         print("Don't forget to clean up when 'close()' is called.")
...
>>> generator = echo(1)
>>> next(generator)
Execution starts when 'next()' is called for the first time.
>>> generator.close()
Don't forget to clean up when 'close()' is called.

Which rule applies to generator.close() ? I am confused.

My understanding:

  1. generator.close() raise a GeneratorExit exception
  2. GeneratorExit is catched by except Exception as e: and loop continues
  3. value = (yield value) executes
  4. according to rule 2 above, a RuntimeError will be raised.

But that doesn't seem to be the case.

Please tell me what's going on inside.

like image 972
Rick Avatar asked Feb 09 '20 14:02

Rick


1 Answers

GeneratorExit does not inherit from Exception, but from the more fundamental BaseException. Thus, it is not caught by your except Exception block.

So your assumption 2 is wrong. The generator exits gracefully via case 1.3, since GeneratorExit is not stopped.

  1. The GeneratorExit is thrown at (yield value).
  2. The try: except Exception as e: checks whether the current exception is subclass of Exception. Since this is not the case, it unwinds.
  3. The while True: unwinds due the current exception.
  4. The try: finally: unwinds, running its finally: block. This causes the message to be displayed.
  5. The generator exits with the current exception, i.e. GeneratorExit.
  6. generator.close detects and suppresses GeneratorExit.
like image 53
MisterMiyagi Avatar answered Sep 27 '22 16:09

MisterMiyagi