A notebook's plotting settings are shared by every chart. One chart needs imperial units and five decimals, and the settings must be exactly as before once it is drawn, even if drawing fails. Write a context manager patched(target, **changes):
with patched(settings, units="imperial", precision=5):
draw_chart()
- If
targetis adict, the changes are to its keys; otherwise they are to its attributes. - On entering, each change is applied in the order given, and the block gets
targetitself (foras). - On leaving, however the block ends, every changed name gets back its old value, and names that did not exist before are removed again. Any exception from the block still reaches the caller.
- If applying a change fails (for example, an attribute that cannot be assigned raises
AttributeError), the changes already applied are undone, and then that error reaches the caller; the block does not run. - Restoring does not care what the block did with a patched name in the meantime: it is set back to its value from before the
with.
Blocks may be nested. Setup code available with Run: the classes Settings (with precision, units, debug and a state() method listing its attributes) and Panel (with a read-only area), label(settings, metres), and the scenarios basic(), failing_block(), environment(), nested(), read_only(), block_changes_too(), none_values() and many_blocks(n).
Examples
Input: basic()
Output: (("32.81 ft", True, True),
[("debug", False), ("precision", 2), ("units", "metric")],
False)
Input: environment()
Output: ([("HOME", "/home/ana"), ("LANG", "fr"), ("TZ", "UTC")],
[("HOME", "/home/ana"), ("LANG", "en")])
Constraints
- Names are valid identifiers, and the block never deletes a patched name.
- A value may be
None, which is an ordinary old value, not "did not exist".
Goals
- Change shared state for the duration of a `with` block and restore it afterwards
- Tell apart "had an old value" from "did not exist" with a sentinel
- Undo partial changes when setting up fails halfway