Here's something that works a bit like Lisp's special variables, but fits a little better into Python.
_stack = []
class _EnvBlock(object):
def __init__(self, kwargs):
self.kwargs = kwargs
def __enter__(self):
_stack.append(self.kwargs)
def __exit__(self, t, v, tb):
_stack.pop()
class _Env(object):
def __getattr__(self, name):
for scope in reversed(_stack):
if name in scope:
return scope[name]
raise AttributeError("no such variable in environment")
def let(self, **kwargs):
return _EnvBlock(kwargs)
def __setattr__(self, name, value):
raise AttributeError("env variables can only be set using `with env.let()`")
env = _Env()
You can use it like this:
with env.let(bufsize=8192, encoding="ascii"):
print env.bufsize # prints 8192
a() # call a function that uses env.bufsize or env.encoding
The effects of env.let
last for the duration of the with
block.
Note that if you use threads, you'll definitely want a different _stack
for each thread. You could use threading.local to implement that.