Merge branch 'release/3.37.0' into master

This commit is contained in:
Roman Mogylatov 2020-09-03 18:06:01 -04:00
commit 2cf5efa031
47 changed files with 589 additions and 427 deletions

View File

@ -52,7 +52,7 @@ What is ``Dependency Injector``?
``Dependency Injector`` is a dependency injection framework for Python.
It helps you in implementing the dependency injection principle.
It helps implementing the dependency injection principle.
What is dependency injection?
-----------------------------

View File

@ -1,3 +1,5 @@
.. _containers:
Containers
==========

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -64,22 +64,49 @@ Dependency Injector --- Dependency injection framework for Python
``Dependency Injector`` is a dependency injection framework for Python.
It stands on two principles:
It helps implementing the dependency injection principle.
- Explicit is better than implicit (PEP20).
- Do no magic to your code.
Key features of the ``Dependency Injector``:
How does it different from the other frameworks?
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your
objects. See :ref:`providers`.
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
and configuring dev / stage environment to replace API clients with stubs etc. See
:ref:`provider-overriding`.
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
and dictionaries. See :ref:`configuration-provider`.
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
- **Performance**. Fast. Written in ``Cython``.
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.
- **No autowiring.** The framework does NOT do any autowiring / autoresolving of the dependencies. You need to specify everything explicitly. Because *"Explicit is better than implicit" (PEP20)*.
- **Does not pollute your code.** Your application does NOT know and does NOT depend on the framework. No ``@inject`` decorators, annotations, patching or any other magic tricks.
.. code-block:: python
``Dependency Injector`` makes a simple contract with you:
from dependency_injector import containers, providers
- You tell the framework how to assemble your objects
- The framework does it for you
The power of the ``Dependency Injector`` is in its simplicity and straightforwardness. It is a simple tool for the powerful concept.
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout.as_int(),
)
service = providers.Factory(
Service,
api_client=api_client,
)
if __name__ == '__main__':
container = Container()
container.config.api_key.from_env('API_KEY')
container.config.timeout.from_env('TIMEOUT')
service = container.service()
With the ``Dependency Injector`` you keep **application structure in one place**.
This place is called **the container**. You use the container to manage all the components of the

View File

@ -17,4 +17,3 @@ dependency injection pattern, inversion of control principle and
what_is_di
di_in_python
key_features
structure

View File

@ -3,66 +3,33 @@ Key features
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes key features of "Dependency Injector"
framework. It also provides some cases and recommendations
about usage of "Dependency Injector" framework.
:description: This article describes key features of the Dependency Injector
framework.
Key features of the ``Dependency Injector``:
``Dependency Injector`` is a dependency injection framework for Python.
It was designed to be a unified and developer-friendly tool that helps
implement a dependency injection design pattern in a formal, pretty, and
Pythonic way.
- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your
objects. See :ref:`providers`.
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
and configuring dev / stage environment to replace API clients with stubs etc. See
:ref:`provider-overriding`.
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
and dictionaries. See :ref:`configuration-provider`.
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
- **Performance**. Fast. Written in ``Cython``.
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.
It stands on two principles:
The framework stands on two principles:
- Explicit is better than implicit (PEP20).
- Do no magic to your code.
- **Explicit is better than implicit (PEP20)**.
- **Do not do any magic to your code**.
How does it different from the other frameworks?
How is that different from the other frameworks?
- **No autowiring.** The framework does NOT do any autowiring / autoresolving of the dependencies. You need to specify everything explicitly. Because *"Explicit is better than implicit" (PEP20)*.
- **Does not pollute your code.** Your application does NOT know and does NOT depend on the framework. No ``@inject`` decorators, annotations, patching or any other magic tricks.
``Dependency Injector`` makes a simple contract with you:
- You tell the framework how to build you code
- The framework does it for you
The power of the ``Dependency Injector`` is in its simplicity and straightforwardness. It is a simple tool for the powerful concept.
The key features of the ``Dependency Injector`` framework are:
+ Easy, smart, and Pythonic style.
+ Does NOT pollute client code.
+ Obvious and clear structure.
+ Extensibility and flexibility.
+ High performance.
+ Memory efficiency.
+ Thread safety.
+ Documented.
+ Semantically versioned.
+ Distributed as pre-compiled wheels.
``Dependency Injector`` containers and providers are implemented as C extension
types using ``Cython``.
``Dependency Injector`` framework can be used in the different application types:
+ Web applications based on the ``Flask``, ``Django`` or any other web framework.
+ Asynchronous applications ``asyncio``, ``aiohttp``, ``Tornado``, or ``Twisted``.
+ Standalone frameworks and libraries.
+ GUI applications.
``Dependency Injector`` framework can be integrated on the different project
stages:
+ It can be used in the beginning of the development of a new application.
+ It can be integrated into application that is on its active development stage.
+ It can be used for refactoring of legacy application.
Components of ``Dependency Injector`` framework could be used:
+ In composition with each other.
+ Independently from each other.
The power of the framework is in a simplicity. ``Dependency Injector`` is a simple tool for the powerful concept.
.. disqus::

View File

