I am trying to write a context manager function. It "works", but I have noticed that the __repr__ method from the context continues to fire even after the "with" has exited.
Here is the TimedContext class:
import time
class TimedContext:
def __init__(self):
self.start = self.now = self.duration = 0
def __enter__(self):
self.start = time.time()
return self
def __format__(self, spec):
self._update_duration()
return format(self.duration, spec)
def __exit__(self, type_, value, traceback):
self._update_duration()
return traceback is None
def _update_duration(self):
self.now = time.time()
self.duration = self.now - self.start
Here is me using it:
from TimedContext import TimedContext
import time
with TimedContext() as t:
time.sleep(1)
print(f"Time0: {t}")
print(f"Time1: {t}")
time.sleep(1)
print(f"Time2: {t}")
Here is my output running the script:
Time0: 1.001316785812378
Time1: 1.0014879703521729
Time2: 2.0025713443756104
I had expected Time2 to be the same as Time1 since I expected the end of the with completed the context so that t would now just be a variable containing the final return from the context. (do I have my wording right here?)
The reason I am updating the TimedContext within the __format__ function is that I want to be able to reference t from both inside and outside the "with". If I did not update here, t would be correct after the __exit__ was run, but inside the with, it would just be the __init__ value.
I can fix this "issue" by creating a boolean exit_called, setting it to False on __init__ and True on __exit__ and then only update self.duration if exit_called is False. While this "works", I was expecting the context to be gone since the with completed and __exit__ has run to completion. However, it appears the context sticks around. Is it deterministic how long? When does the context get cleaned up? I had assumed the closing of the with did that. Is there something I need to do to allow the context to be cleaned up, or do I need to explicitly do it?
I'd like to understand what should be going on here. I wasn't expecting the context to stick around once __exit__ completes. Maybe I should be addressing my problem a different way?
From Unraveling the
withstatement:withdoesn't do any cleanup of its own, it calls the__exit__()method to do that. It also doesn't delete thebvariable -- it still contains the value that was assigned byas b.For example, when you use
fwill still contain the file object, but it will be closed and unusable because its__exit__()method closes the file and reclaims any resources.This is no different from other constructs that assign variables for use in a block. For instance, the variable in a
forstatement can be used after the loop is done -- it will contain the value from the last iteration.The context manager object itself will eventually be cleaned up by the garbage collector, when the variable goes out of scope.