Package: mainline
Simple yet powerful python dependency injection.Docs:http://mainline.readthedocs.org/en/latestAPI Docs:http://mainline.readthedocs.org/en/latest/mainline.htmlPyPi:https://pypi.python.org/pypi/mainlineWhyPure Python, so it basically works everywhere.
Tested against cPython3.5,3.6,3.7in addition to2.7.
PyPy/PyPy3 are also fully supported.Only external dependencies aresixandwrapt, both of which you’re likely to already have.Supports using function annotations in Python3.x.
This is in addition to a standard syntax that works with both3.xand2.7.Your method signature is fully preserved, maintaining introspection ability.
(Minus any injected arguments of course.)Scope is fully configurable (per injectable), giving you tight control over where an object should be shared and where it should not.Supports auto injection”, where your argument names are used to determine what gets injected.
It’s also fully optional, as it’s slightly less performant due to it’s dynamic nature.Provider keys tend to be strings, but really any hashable object is supported, so if you prefer to use classes, go for it.Just keep in mind that you can’t use a class as an argument name (rightfully so) in python.
This means you can’t auto inject it, for instance.
You can simply make an alias to get both worlds, however. The world is your oyster.Check out that sweet syringe.InstallationpipinstallmainlineQuickstartMake sure to check the docs for more use cases!"""
Initialize your Di instance.
""">>>frommainlineimportDi>>>di=Di()"""
Feed it your delicious factories, optionally scoped.
""">>>@di.register_factory('apple')...defapple():...return'apple'"""
Factories can of course be injected themselves.
""">>>@di.f('banana',scope='global')# f is syntactic sugar for register_factory...defbanana():...return'banana'"""
Let's verify that our factories above do what they're supposed to.
""">>>di.resolve('apple')=='apple'anddi.resolve('banana')=='banana'True"""
Positional arguments are injected in the order given:
""">>>@di.inject('apple')...definjected(apple):...returnapple>>>injected()==apple()# verifyTrue"""
Injecting keyword arguments is straight forward, you simply hand them as keyword arguments:
""">>>@di.f('orange')# alias for register_factory...@di.i('apple')# alias for inject...deforange(apple):...return'banana',apple>>>@di.i('apple',an_orange='orange')...definjected(apple,arg1,an_orange=None):...returnapple,arg1,an_orange>>>injected('arg1')==(apple(),'arg1',orange())# verifyTrue"""
Arguments that are not injected work as expected:
""">>>@di.inject('apple')...definjected(apple,arg1):...returnapple,arg1>>>injected('arg1')==(apple(),'arg1')True"""
Injection on a class injects upon it's `__init__` method:
""">>>@di.inject('apple')...classInjectee(object):...def__init__(self,apple):...self.apple=apple>>>Injectee().apple==apple()True"""
You can inject class-level properties using `di.inject_classproperty()`:
""">>>@di.inject_classproperty('apple')...classInjectee(object):...pass>>>Injectee.apple==apple()True"""
Injecting providers based upon the argpsec can be done with `di.auto_inject`, or it's shortened alias `di.ai()`:
""">>>@di.auto_inject()...definjected(apple):...returnapple>>>injected()==apple()True>>>@di.ai('apple')# alias for auto_inject...definjected(apple,arg1):...returnapple,arg1>>>injected('arg1')==(apple(),'arg1')True>>>@di.auto_inject()...definjected(apple,arg1,banana=None):...returnapple,arg1,banana>>>injected('arg1')==(apple(),'arg1',banana())True>>>@di.auto_inject(renamed_banana='banana')...definjected(apple,arg1,renamed_banana):...returnapple,arg1,renamed_banana>>>injected('arg1')==(apple(),'arg1',banana())TrueRunning testsTox is used to handle testing multiple python versions.tox