@ -1,50 +0,0 @@
Structure of Dependency Injector
--------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes "Dependency Injector" framework
components and their interaction between each other.
Providers and containers are the former components of
the framework.
Current section describes *Dependency Injector* main entities and their
interaction between each other.
.. image:: /images/internals.png
:width: 100%
:align: center
There are 2 main entities: providers & containers.
Providers
~~~~~~~~~
Providers are strategies of accessing objects. For example,
:py:class:`dependency_injector.providers.Factory` creates new instance
of provided class every time it is called.
:py:class:`dependency_injector.providers.Singleton` creates provided
instance once and returns it on every next call. Base class is -
:py:class:`dependency_injector.providers.Provider`.
Providers could be:
+ Injected into each other.
+ Overridden by each other.
+ Extended.
Containers
~~~~~~~~~~
Containers are collections of providers. They are used for grouping
of providers by some principles. Base class is -
:py:class:`dependency_injector.containers.DeclarativeContainer`.
Containers could be:
+ Overridden by each other.
+ Copied from each other.
+ Extended.
.. disqus::

View File

@ -7,6 +7,14 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_
3.37.0
------
- Update index documentation page.
- Make multiple improvements and fixes for the providers documentation.
- Update "Key Features" documentation page.
- Remove "Structure of Dependency Injector" documentation page.
- Edit "Feedback" documentation page.
3.36.0
------
- Update providers overriding documentation and rework examples.

View File

@ -1,12 +1,8 @@
Feedback
========
Feel free to post questions, bugs, feature requests, proposals etc. on
*Dependency Injector* GitHub Issues:
https://github.com/ets-labs/python-dependency-injector/issues
Your feedback is quite important!
To post a question, bug report, a feature proposal or get some help open a
`Github Issue <https://github.com/ets-labs/python-dependency-injector/issues>`_ or leave a comment
below.
.. disqus::

View File

@ -1,3 +1,5 @@
.. _configuration-provider:
Configuration provider
======================
@ -14,8 +16,8 @@ Configuration provider
.. literalinclude:: ../../examples/providers/configuration/configuration.py
:language: python
:emphasize-lines: 4,9-10
:lines: 4-14
:emphasize-lines: 7,12-13
:lines: 3-
It implements the principle "use first, define later".
@ -27,8 +29,8 @@ Loading from an INI file
.. literalinclude:: ../../examples/providers/configuration/configuration_ini.py
:language: python
:lines: 3-5,6-
:emphasize-lines: 6
:lines: 3-
:emphasize-lines: 12
where ``examples/providers/configuration/config.ini`` is:
@ -47,8 +49,8 @@ Loading from a YAML file
.. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py
:language: python
:lines: 3-5,6-
:emphasize-lines: 6
:lines: 3-
:emphasize-lines: 12
where ``examples/providers/configuration/config.yml`` is:
@ -81,8 +83,8 @@ Loading from a dictionary
.. literalinclude:: ../../examples/providers/configuration/configuration_dict.py
:language: python
:lines: 3-5,6-
:emphasize-lines: 6-13
:lines: 3-
:emphasize-lines: 12-19
Loading from an environment variable
------------------------------------
@ -92,8 +94,8 @@ Loading from an environment variable
.. literalinclude:: ../../examples/providers/configuration/configuration_env.py
:language: python
:lines: 5-7,13-21
:emphasize-lines: 6-8
:lines: 3-
:emphasize-lines: 18-20
Loading from the multiple sources
---------------------------------
@ -103,8 +105,8 @@ configuration is merged recursively over the existing configuration.
.. literalinclude:: ../../examples/providers/configuration/configuration_multiple.py
:language: python
:lines: 3-5,6-14
:emphasize-lines: 6-7
:lines: 3-
:emphasize-lines: 12-13
where ``examples/providers/configuration/config.local.yml`` is:
@ -122,7 +124,7 @@ convert it into an ``int`` or a ``float``.
.. literalinclude:: ../../examples/providers/configuration/configuration_type.py
:language: python
:lines: 3-
:emphasize-lines: 17
:emphasize-lines: 19
``Configuration`` provider has next helper methods:
@ -135,10 +137,27 @@ The last method ``.as_(callback, *args, **kwargs)`` helps to implement other con
.. literalinclude:: ../../examples/providers/configuration/configuration_type_custom.py
:language: python
:lines: 3-
:emphasize-lines: 16
:emphasize-lines: 18
With the ``.as_(callback, *args, **kwargs)`` you can specify a function that will be called
before the injection. The value from the config will be passed as a first argument. The returned
value will be injected. Parameters ``*args`` and ``**kwargs`` are handled as any other injections.
Injecting invariants
--------------------
You can inject invariant configuration options based on the value of the other configuration
option.
To use that you should provide the switch-value as an item of the configuration option that
contains sections ``config.options[config.switch]``:
- When the value of the ``config.switch`` is ``A``, the ``config.options.A`` is injected
- When the value of the ``config.switch`` is ``B``, the ``config.options.B`` is injected
.. literalinclude:: ../../examples/providers/configuration/configuration_itemselector.py
:language: python
:lines: 3-
:emphasize-lines: 15,30-31,38
.. disqus::

View File

