How can I override global variables just for the scope of callees of a function in Python?

Viewed 43

I'm writing a decorator which needs to pass data to other utility functions; something like:

STORE = []
def utility(message):
  STORE.append(message)

def decorator(func):
  def decorator_wrap(*args, **kwargs):
    global STORE
    saved_STORE = STORE
    STORE = list()
    func(*args, **kwargs)
    for line in STORE:
      print(line)
    STORE = saved_STORE
  return decorator_wrap

@decorator
def foo(x):
  # ...
  utility(x)
  # ...

But that's kind of yuck, and not thread safe. Is there a way to override utility()'s view of STORE for the duration of decorator_wrap()? Or some other way to signal to utility() that there's an alternate STORE it should use?

Alternatively, to present an different utility() to foo() and all its callees; but that seems like exactly the same problem.

1 Answers

From this answer I find that I can implement it this way:

import inspect

STORE = []

def utility(message):
  global STORE
  store = STORE
  frame = inspect.currentframe()
  while frame:
    if 'LOCAL_STORE' in frame.f_locals:
      store = frame.f_locals['LOCAL_STORE']
      break;
    frame = frame.f_back

  store.append(message)

def decorator(func):
  def decorator_wrap(*args, **kwargs):
    LOCAL_STORE = []
    func(*args, **kwargs)
    for line in LOCAL_STORE:
      print(line)
  return decorator_wrap

Buuuut while reading the documentation I see f_globals is present in every stack frame. I think the more efficient method would be to inject my local into my callee's f_globals. This would be similar to setting an environment variable before executing another command, but I don't know if it's legal.

Related