Skip to content

Instantly share code, notes, and snippets.

@betatim
Created April 2, 2019 11:14
Show Gist options
  • Save betatim/60c88eafc387872e50e4b6f7fd06840a to your computer and use it in GitHub Desktop.
Save betatim/60c88eafc387872e50e4b6f7fd06840a to your computer and use it in GitHub Desktop.
Can we inject arguments from the caller's locals into callees? Yes! Run `python injector.py`
import inspect
import logging
from functools import wraps
def inject_logger(method):
@wraps(method)
def wrapper(*args, **kwargs):
frame = inspect.currentframe()
try:
frames = inspect.getouterframes(frame)
callers_locals = frames[-2].frame.f_locals
# could make this more robust ...
return method(*args, logger=callers_locals['self'].logger, **kwargs)
finally:
del frame
del frames
return wrapper
class External:
def __init__(self):
self.a = 'a'
@inject_logger
def do_it(self, b, logger=None):
if logger is None:
logger = logging.getLogger('external')
logger.error("%s is %s", self.a, b)
from external import External
import logging
# connfigure some bespoke logger that we want to use
logger = logging.getLogger()
fmt = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s')
ch = logging.StreamHandler()
ch.setFormatter(fmt)
logger.addHandler(ch)
class IDAdapter(logging.LoggerAdapter):
def process(self, msg, kwargs):
return "[%s] %s" % (self.extra["id"], msg), kwargs
class Internal:
def __init__(self, n):
# some value that changes in every instance, maybe a unique ID for this
# request and we use an adaptor to pass that value into our logger
# without having to add it to every `logger.log()` call
self.n = n
self.logger = IDAdapter(logger, {'id': n})
def do_something(self):
# because `do_it` is decorated by `inject_logger` we don't have to
# explicitly pass in our logger, saving us some typing and we won't
# forget to do it. This means we don't have to write:
# ext.do_it(self.n, logger=self.logger)
# but can get away with the simpler:
ext.do_it(self.n)
# some kind of external service we instantiate once
ext = External()
int = Internal(1)
int.do_something()
int = Internal(2)
int.do_something()
int = Internal(3)
int.do_something()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment