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)