Transaction Context Manager
Hard · Context Managers
A context manager runs setup code before a with block and cleanup code after it, even when the block raises. With @contextlib.contextmanager you write it as a generator: the code before yield is the setup, and wrapping the yield in try/except lets you react to errors raised inside the block.
Write a context manager transaction(data) for a dict:
- on entry, save a copy of data (values are plain numbers and strings, so a shallow copy is enough)
- if the block finishes normally, keep the changes
- if the block raises, restore data to the saved state in place (the caller holds a reference to this same dict, so do not just build a new one), then let the exception continue
The driver run_transactions(data, batches) is already written. It runs each batch of operations inside with transaction(data):, records errors as "<Type>: <message>", and returns the final dict and the errors. Operations are ["set", key, value], ["del", key] (a missing key raises KeyError) and ["fail", message] (raises RuntimeError).