@ -73,7 +73,7 @@ all the classes and use special double-underscore ``__`` syntax for passing the
.. literalinclude:: ../../examples/providers/factory_init_injections_underlying.py
:language: python
:lines: 3-
:emphasize-lines: 24-35,39,42,45
:emphasize-lines: 44,49
When you use ``__`` separator in the name of the keyword argument the ``Factory`` looks for
the dependency with the same name as the left part of the ``__`` expression.
@ -98,7 +98,7 @@ attribute of the provider that you're going to inject.
.. literalinclude:: ../../examples/providers/factory_delegation.py
:language: python
:lines: 3-
:emphasize-lines: 25
:emphasize-lines: 28
.. note:: Any provider has a ``.provider`` attribute.
@ -135,7 +135,7 @@ provider with two peculiarities:
.. literalinclude:: ../../examples/providers/abstract_factory.py
:language: python
:lines: 3-
:emphasize-lines: 32
:emphasize-lines: 34
Factory aggregate
-----------------
@ -155,7 +155,7 @@ rest of the arguments are passed to the delegated ``Factory``.
.. literalinclude:: ../../examples/providers/factory_aggregate.py
:language: python
:lines: 3-
:emphasize-lines: 31-35,43
:emphasize-lines: 33-37,47
You can get a dictionary of the aggregated factories using the ``.factories`` attribute of the
``FactoryAggregate``. To get a game factories dictionary from the previous example you can use

View File

@ -13,7 +13,7 @@ List provider
.. literalinclude:: ../../examples/providers/list.py
:language: python
:lines: 3-
:emphasize-lines: 19-22
:emphasize-lines: 21-24
``List`` provider handles positional arguments the same way as a :ref:`factory-provider`.

View File

@ -14,7 +14,7 @@ You can inject provided object attribute, item or result of its method call.
.. literalinclude:: ../../examples/providers/provided_instance.py
:language: python
:emphasize-lines: 26-32
:emphasize-lines: 28-34
:lines: 3-
To use the feature you should use the ``.provided`` attribute of the injected provider. This
@ -32,7 +32,7 @@ You can do nested constructions:
.. literalinclude:: ../../examples/providers/provided_instance_complex.py
:language: python
:emphasize-lines: 24-30
:emphasize-lines: 26-32
:lines: 3-
The ``.provided`` attribute is available for the next providers:

View File

@ -17,7 +17,7 @@ Selector provider
.. literalinclude:: ../../examples/providers/selector.py
:language: python
:lines: 3-
:emphasize-lines: 14-18
:emphasize-lines: 16-20
The first argument of the ``Selector`` provider is called ``selector``. It can be an option of
a ``Configuration`` provider or any other callable. The ``selector`` callable has to return a

View File

@ -32,6 +32,13 @@ factories:
- :ref:`factory-specialize-provided-type`
- :ref:`abstract-factory`
``Singleton`` provider scope is tied to the container. Two different containers will provider
two different singleton objects:
.. literalinclude:: ../../examples/providers/singleton_multiple_containers.py
:language: python
:lines: 3-
Resetting memorized object
--------------------------
@ -41,7 +48,7 @@ provider.
.. literalinclude:: ../../examples/providers/singleton_resetting.py
:language: python
:lines: 3-
:emphasize-lines: 14
:emphasize-lines: 18
.. note::
Resetting of the memorized object clears the reference to it. Further object's lifecycle is
@ -64,7 +71,7 @@ There are two thread-safe singleton implementations out of the box:
.. literalinclude:: ../../examples/providers/singleton_thread_locals.py
:language: python
:lines: 3-
:emphasize-lines: 11,12
:emphasize-lines: 13,15
Implementing scopes
-------------------

View File

@ -5,7 +5,7 @@ import dataclasses
import random
from typing import List
from dependency_injector import providers
from dependency_injector import containers, providers
class AbstractCacheClient(metaclass=abc.ABCMeta):
@ -31,18 +31,22 @@ class Service:
cache: AbstractCacheClient
cache_client_factory = providers.AbstractFactory(AbstractCacheClient)
service_factory = providers.Factory(
Service,
cache=cache_client_factory,
)
class Container(containers.DeclarativeContainer):
cache_client_factory = providers.AbstractFactory(AbstractCacheClient)
service_factory = providers.Factory(
Service,
cache=cache_client_factory,
)
if __name__ == '__main__':
cache_type = random.choice(['redis', 'memcached', None])
container = Container()
cache_type = random.choice(['redis', 'memcached'])
if cache_type == 'redis':
cache_client_factory.override(
container.cache_client_factory.override(
providers.Factory(
RedisCacheClient,
host='localhost',
@ -51,7 +55,7 @@ if __name__ == '__main__':
),
)
elif cache_type == 'memcached':
cache_client_factory.override(
container.cache_client_factory.override(
providers.Factory(
MemcachedCacheClient,
hosts=['10.0.1.1'],
@ -60,7 +64,7 @@ if __name__ == '__main__':
),
)
service = service_factory()
service = container.service_factory()
print(service.cache)
# The output depends on cache_type variable value.
#

View File

@ -2,17 +2,22 @@
import passlib.hash
from dependency_injector import providers
from dependency_injector import containers, providers
password_hasher = providers.Callable(
passlib.hash.sha256_crypt.hash,
salt_size=16,
rounds=10000,
)
password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify)
class Container(containers.DeclarativeContainer):
password_hasher = providers.Callable(
passlib.hash.sha256_crypt.hash,
salt_size=16,
rounds=10000,
)
password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify)
if __name__ == '__main__':
hashed_password = password_hasher('super secret')
assert password_verifier('super secret', hashed_password)
container = Container()
hashed_password = container.password_hasher('super secret')
assert container.password_verifier('super secret', hashed_password)

View File

