Package: dinjections
DInjectionsThis project implements a dependency injection framework in Python, based on uber's fx package.It works as a modular dependency injection framework, where you can define provision of dependencies, using options pattern and hints to define which dependencies should be provided.It also implements lifecycle management, where you can define hooks to be executed on start and stop.Dependencies are only initialized when they are needed, even if provided.InstallationpipinstalldinjectionsUsagefromdinjectionsimportApp,Provide,Invoke,Hook,Lifecycle,Provider,Annotated,Annotations# Define a class that will be injectedclassDependency0:pass# Define a class that will be injected and has dependencies of the same typeclassDependency1:def__init__(self,d0_1:Annotated[Dependency0,Annotations(name="d0_1")],d0_2:Annotated[Dependency0,Annotations(name="d0_2")]):self.d0_1=d0_1self.d0_2=d0_2defrun(self):print("Dependency1 running")# Define another class that will be injectedclassDependency2:def__init__(self,d1:Dependency1):self.d1=d1defrun(self):self.d1.run()# Function that returns a dependencydefnew_dep_2(d1:Dependency1)->Dependency2:returnDependency2()# function that will be invokeddefregister_hooks(l:Lifecycle,d:Dependency2):l.append_hook(Hook(on_start=lambda:{d.run()},))app=App(Provide(# dependency can be initiliazed passing it's classDependency1,# and also with a function that returns the objectnew_dep_2,# you can also define a name for the dependency, in case there is more than one dependency of the same typeProvider(Dependency0,name="d0_1"),Provider(Dependency0,name="d0_2"),),Invoke(register_hooks,))app.run()Dependencies of the same typeIf you have dependencies of the same type, you can use theProviderclass to define a name for the dependency with the name argument, and then use it to inject the dependency (as shown above).If this is not done, a dependency will override the previous.Dependency inheritanceIf you have a dependency that inherits from another, you can use theProviderclass to define the dependency with a name argument, and then use it to inject the dependency (as shown below).# Define a class that will be injectedclassTestClass4(TestClass3):pass# expects a dependency of type TestClass3, but will accept TestClass4 named "t1"defregister_hooks(l:Lifecycle,t:TestClass3):l.append_hook(Hook(on_start=lambda:{t.run()},))app=App(Provide(# provides names dependency that inherites from expected class TestClass3Provider(TestClass4,name=TestClass3),),Invoke(register_hooks,))app.run()Grouped dependenciesYou can group dependencies setting thegroupparameters toTruein aProvider. This will make the dependency be injected as a list of dependencies, with all the dependencies that were provided with the same name.classMyDependency:def__init__(self):defrun(self):print("MyDependency running")defregister_hooks(l:Lifecycle,d:Annotated[MyDependency,Annotations(group=True)]):# d is now a list of MyDependency, with all the dependencies named "md" that were providedfortind:l.append_hook(Hook(on_start=lambda:{t.run()},))app=App(Provide(Provider(MyDependency,group=True),Provider(MyDependency,group=True),),Invoke(register_hooks,))app.run()LifecycleYou can define hooks to be executed on start and stop of the application.Currently hooks are executed in the order they are defined, with no support for concurrency.fromdinjectionsimportApp,Hook,Lifecycledefregister_hooks(l:Lifecycle):l.append_hook(Hook(on_start=lambda:{print("Invoking 1")},on_stop=lambda:{print("Stopping 1")},))l.append_hook(Hook(on_start=lambda:{print("Invoking 2")},on_stop=lambda:{print("Stopping 2")},))app=App(Invoke(register_hooks,))app.run()