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
__exit__() method This is a method of ContextManager class. The __exit__ method takes care of releasing the resources occupied with the current code snippet. This method must be executed no matter what after we are done with the resources.
A context manager usually takes care of setting up some resource, e.g. opening a connection, and automatically handles the clean up when we are done with it. Probably, the most common use case is opening a file. The code above will open the file and will keep it open until we are out of the with statement.
__exit__() automatically gets called and closes the file for you, even if an exception is raised inside the with block.
__enter__() is provided which returns self while object. __exit__() is an abstract method which by default returns None .
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()
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
>>>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With