Package: injectool
injectoolLightweight dependency injection tool.InstallationInstall using pip:pip install injectoolHow to useInjectinghttps://github.com/eumis/injectool/blob/dev/injectool/injection.pyresolve()importinjectoolinstance=injectool.resolve(SomeClass)function=injectool.resolve(some_function)value=injectool.resolve('some_value')inject decoratorimportinjectoolfromtypingimportCallableclassDependenciesUser:@injectool.inject(instance=SomeClass)def__init__(self,instance:SomeClass=injectool.In):pass@injectool.inject(function=some_function)defsome_method(self,function:Callable=injectool.In):pass@injectool.inject(value='some_value')defuse_some_value(value:int=injectool.In):passdependency decoratorimportinjectool@injectool.dependencydefsome_function():return'some_function'defsome_function_implementation():return'some_function implementation'injectool.add_singleton(some_function,some_function_implementation)value=some_function()# value: some_function implementationDependencieshttps://github.com/eumis/injectool/blob/dev/injectool/resolvers.pySingletonimportinjectoolinjectool.add_singleton('some_value',54)some_value=injectool.resolve('some_value')injectool.add_singleton(SomeClass,SomeClassImplementation())instance:SomeClass=injectool.resolve(SomeClass)TypeNew instance is created for every resolving.importinjectoolinjectool.add_type(SomeClass,SomeClassImplementation)instance=injectool.resolve(SomeClass)ScopedOne instance is created per scope.importinjectoolinjectool.add_scoped(SomeClass,SomeClassImplementation)withinjectool.scope():instance1:SomeClass=injectool.resolve(SomeClass)withinjectool.scope():instance2:SomeClass=injectool.resolve(SomeClass)Dispose method can be passed to add_type method.
The method will be called on closing scope.importinjectooldefdispose(instance:SomeClassImplementation):passinjectool.add_scoped(SomeClass,SomeClassImplementation,dispose)withinjectool.scope():injectool.resolve(SomeClass)ThreadOne instance is created per thread.importinjectoolfromthreadingimportThreadfromconcurrent.futures.threadimportThreadPoolExecutorinjectool.add_per_thread(SomeClass,SomeClassImplementation)one=injectool.resolve(SomeClass)defthread_target():two=injectool.resolve(SomeClass)thread=Thread(target=thread_target)thread.start()withThreadPoolExecutor(max_workers=1)asexecutor:future=executor.submit(injectool.resolve,SomeClass)three=future.result()Custom resolverimportinjectoolinjectool.add('some_value',lambda:54)some_value=injectool.resolve('some_value')injectool.add(SomeClass,lambda:SomeClassImplementation())instance:SomeClass=injectool.resolve(SomeClass)How it worksAll dependencies are stored inContainer.Basically Container is just a dictionary withDependencyused as a key andResolverused as a value.Any object can be used asDependency.Resolveris function that returns value for aDependency.https://github.com/eumis/injectool/blob/dev/injectool/core.py#L12-37Dependency=AnyResolver=Callable[[],Any]classContainer:"""Container for dependencies"""def__init__(self,resolvers:Optional[Dict[Dependency,Resolver]]=None):self._resolvers:Dict[Dependency,Resolver]={}ifresolversisNoneelseresolversself.set(Container,lambda:self)defset(self,dependency:Dependency,resolve:Resolver):"""Sets resolver for dependency"""self._resolvers[dependency]=resolvedefresolve(self,dependency:Dependency)->Any:"""Resolve dependency"""resolve=self._resolvers.get(dependency)ifresolveisNone:dependency_name=dependency.__name__ifhasattr(dependency,'__name__')elsestr(dependency)raiseDependencyError(f'Dependency "{dependency_name}" is not found')returnresolve()defcopy(self)->'Container':"""returns new container with same dependencies"""returnContainer(self._resolvers.copy())Defaultcontainer is stored as global variable and used by default.Defaultcontainer can be changed.https://github.com/eumis/injectool/blob/dev/injectool/core.py#L40-45_DEFAULT_CONTAINER=Container()defset_default_container(container:Container):"""Sets default container"""global_DEFAULT_CONTAINER_DEFAULT_CONTAINER=containerCurrentcontainer can be set and used temporary.
It's stored in ContextVar so it can be used in asynchronous code.https://github.com/eumis/injectool/blob/dev/injectool/core.py#L48-66_CURRENT_CONTAINER=ContextVar('dependency_container')defget_container()->Container:"""Returns current container"""return_CURRENT_CONTAINER.get(_DEFAULT_CONTAINER)@contextmanagerdefuse_container(container:Optional[Container]=None)->Generator[Container,None,None]:"""Uses passed container for registering and resolving dependenciesCreates new if container doesn't exist."""container=containerifcontainerelseContainer()reset_token=_CURRENT_CONTAINER.set(container)try:yieldcontainerfinally:_CURRENT_CONTAINER.reset(reset_token)LicenseMITCopyright (c) 2017-present, eumis (Eugen Misievich)