@ -1,21 +1,24 @@
"""`Configuration` provider example."""
import boto3
from dependency_injector import providers
from dependency_injector import containers, providers
config = providers.Configuration()
class Container(containers.DeclarativeContainer):
s3_client_factory = providers.Factory(
boto3.client,
's3',
aws_access_key_id=config.aws.access_key_id,
aws_secret_access_key=config.aws.secret_access_key,
)
config = providers.Configuration()
s3_client_factory = providers.Factory(
boto3.client,
's3',
aws_access_key_id=config.aws.access_key_id,
aws_secret_access_key=config.aws.secret_access_key,
)
if __name__ == '__main__':
config.from_dict(
container = Container()
container.config.from_dict(
{
'aws': {
'access_key_id': 'KEY',
@ -23,4 +26,4 @@ if __name__ == '__main__':
},
},
)
s3_client = s3_client_factory()
s3_client = container.s3_client_factory()

View File

@ -1,20 +1,34 @@
"""`Configuration` provider values loading example."""
from dependency_injector import providers
from dependency_injector import containers, providers
config = providers.Configuration()
class Container(containers.DeclarativeContainer):
config.from_dict(
{
config = providers.Configuration()
if __name__ == '__main__':
container = Container()
container.config.from_dict(
{
'aws': {
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
},
},
)
assert container.config() == {
'aws': {
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
},
},
)
assert config() == {'aws': {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}}
assert config.aws() == {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}
assert config.aws.access_key_id() == 'KEY'
assert config.aws.secret_access_key() == 'SECRET'
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
},
}
assert container.config.aws() == {
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
}
assert container.config.aws.access_key_id() == 'KEY'
assert container.config.aws.secret_access_key() == 'SECRET'

View File

@ -2,20 +2,25 @@
import os
from dependency_injector import providers
from dependency_injector import containers, providers
# Emulate environment variables
os.environ['AWS_ACCESS_KEY_ID'] = 'KEY'
os.environ['AWS_SECRET_ACCESS_KEY'] = 'SECRET'
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
config = providers.Configuration()
if __name__ == '__main__':
container = Container()
config.aws.access_key_id.from_env('AWS_ACCESS_KEY_ID')
config.aws.secret_access_key.from_env('AWS_SECRET_ACCESS_KEY')
config.optional.from_env('UNDEFINED', 'default_value')
# Emulate environment variables
os.environ['AWS_ACCESS_KEY_ID'] = 'KEY'
os.environ['AWS_SECRET_ACCESS_KEY'] = 'SECRET'
assert config.aws.access_key_id() == 'KEY'
assert config.aws.secret_access_key() == 'SECRET'
assert config.optional() == 'default_value'
container.config.aws.access_key_id.from_env('AWS_ACCESS_KEY_ID')
container.config.aws.secret_access_key.from_env('AWS_SECRET_ACCESS_KEY')
container.config.optional.from_env('UNDEFINED', 'default_value')
assert container.config.aws.access_key_id() == 'KEY'
assert container.config.aws.secret_access_key() == 'SECRET'
assert container.config.optional() == 'default_value'

View File

@ -1,13 +1,27 @@
"""`Configuration` provider values loading example."""
from dependency_injector import providers
from dependency_injector import containers, providers
config = providers.Configuration()
class Container(containers.DeclarativeContainer):
config.from_ini('examples/providers/configuration/config.ini')
config = providers.Configuration()
assert config() == {'aws': {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}}
assert config.aws() == {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}
assert config.aws.access_key_id() == 'KEY'
assert config.aws.secret_access_key() == 'SECRET'
if __name__ == '__main__':
container = Container()
container.config.from_ini('examples/providers/configuration/config.ini')
assert container.config() == {
'aws': {
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
},
}
assert container.config.aws() == {
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
}
assert container.config.aws.access_key_id() == 'KEY'
assert container.config.aws.secret_access_key() == 'SECRET'

View File

@ -1,11 +1,8 @@
"""`Configuration` provider dynamic item selector.
Details: https://github.com/ets-labs/python-dependency-injector/issues/274
"""
"""`Configuration` provider dynamic item selector."""
import dataclasses
from dependency_injector import providers
from dependency_injector import containers, providers
@dataclasses.dataclass
@ -14,34 +11,37 @@ class Foo:
option2: object
config = providers.Configuration(default={
'target': 'A',
'items': {
'A': {
'option1': 60,
'option2': 80,
},
'B': {
'option1': 10,
'option2': 20,
},
},
})
class Container(containers.DeclarativeContainer):
foo = providers.Factory(
Foo,
option1=config.items[config.target].option1,
option2=config.items[config.target].option2,
)
config = providers.Configuration(default={
'target': 'A',
'items': {
'A': {
'option1': 60,
'option2': 80,
},
'B': {
'option1': 10,
'option2': 20,
},
},
})
foo_factory = providers.Factory(
Foo,
option1=config.items[config.target].option1,
option2=config.items[config.target].option2,
)
if __name__ == '__main__':
config.target.from_env('TARGET')
f = foo()
print(f.option1, f.option2)
container = Container()
container.config.target.from_env('TARGET')
foo = container.foo_factory()
print(foo.option1, foo.option2)
# $ TARGET=A python configuration_itemselector.py
# 60 80
# $ TARGET=B python configuration_itemselector.py
# 10 20
# $ TARGET=A python configuration_itemselector.py
# 60 80
# $ TARGET=B python configuration_itemselector.py
# 10 20

View File

