A tiny dependency injectorish snippet
My favorite pytest feature is its support for āfuncargā fixtures, a lightweight approach to wiring up data flow. Years ago, I even wrote a small library targeted at more generic usage. It was a fun experience, but most of the Python Iāve written lately is in the form of small scripts. In that context, itās often convenient to avoid extra dependencies.
To that end, Iāve been using a minimal implementation of the concept in a few places.
# License: MIT (http://opensource.org/licenses/MIT)
import inspect
def run(func, completed=None, scope=None):
completed = {} if completed is None else completed
scope = inspect.currentframe().f_back.f_locals if scope is None else scope
if func not in completed:
deps = [scope[p] for p in inspect.signature(func).parameters]
args = [run(d, completed=completed, scope=scope) for d in deps]
completed[func] = func(*args)
return completed[func]
Itās small enough to copy/paste into the bottom of a script (or drop the file in a directory if youāre sharing). Usage is simple, and doesnāt require spreading a special decorator around.
def one():
return 1
def two(one):
return 1 + one
if __name__ == "__main__":
assert(run(two) == 2)
Only required functions are evaluated, and results are cached so that each is only run once.
Despite the small size, thereās actually a fair amount of inherent flexibility. You can pass in a custom dictionary of functions.
def two(one):
return 1 + one
if __name__ == "__main__":
assert(run(two, scope={'one': lambda: 1}) == 2)
You can also pre-populate the dictionary of resolved values, allowing reuse between runs or other use cases.
def two(one):
return 1 + one
if __name__ == "__main__":
# Note that the key is the actual function rather than just the name as a string
assert(run(two, completed={one: 1}) == 2)
I would not suggest architecting a non-trivial app around this little snippet. It is sure to have dark corners, it doesnāt attempt to cover many edge cases, and the indirection has potential to confuse unwitting visitors. However, if your task is complex enough to benefit from being able to wire up a little dependency graph and this style brings you joy, feel free to copy/paste it and go to town.