Question

I'm making something like a task scheduler using generators as coroutines. In the below code, I need to execute the print cleanup deterministically.

It seems from my interaction that releasing the object to the garbage collector causes the context manager to exit. But, I know better than to rely on the timing of a GC. Is it really the GC calling the __exit__ or is it another mechanism?

How can I strictly force print 'cleanup'?

>>> from contextlib import contextmanager
>>> @contextmanager
... def foo():
...     print 'setup'
...     try:
...         yield
...     finally:
...         print 'cleanup'
... 
>>> def bar():
...     with foo():
...         while True:
...             yield 'bar'
... 
>>> b = bar()
>>> b.next()
setup
'bar'
>>> b = None
cleanup
Was it helpful?

Solution

Yes, the GC is calling the __del__ cleanup hook of the generator, which in turn raises a GeneratorExit in the generator function to exit the generator (by calling generator.close()).

This means the context manager __exit__ hook will be called whenever a generator function is cleared from memory.

You can manually close the generator yourself first, with generator.close():

b.close()

OTHER TIPS

You have to cause the generator to exit. If the nature of the generator is to look forever, you can use gen.throw() to raise an exception in the generator.

Actually, I just looked at the specification for generators, and they have a method close() that does exactly this (it raises a GeneratorExit() exception inside the generator. So just call gen.close() when you are done with it any any context managers will invoke their exit methods. The generator will eat the exception so you don't need to wrap the close() call in a try block:

>>> b= bar()
>>> b.next()
setup
'bar'
>>> b.close()
cleanup
>>>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top