@ -1,14 +1,28 @@
"""`Configuration` provider values loading example."""
from dependency_injector import providers
from dependency_injector import containers, providers
config = providers.Configuration()
class Container(containers.DeclarativeContainer):
config.from_yaml('examples/providers/configuration/config.yml')
config.from_yaml('examples/providers/configuration/config.local.yml')
config = providers.Configuration()
assert config() == {'aws': {'access_key_id': 'LOCAL-KEY', 'secret_access_key': 'LOCAL-SECRET'}}
assert config.aws() == {'access_key_id': 'LOCAL-KEY', 'secret_access_key': 'LOCAL-SECRET'}
assert config.aws.access_key_id() == 'LOCAL-KEY'
assert config.aws.secret_access_key() == 'LOCAL-SECRET'
if __name__ == '__main__':
container = Container()
container.config.from_yaml('examples/providers/configuration/config.yml')
container.config.from_yaml('examples/providers/configuration/config.local.yml')
assert container.config() == {
'aws': {
'access_key_id': 'LOCAL-KEY',
'secret_access_key': 'LOCAL-SECRET',
},
}
assert container.config.aws() == {
'access_key_id': 'LOCAL-KEY',
'secret_access_key': 'LOCAL-SECRET',
}
assert container.config.aws.access_key_id() == 'LOCAL-KEY'
assert container.config.aws.secret_access_key() == 'LOCAL-SECRET'

View File

@ -2,7 +2,7 @@
import os
from dependency_injector import providers
from dependency_injector import containers, providers
class ApiClient:
@ -11,24 +11,28 @@ class ApiClient:
self.timeout = timeout
config = providers.Configuration()
class Container(containers.DeclarativeContainer):
api_client_factory = providers.Factory(
ApiClient,
api_key=config.api.key,
timeout=config.api.timeout.as_int(),
)
config = providers.Configuration()
api_client_factory = providers.Factory(
ApiClient,
api_key=config.api.key,
timeout=config.api.timeout.as_int(),
)
if __name__ == '__main__':
container = Container()
# Emulate environment variables
os.environ['API_KEY'] = 'secret'
os.environ['API_TIMEOUT'] = '5'
config.api.key.from_env('API_KEY')
config.api.timeout.from_env('API_TIMEOUT')
container.config.api.key.from_env('API_KEY')
container.config.api.timeout.from_env('API_TIMEOUT')
api_client = api_client_factory()
api_client = container.api_client_factory()
assert api_client.api_key == 'secret'
assert api_client.timeout == 5

View File

@ -3,7 +3,7 @@
import os
import decimal
from dependency_injector import providers
from dependency_injector import containers, providers
class Calculator:
@ -11,20 +11,24 @@ class Calculator:
self.pi = pi
config = providers.Configuration()
class Container(containers.DeclarativeContainer):
calculator_factory = providers.Factory(
Calculator,
pi=config.pi.as_(decimal.Decimal),
)
config = providers.Configuration()
calculator_factory = providers.Factory(
Calculator,
pi=config.pi.as_(decimal.Decimal),
)
if __name__ == '__main__':
container = Container()
# Emulate environment variables
os.environ['PI'] = '3.1415926535897932384626433832'
config.pi.from_env('PI')
container.config.pi.from_env('PI')
calculator = calculator_factory()
calculator = container.calculator_factory()
assert calculator.pi == decimal.Decimal('3.1415926535897932384626433832')

View File

@ -1,13 +1,27 @@
"""`Configuration` provider values loading example."""
from dependency_injector import providers
from dependency_injector import containers, providers
config = providers.Configuration()
class Container(containers.DeclarativeContainer):
config.from_yaml('examples/providers/configuration/config.yml')
config = providers.Configuration()
assert config() == {'aws': {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}}
assert config.aws() == {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}
assert config.aws.access_key_id() == 'KEY'
assert config.aws.secret_access_key() == 'SECRET'
if __name__ == '__main__':
container = Container()
container.config.from_yaml('examples/providers/configuration/config.yml')
assert container.config() == {
'aws': {
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
},
}
assert container.config.aws() == {
'access_key_id': 'KEY',
'secret_access_key': 'SECRET',
}
assert container.config.aws.access_key_id() == 'KEY'
assert container.config.aws.secret_access_key() == 'SECRET'

View File

@ -2,7 +2,7 @@
import asyncio
from dependency_injector import providers
from dependency_injector import containers, providers
async def coroutine(arg1, arg2):
@ -10,10 +10,14 @@ async def coroutine(arg1, arg2):
return arg1, arg2
coroutine_provider = providers.Coroutine(coroutine, arg1=1, arg2=2)
class Container(containers.DeclarativeContainer):
coroutine_provider = providers.Coroutine(coroutine, arg1=1, arg2=2)
if __name__ == '__main__':
arg1, arg2 = asyncio.run(coroutine_provider())
container = Container()
arg1, arg2 = asyncio.run(container.coroutine_provider())
assert (arg1, arg2) == (1, 2)
assert asyncio.iscoroutinefunction(coroutine_provider)
assert asyncio.iscoroutinefunction(container.coroutine_provider)

View File

@ -1,6 +1,6 @@
"""Custom provider example."""
from dependency_injector import providers
from dependency_injector import containers, providers
class CustomFactory(providers.Provider):
@ -29,14 +29,18 @@ class CustomFactory(providers.Provider):
return self._factory(*args, **kwargs)
factory = CustomFactory(object)
class Container(containers.DeclarativeContainer):
factory = CustomFactory(object)
if __name__ == '__main__':
object1 = factory()
container = Container()
object1 = container.factory()
assert isinstance(object1, object)
object2 = factory()
object2 = container.factory()
assert isinstance(object1, object)
assert object1 is not object2

View File

