Today, when i wrote another context manager, i came up with the idea – write let
statement in python using context manager.
First i invented simple realisation, where we manually pass locals:
from contextlib import contextmanager
@contextmanager
def let(locals_, **bindings):
original = {var: locals_.get(var) for var in bindings.keys()}
locals_.update(bindings)
yield
locals_.update(original)
And usage:
>>> a = 1
>>> b = 2
>>> with let(locals(), a=10, b=20):
... print(a, b) # inside `let` scope
...
(10, 20)
>>> print(a, b) # outside of `let` scope
(1, 2)
Looks ugly. But we can use little piece of magic with inspect. We can get outer frame and get his locals
from contextlib import contextmanager
from inspect import currentframe, getouterframes
@contextmanager
def let(**bindings):
frame = getouterframes(currentframe(), 2)[-1][0] # 2 because first frame in `contextmanager` decorator
locals_ = frame.f_locals
original = {var: locals_.get(var) for var in bindings.keys()}
locals_.update(bindings)
yield
locals_.update(original)
Now we don’t need to pass locals explicitly:
>>> a = 3
>>> b = 4
>>> with let(a=33, b=44):
... print(a, b)
...
(33, 44)
>>> print(a, b)
(3, 4)