Context managers
with guarantees cleanup, whichever way a block ends. An object supports it by defining __enter__ and __exit__.
class Timer:
def __enter__(self):
return self
def __exit__(self, kind, err, tb):
print("done")
return False # do not swallow__exit__ receives details of any exception. A true return value suppresses it, so usually return False.
The short form
contextlib.contextmanager turns a generator into a context manager. Code before yield is the setup. Code after it is the cleanup.
from contextlib import contextmanager
@contextmanager
def tag(out, name):
out.append(f"<{name}>")
try:
yield
finally:
out.append(f"</{name}>")The try/finally matters. An exception inside the with block is raised at the yield, so without finally your cleanup would be skipped.
Your task
Write a context manager temporary_setting(settings, key, value) that sets settings[key] = value for the block, then restores the old value, or removes the key if it did not exist before. It must clean up after an exception and let the exception propagate.
The explanation opens here once every check passes.