@ -1,15 +1,19 @@
"""`Factory` provider example."""
from dependency_injector import providers
from dependency_injector import containers, providers
class User:
...
users_factory = providers.Factory(User)
class Container(containers.DeclarativeContainer):
user_factory = providers.Factory(User)
if __name__ == '__main__':
user1 = users_factory()
user2 = users_factory()
container = Container()
user1 = container.user_factory()
user2 = container.user_factory()

View File

@ -3,7 +3,7 @@
import dataclasses
import sys
from dependency_injector import providers
from dependency_injector import containers, providers
@dataclasses.dataclass
@ -30,11 +30,13 @@ class Ludo(Game):
...
game_factory = providers.FactoryAggregate(
chess=providers.Factory(Chess),
checkers=providers.Factory(Checkers),
ludo=providers.Factory(Ludo),
)
class Container(containers.DeclarativeContainer):
game_factory = providers.FactoryAggregate(
chess=providers.Factory(Chess),
checkers=providers.Factory(Checkers),
ludo=providers.Factory(Ludo),
)
if __name__ == '__main__':
@ -42,7 +44,9 @@ if __name__ == '__main__':
player1 = sys.argv[2].capitalize()
player2 = sys.argv[3].capitalize()
selected_game = game_factory(game_type, player1, player2)
container = Container()
selected_game = container.game_factory(game_type, player1, player2)
selected_game.play()
# $ python factory_aggregate.py chess John Jane

View File

@ -2,7 +2,7 @@
from typing import Callable, List
from dependency_injector import providers
from dependency_injector import containers, providers
class User:
@ -21,15 +21,20 @@ class UserRepository:
]
user_factory = providers.Factory(User)
user_repository_factory = providers.Factory(
UserRepository,
user_factory=user_factory.provider,
)
class Container(containers.DeclarativeContainer):
user_factory = providers.Factory(User)
user_repository_factory = providers.Factory(
UserRepository,
user_factory=user_factory.provider,
)
if __name__ == '__main__':
user_repository = user_repository_factory()
container = Container()
user_repository = container.user_repository_factory()
user1, user2 = user_repository.get_all()

View File

