Have you ever had a piece of configuration that you want access to deeply in a function, but you don't want to always have to pass it, and you don't want to have a singleton?
This is for you!
Here's a basic before/after for passing variables deeply.
Before:
def outer_function(config):
middle_function(config)
def middle_function(config):
inner_function(config)
def inner_function(config):
# To supply config here, we have to manually pass the config through 2 layers of function calls!
do_something_based_on(config)
outer_function(Config())
After:
from supplier import Supplier, supply
config_supplier = Supplier('config')
def outer_function():
middle_function()
def middle_function():
inner_function()
@supply(config_supplier)
def inner_function(config):
# No longer have to manually pass the config through 2 layers!
do_something_based_on(config)
with config_supplier.use(Config()):
outer_function()
This is essentially python contextvars + contextlib in one convenient package
Supplier prepends the function's arguments with the supplied value.
@supply(foo_supplier)
def func_with_args(foo, arg1, arg2):
print(foo, arg1, arg2)
with foo_supplier.use("foo"):
func_with_args("arg1", "arg2")
You can supply multiple variables simultaneously.
@supply(foo_supplier, bar_supplier)
def doubly_provided_func(foo, bar, arg):
print(foo, bar, arg)
with foo_supplier.use("foo"), bar_supplier.use("bar"):
doubly_provided_func("arg")
When using supply to decorate class method, supply keeps the self
argument in the same place, for ease of use.
class A(Printer):
@supply(foo_supplier):
def print_foo(self, foo):
self.print(foo)