Package: meta-di
Meta DI - A DI Container for Python using meta-programming (source generation)This is a DI Container for Python using meta-programming (source generation). Since we know the dependencies and their lifecycles when generating the source code, we can optimize the generated code to avoid unnecessary lookups and function calls, achieving very good performance.Installationpipinstallmeta-diQuickstartfrommeta_diimportContainerBuilderclassConfig:USER:str="admin"PASSWORD:str="admin"classAuthServiceProto:defauthenticate(self,user:str,password:str)->bool:"""Returns true if user/password is valid"""...classFakeAuthService(AuthServiceProto):def__init__(self,config:Config):self._config=configdefauthenticate(self,user:str,password:str)->bool:ifself._config.USER==userandself._config.PASSWORD==password:returnTruereturnFalsebuilder=ContainerBuilder()# We register AuthServiceProto with transient, meaning that a new instance will be created every time it is requested# We also specify that the provider for AuthServiceProto is FakeAuthServicebuilder.add_transient(AuthServiceProto,FakeAuthService)# FakeAuthService has a dependency on Config, so we must register it as well# We register Config as a singleton, meaning that the same instance will be returned every time it is requestedbuilder.add_singleton(Config)# We build the containercontainer=builder.build()# We can now request AuthServiceProto from the container# The container will create a new instance of FakeAuthService and inject it with the singleton instance of Configauth_service=container.get(AuthServiceProto)auth_service2=container.get(AuthServiceProto)assertisinstance(auth_service,FakeAuthService)assertauth_serviceisnotauth_service2# Auth service is transient, so we get a new instance every timeassertauth_service._configisauth_service2._config# Config is singleton, so we get the same instance every timeassertauth_service.authenticate("admin","admin")isTrueOther featuresScoped lifetimefrommeta_diimportContainerBuilderclassService:passcontainer=ContainerBuilder().add_scoped(Service).build()withcontainerasscoped_container:service1=scoped_container.get(Service)service2=scoped_container.get(Service)# Scoped lifetime, so we get the same instance within the scopeassertservice1isservice2# Alterntively, we can create a scope with create_scope()scoped_container=container.create_scope()service3=scoped_container.get(Service)service4=scoped_container.get(Service)# Scoped lifetime, so we get the same instance within the scopeassertservice3isservice4# Different scopes, so we get different instancesassertservice1isnotservice3Function providersfrommeta_diimportContainerBuilder,ContainerProtoclassDependency:passclassService:def__init__(self,dependency:Dependency,value:str):self.dependency=dependencyself.value=value# We can use this function to provide Service# container will be injected by the container (as itself)defservice_provider(container:ContainerProto):returnService(dependency=container.get(Dependency),value="from function provider",)# Alternatively, we could write the function this way# Dependency will be injected by the containerdefservice_provider(dependency:Dependency):returnService(dependency=dependency,value="from function provider",)container=ContainerBuilder().add_transient(Dependency).add_transient(Service,service_provider).build()assertcontainer.get(Service).value=="from function provider"Building Container class or getting the generated source codefrommeta_diimportContainerBuilderclassService:passbuilder=ContainerBuilder().add_singleton(Service)# This will return the generated source code for the Container class# You can use this to inspect the generated code or save it to a filesource_code=builder.get_code()# This will generate the Container class and return itContainerClass=builder.build_class()# You can use this class to create any number of container instancescontainer=ContainerClass()container2=ContainerClass()# NOTE, each container instance will have its own singleton instancesassertcontainer.get(Service)isnotcontainer2.get(Service)