@ -1,6 +1,6 @@
"""`Factory` provider init injections example."""
from dependency_injector import providers
from dependency_injector import containers, providers
class Photo:
@ -13,23 +13,27 @@ class User:
self.main_photo = main_photo
photo_factory = providers.Factory(Photo)
user_factory = providers.Factory(
User,
main_photo=photo_factory,
)
class Container(containers.DeclarativeContainer):
photo_factory = providers.Factory(Photo)
user_factory = providers.Factory(
User,
main_photo=photo_factory,
)
if __name__ == '__main__':
user1 = user_factory(1)
container = Container()
user1 = container.user_factory(1)
# Same as: # user1 = User(1, main_photo=Photo())
user2 = user_factory(2)
user2 = container.user_factory(2)
# Same as: # user2 = User(2, main_photo=Photo())
# Context keyword arguments have a priority:
another_photo = Photo()
user3 = user_factory(
user3 = container.user_factory(
uid=3,
main_photo=another_photo,
)

View File

@ -1,6 +1,6 @@
"""`Factory` provider - passing injections to the underlying providers example."""
from dependency_injector import providers
from dependency_injector import containers, providers
class Regularizer:
@ -23,26 +23,31 @@ class Algorithm:
self.task = task
algorithm_factory = providers.Factory(
Algorithm,
task=providers.Factory(
ClassificationTask,
loss=providers.Factory(
Loss,
regularizer=providers.Factory(
Regularizer,
class Container(containers.DeclarativeContainer):
algorithm_factory = providers.Factory(
Algorithm,
task=providers.Factory(
ClassificationTask,
loss=providers.Factory(
Loss,
regularizer=providers.Factory(
Regularizer,
),
),
),
),
)
)
if __name__ == '__main__':
algorithm_1 = algorithm_factory(task__loss__regularizer__alpha=0.5)
container = Container()
algorithm_1 = container.algorithm_factory(
task__loss__regularizer__alpha=0.5,
)
assert algorithm_1.task.loss.regularizer.alpha == 0.5
algorithm_2 = algorithm_factory(task__loss__regularizer__alpha=0.7)
algorithm_2 = container.algorithm_factory(
task__loss__regularizer__alpha=0.7,
)
assert algorithm_2.task.loss.regularizer.alpha == 0.7
algorithm_3 = algorithm_factory(task__loss__regularizer=Regularizer(alpha=0.8))
assert algorithm_3.task.loss.regularizer.alpha == 0.8

View File

@ -1,6 +1,6 @@
"""`Factory` specialization with limitation to provided type example."""
from dependency_injector import providers, errors
from dependency_injector import containers, providers, errors
class BaseService:
@ -17,11 +17,15 @@ class ServiceProvider(providers.Factory):
# Creating service provider with a correct provided type:
some_service_provider = ServiceProvider(SomeService)
class Services(containers.DeclarativeContainer):
some_service_provider = ServiceProvider(SomeService)
# Trying to create service provider an incorrect provided type:
try:
some_service_provider = ServiceProvider(object)
class Container(containers.DeclarativeContainer):
some_service_provider = ServiceProvider(object)
except errors.Error as exception:
print(exception)
# The output is:

View File

@ -3,7 +3,7 @@
import dataclasses
from typing import List
from dependency_injector import providers
from dependency_injector import containers, providers
@dataclasses.dataclass
@ -16,23 +16,27 @@ class Dispatcher:
modules: List[Module]
dispatcher_factory = providers.Factory(
Dispatcher,
modules=providers.List(
providers.Factory(Module, name='m1'),
providers.Factory(Module, name='m2'),
),
)
class Container(containers.DeclarativeContainer):
dispatcher_factory = providers.Factory(
Dispatcher,
modules=providers.List(
providers.Factory(Module, name='m1'),
providers.Factory(Module, name='m2'),
),
)
if __name__ == '__main__':
dispatcher = dispatcher_factory()
container = Container()
dispatcher = container.dispatcher_factory()
assert isinstance(dispatcher.modules, list)
assert dispatcher.modules[0].name == 'm1'
assert dispatcher.modules[1].name == 'm2'
# Call "dispatcher = dispatcher_factory()" is an equivalent for:
# Call "dispatcher = container.dispatcher_factory()" is equivalent to:
# dispatcher = Dispatcher(
# modules=[
# Module(name='m1'),

View File

@ -1,10 +1,14 @@
"""`Object` provider example."""
from dependency_injector import providers
from dependency_injector import containers, providers
object_provider = providers.Object(1)
class Container(containers.DeclarativeContainer):
object_provider = providers.Object(1)
if __name__ == '__main__':
assert object_provider() == 1
container = Container()
assert container.object_provider() == 1

View File

@ -1,8 +1,9 @@
"""Simple providers overriding example."""
import dataclasses
import unittest.mock
from dependency_injector import providers
from dependency_injector import containers, providers
class ApiClient:
@ -13,30 +14,35 @@ class ApiClientStub(ApiClient):
...
@dataclasses.dataclass
class Service:
def __init__(self, api_client: ApiClient):
self._api_client = api_client
api_client: ApiClient
api_client_factory = providers.Factory(ApiClient)
service_factory = providers.Factory(
Service,
api_client=api_client_factory,
)
class Container(containers.DeclarativeContainer):
api_client_factory = providers.Factory(ApiClient)
service_factory = providers.Factory(
Service,
api_client=api_client_factory,
)
if __name__ == '__main__':
container = Container()
# 1. Use .override() to replace the API client with stub
api_client_factory.override(providers.Factory(ApiClientStub))
service1 = service_factory()
container.api_client_factory.override(providers.Factory(ApiClientStub))
service1 = container.service_factory()
assert isinstance(service1.api_client, ApiClientStub)
# 2. Use .override() as a context manager to mock the API client in testing
with api_client_factory.override(unittest.mock.Mock(ApiClient)):
service3 = service_factory()
assert isinstance(service3.api_client, unittest.mock.Mock)
with container.api_client_factory.override(unittest.mock.Mock(ApiClient)):
service2 = container.service_factory()
assert isinstance(service2.api_client, unittest.mock.Mock)
# 3. Use .reset_override() to get back to normal
api_client_factory.reset_override()
service3 = service_factory()
container.api_client_factory.reset_override()
service3 = container.service_factory()
assert isinstance(service3.api_client, ApiClient)

View File

@ -1,6 +1,6 @@
"""Example of the injecting of provided instance attributes and items."""
from dependency_injector import providers
from dependency_injector import containers, providers
class Service:
@ -23,17 +23,21 @@ class Client:
self.value4 = value4
service = providers.Singleton(Service)
class Container(containers.DeclarativeContainer):
client_factory = providers.Factory(
Client,
value1=service.provided[0],
value2=service.provided.value,
value3=service.provided.values[0],
value4=service.provided.get_value.call(),
)
service = providers.Singleton(Service)
client_factory = providers.Factory(
Client,
value1=service.provided[0],
value2=service.provided.value,
value3=service.provided.values[0],
value4=service.provided.get_value.call(),
)
if __name__ == '__main__':
client = client_factory()
container = Container()
client = container.client_factory()
assert client.value1 == client.value2 == client.value3 == 'foo'

View File

@ -1,6 +1,6 @@
"""Complex example of the injecting of provided instance attributes and items."""
from dependency_injector import providers
from dependency_injector import containers, providers
class Service:
@ -12,31 +12,35 @@ class Service:
return self.value
service = providers.Singleton(Service, value=42)
class Container(containers.DeclarativeContainer):
dependency = providers.Object(
{
'foo': {
'bar': 10,
'baz': lambda arg: {'arg': arg}
service = providers.Singleton(Service, value=42)
dependency = providers.Object(
{
'foo': {
'bar': 10,
'baz': lambda arg: {'arg': arg}
},
},
},
)
)
demo_list = providers.List(
dependency.provided['foo']['bar'],
dependency.provided['foo']['baz'].call(22)['arg'],
dependency.provided['foo']['baz'].call(service)['arg'],
dependency.provided['foo']['baz'].call(service)['arg'].value,
dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(),
)
demo_list = providers.List(
dependency.provided['foo']['bar'],
dependency.provided['foo']['baz'].call(22)['arg'],
dependency.provided['foo']['baz'].call(service)['arg'],
dependency.provided['foo']['baz'].call(service)['arg'].value,
dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(),
)
if __name__ == '__main__':
assert demo_list() == [
container = Container()
assert container.demo_list() == [
10,
22,
service(),
container.service(),
42,
42,
]

View File

@ -1,6 +1,6 @@
"""`Selector` provider example."""
from dependency_injector import providers
from dependency_injector import containers, providers
class SomeClass:
@ -11,19 +11,24 @@ class SomeOtherClass:
...
config = providers.Configuration()
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
selector = providers.Selector(
config.one_or_another,
one=providers.Factory(SomeClass),
another=providers.Factory(SomeOtherClass),
)
selector = providers.Selector(
config.one_or_another,
one=providers.Factory(SomeClass),
another=providers.Factory(SomeOtherClass),
)
if __name__ == '__main__':
config.override({'one_or_another': 'one'})
instance_1 = selector()
container = Container()
container.config.override({'one_or_another': 'one'})
instance_1 = container.selector()
assert isinstance(instance_1, SomeClass)
config.override({'one_or_another': 'another'})
instance_2 = selector()
container.config.override({'one_or_another': 'another'})
instance_2 = container.selector()
assert isinstance(instance_2, SomeOtherClass)

View File

@ -1,16 +1,20 @@
"""`Singleton` provider example."""
from dependency_injector import providers
from dependency_injector import containers, providers
class UserService:
...
user_service_provider = providers.Singleton(UserService)
class Container(containers.DeclarativeContainer):
user_service_provider = providers.Singleton(UserService)
if __name__ == '__main__':
user_service1 = user_service_provider()
user_service2 = user_service_provider()
container = Container()
user_service1 = container.user_service_provider()
user_service2 = container.user_service_provider()
assert user_service1 is user_service2

View File

@ -0,0 +1,24 @@
"""`Singleton` provider resetting example."""
from dependency_injector import containers, providers
class UserService:
...
class Container(containers.DeclarativeContainer):
user_service_provider = providers.Singleton(UserService)
if __name__ == '__main__':
container1 = Container()
user_service1 = container1.user_service_provider()
assert user_service1 is container1.user_service_provider()
container2 = Container()
user_service2 = container2.user_service_provider()
assert user_service2 is container2.user_service_provider()
assert user_service1 is not user_service2

View File

@ -1,19 +1,23 @@
"""`Singleton` provider resetting example."""
from dependency_injector import providers
from dependency_injector import containers, providers
class UserService:
...
user_service_provider = providers.Singleton(UserService)
class Container(containers.DeclarativeContainer):
user_service_provider = providers.Singleton(UserService)
if __name__ == '__main__':
user_service1 = user_service_provider()
container = Container()
user_service_provider.reset()
user_service1 = container.user_service_provider()
users_service2 = user_service_provider()
assert users_service2 is not user_service1
container.user_service_provider.reset()
user_service2 = container.user_service_provider()
assert user_service2 is not user_service1

View File

@ -1,30 +1,35 @@
"""`Singleton` - Flask request scope example."""
from dependency_injector import providers
from flask import Flask
from dependency_injector import containers, providers
from flask import Flask, current_app
class Service:
...
service_provider = providers.Singleton(Service)
class Container(containers.DeclarativeContainer):
service_provider = providers.Singleton(Service)
def index_view():
service_1 = service_provider()
service_2 = service_provider()
service_1 = current_app.container.service_provider()
service_2 = current_app.container.service_provider()
assert service_1 is service_2
print(service_1)
return 'Hello World!'
def teardown_context(request):
service_provider.reset()
current_app.container.service_provider.reset()
return request
container = Container()
app = Flask(__name__)
app.container = container
app.add_url_rule('/', 'index', view_func=index_view)
app.after_request(teardown_context)

View File

@ -3,31 +3,39 @@
import threading
import queue
from dependency_injector import providers
from dependency_injector import containers, providers
def put_in_queue(example_object, queue_object):
queue_object.put(example_object)
thread_local_object = providers.ThreadLocalSingleton(object)
queue_provider = providers.ThreadSafeSingleton(queue.Queue)
put_in_queue = providers.Callable(
put_in_queue,
example_object=thread_local_object,
queue_object=queue_provider,
)
thread_factory = providers.Factory(
threading.Thread,
target=put_in_queue.provider,
)
class Container(containers.DeclarativeContainer):
thread_local_object = providers.ThreadLocalSingleton(object)
queue_provider = providers.ThreadSafeSingleton(queue.Queue)
put_in_queue = providers.Callable(
put_in_queue,
example_object=thread_local_object,
queue_object=queue_provider,
)
thread_factory = providers.Factory(
threading.Thread,
target=put_in_queue.provider,
)
if __name__ == '__main__':
container = Container()
n = 10
threads = []
for thread_number in range(10):
for thread_number in range(n):
threads.append(
thread_factory(name='Thread{0}'.format(thread_number)),
container.thread_factory(name='Thread{0}'.format(thread_number)),
)
for thread in threads:
thread.start()
@ -35,9 +43,9 @@ if __name__ == '__main__':
thread.join()
all_objects = set()
while not queue_provider().empty():
all_objects.add(queue_provider().get())
while not container.queue_provider().empty():
all_objects.add(container.queue_provider().get())
assert len(all_objects) == len(threads)
assert len(all_objects) == len(threads) == n
# Queue contains same number of objects as number of threads where
# thread-local singleton provider was used.

View File

@ -1,6 +1,6 @@
"""Dependency injector top-level package."""
__version__ = '3.36.0'
__version__ = '3.37.0'
"""Version number that follows semantic versioning.
:type: str

View File

@ -283,8 +283,8 @@ class Selector(Provider):
class ProvidedInstanceFluentInterface:
def __getattr__(self, item: str) -> AttributeGetter: ...
def __getitem__(self, item: str) -> ItemGetter: ...
def __getattr__(self, item: Any) -> AttributeGetter: ...
def __getitem__(self, item: Any) -> ItemGetter: ...
def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...