Dependency Injection Container
Full featured, flexible and fluent IoC solution for Python
PyPi
di_container can be installed from PyPi.
The Code
Basics:
Types, callables and values can be registered into a container and bound to either a dependency type, or a name. Registration is independent of dependency order: A dependency can be registered after the dependent type, as long as they are both registered before resolving.
from di_container.container import Container
container = Container(name='container')
container.register_callable(main).to_name('main_func')
container.register_value(app_config['logging']['log_path']).to_name('log_path')
container.register_type(FileLogger).to_type(ILogger)
Multiplicity:
Types and callables can be registered as Instantiation.Singleton (default), or as Instantiation.MultiInstance.
from di_container.container import Container, Instantiation
container = Container(name='container')
container.register_callable(ConnectionFactory.new_udp_connection, instantiation=Instantiation.MultiInstance).to_type(IConnection)
Type checks:
Registering to_type type checks against the given dependency type. When registering to_name, an optional type can be given, in order to make the same check.
If the check fails, a TypeError is raised.
from di_container.container import Container
container = Container(name='container')
container.register_value(app_config['network']['http_port']).to_name('port', int)
Manual assignment:
Type initializers and callables can have some, or all, of their arguments assigned values explicitly. The rest will be resolved at resolve time.
from di_container.container import Container, Instantiation
container = Container(name='container')
container.register_callable(ConnectionFactory.new_udp_connection, instantiation=Instantiation.MultiInstance).to_type(IConnection).with_params(host='localhost', port=12345)
Resolving order:
Resolving a registered type, or name, will attempt to resolve any needed arguments for type initializers, or callables, recursively.
First, with values given in with_params, second, with arguments' type annotations (please use them 🙂), and then, with declared default values.
When a dependency is registered to_name, it cannot be automatically inferred by type annotation.
A container's default behavior is to use an argument's name to lookup a to_name registration as a last attempt to resolve an argument.
This behavior can be changed if it's deemed to be too risky, and dependency names can be assigned explicitly when needed, using with_name_bindings.
The full order of resolution attempts for a parameter is:
- Given value, in the form of
with_params(param=value). The value of param will be the given value. - Name binding, in the form of
with_name_bindings(param='name_binding'). The value of param will be the resolution of the name 'name_binding'. - Type annotation. If the parameter has a type annotation (type hint), then the value of param will be the resolution of this type.
- If
param_names_as_bindingsisTrue, The container will attempt to resolve the param name itself. If successful, the value of param will be the resolution of the parameter's name. - Lastly, if a default value is defined for the parameter, it will be used. The value of param will be its default value.
If, however,
prefer_defaultsisTruein callingresolve_typeorresolve_name, the default value will be used if no explicit value or binding was given (Nowith_paramsorwith_name_bindingsfor that parameter), instead of trying to resolve the type annotation or parameter name.
from di_container.container import Container, Instantiation
container = Container(name='container', param_names_as_bindings=False)
container.register_callable(ConnectionFactory.new_udp_connection, instantiation=Instantiation.MultiInstance).to_type(IConnection).with_name_bindings(host='host_ip', port='port')
container.register_value(app_config['network']['ip']).to_name('host_ip', str)
container.register_value(app_config['network']['http_port']).to_name('port', int)
Sub-containers:
Sub-containers can help when writing several packages, or sub-systems, or just to organize a large amount of dependencies. The resolving process will try to resolve from the current container and if no match is found, will try to resolve using each sub-container (and its sub-containers recursively) in the order they were added. Containers are initialized with names for identification in error messages.
from di_container.container import Container
base_container = Container(name='base')
# register config values and core classes
base_container.register_value(app_config['logging']['log_path']).to_name('log_path')
base_container.register_value(app_config['database']['database_url']).to_name('database_url', str)
base_container.register_type(FileLogger).to_type(ILogger)
base_container.register_type(NoSqlDatabase).to_type(IDatabase)
main_container = Container('main')
# register main class and entry point
main_container.register_callable(main).to_name('main_function').with_name_bindings(main_manager='main_class')
main_container.register_type(MainManager).to_name('main_class').with_name_bindings(internal_comm='int_comm', external_comm='ext_comm')
comm_container = Container('comm')
# register communication classes
comm_container.register_type(UdpCommunicator).to_name('int_comm', ICommunicator)
comm_container.register_type(HttpCommunicator).to_name('ext_comm', ICommunicator)
# setting sub containers
comm_container.add_sub_container(base_container)
main_container.add_sub_container(base_container)
main_container.add_sub_container(comm_container)
# activating the main function
main_container.resolve_name('main_function')
To Do
Some features that are being considered:
- Configuration registration.
- Binding to members of registered items.
- Registration of collections of values.
- Display of dependency tree (forest).
More
- An example is available in the git repository.
Metadata
Release files for di-container 2.1.10
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 | Actions |
|---|---|---|---|---|
| di_container-2.1.10.tar.gz | 11.8 kB | Without provenance | Details for di_container-2.1.10.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| di_container-2.1.10-py3-none-any.whl | Python 3 | none | any | Details for di_container-2.1.10-py3-none-any.whl |
Total release size: 22.4 kB
Release files / di_container-2.1.10.tar.gz
| Download URL | di_container-2.1.10.tar.gz |
|---|---|
| Size | 11.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ce33b2b0c1dda0a630b66b8634bcbb3fb123677d9ce1c055475406f0b245efa2
|
|
BLAKE2b-256 checksum How to use checksums |
b2ef0b1d497368b7a1661f8424d92f0bd492f0ba07f08210cbe7e06fb01ed71d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.0 CPython/3.7.3
|
Release files / di_container-2.1.10-py3-none-any.whl
| Download URL | di_container-2.1.10-py3-none-any.whl |
|---|---|
| Size | 10.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a463f6b4c063bb3ff58a950e478c926a8a75cad6044593a98ff5c8ea823684b7
|
|
BLAKE2b-256 checksum How to use checksums |
5fceca405e37a6b9476859e2344cbbc8c0e1b1b58faf1ea1fe0ad308a56b9755
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.0 CPython/3.7.3
|
Security for di-container 2.1.10
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.