injectool
Lightweight dependency injection tool.
Installation
Install using pip:
pip install injectool
How to use
Injecting
https://github.com/eumis/injectool/blob/dev/injectool/injection.py
resolve()
import injectool
instance = injectool.resolve(SomeClass)
function = injectool.resolve(some_function)
value = injectool.resolve('some_value')
inject decorator
import injectool
from typing import Callable
class DependenciesUser:
@injectool.inject(instance=SomeClass)
def __init__(self, instance: SomeClass = injectool.In):
pass
@injectool.inject(function=some_function)
def some_method(self, function: Callable = injectool.In):
pass
@injectool.inject(value='some_value')
def use_some_value(value: int = injectool.In):
pass
dependency decorator
import injectool
@injectool.dependency
def some_function():
return 'some_function'
def some_function_implementation():
return 'some_function implementation'
injectool.add_singleton(some_function, some_function_implementation)
value = some_function() # value: some_function implementation
Dependencies
https://github.com/eumis/injectool/blob/dev/injectool/resolvers.py
Singleton
import injectool
injectool.add_singleton('some_value', 54)
some_value = injectool.resolve('some_value')
injectool.add_singleton(SomeClass, SomeClassImplementation())
instance: SomeClass = injectool.resolve(SomeClass)
Type
New instance is created for every resolving.
import injectool
injectool.add_type(SomeClass, SomeClassImplementation)
instance = injectool.resolve(SomeClass)
Scoped
One instance is created per scope.
import injectool
injectool.add_scoped(SomeClass, SomeClassImplementation)
with injectool.scope():
instance1: SomeClass = injectool.resolve(SomeClass)
with injectool.scope():
instance2: SomeClass = injectool.resolve(SomeClass)
Dispose method can be passed to add_type method. The method will be called on closing scope.
import injectool
def dispose(instance: SomeClassImplementation):
pass
injectool.add_scoped(SomeClass, SomeClassImplementation, dispose)
with injectool.scope():
injectool.resolve(SomeClass)
Thread
One instance is created per thread.
import injectool
from threading import Thread
from concurrent.futures.thread import ThreadPoolExecutor
injectool.add_per_thread(SomeClass, SomeClassImplementation)
one = injectool.resolve(SomeClass)
def thread_target():
two = injectool.resolve(SomeClass)
thread = Thread(target=thread_target)
thread.start()
with ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(injectool.resolve, SomeClass)
three = future.result()
Custom resolver
import injectool
injectool.add('some_value', lambda: 54)
some_value = injectool.resolve('some_value')
injectool.add(SomeClass, lambda: SomeClassImplementation())
instance: SomeClass = injectool.resolve(SomeClass)
How it works
All dependencies are stored in Container.
Basically Container is just a dictionary with Dependency used as a key and Resolver used as a value.
Any object can be used as Dependency.
Resolver is function that returns value for a Dependency.
https://github.com/eumis/injectool/blob/dev/injectool/core.py#L12-37
Dependency = Any
Resolver = Callable[[], Any]
class Container:
"""Container for dependencies"""
def __init__(self, resolvers: Optional[Dict[Dependency, Resolver]] = None):
self._resolvers: Dict[Dependency, Resolver] = {} if resolvers is None else resolvers
self.set(Container, lambda: self)
def set(self, dependency: Dependency, resolve: Resolver):
"""Sets resolver for dependency"""
self._resolvers[dependency] = resolve
def resolve(self, dependency: Dependency) -> Any:
"""Resolve dependency"""
resolve = self._resolvers.get(dependency)
if resolve is None:
dependency_name = dependency.__name__ if hasattr(dependency, '__name__') else str(dependency)
raise DependencyError(f'Dependency "{dependency_name}" is not found')
return resolve()
def copy(self) -> 'Container':
"""returns new container with same dependencies"""
return Container(self._resolvers.copy())
Default container is stored as global variable and used by default. Default container can be changed.
https://github.com/eumis/injectool/blob/dev/injectool/core.py#L40-45
_DEFAULT_CONTAINER = Container()
def set_default_container(container: Container):
"""Sets default container"""
global _DEFAULT_CONTAINER
_DEFAULT_CONTAINER = container
Current container 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')
def get_container() -> Container:
"""Returns current container"""
return _CURRENT_CONTAINER.get(_DEFAULT_CONTAINER)
@contextmanager
def use_container(container: Optional[Container] = None) -> Generator[Container, None, None]:
"""
Uses passed container for registering and resolving dependencies
Creates new if container doesn't exist.
"""
container = container if container else Container()
reset_token = _CURRENT_CONTAINER.set(container)
try:
yield container
finally:
_CURRENT_CONTAINER.reset(reset_token)
License
Copyright (c) 2017-present, eumis (Eugen Misievich)
Metadata
Release files for injectool 3.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| injectool-3.0.0.tar.gz | 6.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| injectool-3.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 13.6 kB
Release files / injectool-3.0.0.tar.gz
| Download URL | injectool-3.0.0.tar.gz |
|---|---|
| Size | 6.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b84c327f2fed7b2857309e9157344e70e8c73e1a95dba6be024f34c1e5f00a6d
|
|
BLAKE2b-256 checksum How to use checksums |
32a9a29716f2427ddf43ef3d72503c7b8aa436b24941565f64d2f1697f448886
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.2 CPython/3.9.16
|
Release files / injectool-3.0.0-py3-none-any.whl
| Download URL | injectool-3.0.0-py3-none-any.whl |
|---|---|
| Size | 7.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0ab39621e00156cea1231b8b24e6de1d777df40223c47db88a581cff086b4181
|
|
BLAKE2b-256 checksum How to use checksums |
8b58b82a375d5d1013a782f57efcd5fe7254431bd5d002814984de7224144103
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.2 CPython/3.9.16
|
Security for injectool 3.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.