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. ``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? What is dependency injection?
----------------------------- -----------------------------

View File

@ -1,3 +1,5 @@
.. _containers:
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. ``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). Key features of the ``Dependency Injector``:
- Do no magic to your code.
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)*. .. code-block:: python
- **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: 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**. 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 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 what_is_di
di_in_python di_in_python
key_features key_features
structure

View File

@ -3,66 +3,33 @@ Key features
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control :keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes key features of "Dependency Injector" :description: This article describes key features of the Dependency Injector
framework. It also provides some cases and recommendations framework.
about usage of "Dependency Injector" framework.
Key features of the ``Dependency Injector``:
``Dependency Injector`` is a dependency injection framework for Python. - **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
It was designed to be a unified and developer-friendly tool that helps ``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your
implement a dependency injection design pattern in a formal, pretty, and objects. See :ref:`providers`.
Pythonic way. - **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). - **Explicit is better than implicit (PEP20)**.
- Do no magic to your code. - **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)*. - **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. - **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: The power of the framework is in a simplicity. ``Dependency Injector`` is a simple tool for the powerful concept.
- 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.
.. disqus:: .. 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 From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_ 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 3.36.0
------ ------
- Update providers overriding documentation and rework examples. - Update providers overriding documentation and rework examples.

View File

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

View File

@ -1,3 +1,5 @@
.. _configuration-provider:
Configuration provider Configuration provider
====================== ======================
@ -14,8 +16,8 @@ Configuration provider
.. literalinclude:: ../../examples/providers/configuration/configuration.py .. literalinclude:: ../../examples/providers/configuration/configuration.py
:language: python :language: python
:emphasize-lines: 4,9-10 :emphasize-lines: 7,12-13
:lines: 4-14 :lines: 3-
It implements the principle "use first, define later". It implements the principle "use first, define later".
@ -27,8 +29,8 @@ Loading from an INI file
.. literalinclude:: ../../examples/providers/configuration/configuration_ini.py .. literalinclude:: ../../examples/providers/configuration/configuration_ini.py
:language: python :language: python
:lines: 3-5,6- :lines: 3-
:emphasize-lines: 6 :emphasize-lines: 12
where ``examples/providers/configuration/config.ini`` is: where ``examples/providers/configuration/config.ini`` is:
@ -47,8 +49,8 @@ Loading from a YAML file
.. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py .. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py
:language: python :language: python
:lines: 3-5,6- :lines: 3-
:emphasize-lines: 6 :emphasize-lines: 12
where ``examples/providers/configuration/config.yml`` is: where ``examples/providers/configuration/config.yml`` is:
@ -81,8 +83,8 @@ Loading from a dictionary
.. literalinclude:: ../../examples/providers/configuration/configuration_dict.py .. literalinclude:: ../../examples/providers/configuration/configuration_dict.py
:language: python :language: python
:lines: 3-5,6- :lines: 3-
:emphasize-lines: 6-13 :emphasize-lines: 12-19
Loading from an environment variable Loading from an environment variable
------------------------------------ ------------------------------------
@ -92,8 +94,8 @@ Loading from an environment variable
.. literalinclude:: ../../examples/providers/configuration/configuration_env.py .. literalinclude:: ../../examples/providers/configuration/configuration_env.py
:language: python :language: python
:lines: 5-7,13-21 :lines: 3-
:emphasize-lines: 6-8 :emphasize-lines: 18-20
Loading from the multiple sources Loading from the multiple sources
--------------------------------- ---------------------------------
@ -103,8 +105,8 @@ configuration is merged recursively over the existing configuration.
.. literalinclude:: ../../examples/providers/configuration/configuration_multiple.py .. literalinclude:: ../../examples/providers/configuration/configuration_multiple.py
:language: python :language: python
:lines: 3-5,6-14 :lines: 3-
:emphasize-lines: 6-7 :emphasize-lines: 12-13
where ``examples/providers/configuration/config.local.yml`` is: 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 .. literalinclude:: ../../examples/providers/configuration/configuration_type.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 17 :emphasize-lines: 19
``Configuration`` provider has next helper methods: ``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 .. literalinclude:: ../../examples/providers/configuration/configuration_type_custom.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 16 :emphasize-lines: 18
With the ``.as_(callback, *args, **kwargs)`` you can specify a function that will be called 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 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. 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:: .. 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 .. literalinclude:: ../../examples/providers/factory_init_injections_underlying.py
:language: python :language: python
:lines: 3- :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 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. 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 .. literalinclude:: ../../examples/providers/factory_delegation.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 25 :emphasize-lines: 28
.. note:: Any provider has a ``.provider`` attribute. .. note:: Any provider has a ``.provider`` attribute.
@ -135,7 +135,7 @@ provider with two peculiarities:
.. literalinclude:: ../../examples/providers/abstract_factory.py .. literalinclude:: ../../examples/providers/abstract_factory.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 32 :emphasize-lines: 34
Factory aggregate Factory aggregate
----------------- -----------------
@ -155,7 +155,7 @@ rest of the arguments are passed to the delegated ``Factory``.
.. literalinclude:: ../../examples/providers/factory_aggregate.py .. literalinclude:: ../../examples/providers/factory_aggregate.py
:language: python :language: python
:lines: 3- :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 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 ``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 .. literalinclude:: ../../examples/providers/list.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 19-22 :emphasize-lines: 21-24
``List`` provider handles positional arguments the same way as a :ref:`factory-provider`. ``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 .. literalinclude:: ../../examples/providers/provided_instance.py
:language: python :language: python
:emphasize-lines: 26-32 :emphasize-lines: 28-34
:lines: 3- :lines: 3-
To use the feature you should use the ``.provided`` attribute of the injected provider. This 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 .. literalinclude:: ../../examples/providers/provided_instance_complex.py
:language: python :language: python
:emphasize-lines: 24-30 :emphasize-lines: 26-32
:lines: 3- :lines: 3-
The ``.provided`` attribute is available for the next providers: The ``.provided`` attribute is available for the next providers:

View File

@ -17,7 +17,7 @@ Selector provider
.. literalinclude:: ../../examples/providers/selector.py .. literalinclude:: ../../examples/providers/selector.py
:language: python :language: python
:lines: 3- :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 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 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:`factory-specialize-provided-type`
- :ref:`abstract-factory` - :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 Resetting memorized object
-------------------------- --------------------------
@ -41,7 +48,7 @@ provider.
.. literalinclude:: ../../examples/providers/singleton_resetting.py .. literalinclude:: ../../examples/providers/singleton_resetting.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 14 :emphasize-lines: 18
.. note:: .. note::
Resetting of the memorized object clears the reference to it. Further object's lifecycle is 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 .. literalinclude:: ../../examples/providers/singleton_thread_locals.py
:language: python :language: python
:lines: 3- :lines: 3-
:emphasize-lines: 11,12 :emphasize-lines: 13,15
Implementing scopes Implementing scopes
------------------- -------------------

View File

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

View File

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

View File

@ -1,21 +1,24 @@
"""`Configuration` provider example.""" """`Configuration` provider example."""
import boto3 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( config = providers.Configuration()
s3_client_factory = providers.Factory(
boto3.client, boto3.client,
's3', 's3',
aws_access_key_id=config.aws.access_key_id, aws_access_key_id=config.aws.access_key_id,
aws_secret_access_key=config.aws.secret_access_key, aws_secret_access_key=config.aws.secret_access_key,
) )
if __name__ == '__main__': if __name__ == '__main__':
config.from_dict( container = Container()
container.config.from_dict(
{ {
'aws': { 'aws': {
'access_key_id': 'KEY', '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.""" """`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': { 'aws': {
'access_key_id': 'KEY', 'access_key_id': 'KEY',
'secret_access_key': 'SECRET', 'secret_access_key': 'SECRET',
}, },
}, },
) )
assert config() == {'aws': {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}} assert container.config() == {
assert config.aws() == {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'} 'aws': {
assert config.aws.access_key_id() == 'KEY' 'access_key_id': 'KEY',
assert config.aws.secret_access_key() == 'SECRET' '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 import os
from dependency_injector import providers from dependency_injector import containers, providers
# Emulate environment variables class Container(containers.DeclarativeContainer):
os.environ['AWS_ACCESS_KEY_ID'] = 'KEY'
os.environ['AWS_SECRET_ACCESS_KEY'] = 'SECRET' config = providers.Configuration()
config = providers.Configuration() if __name__ == '__main__':
container = Container()
config.aws.access_key_id.from_env('AWS_ACCESS_KEY_ID') # Emulate environment variables
config.aws.secret_access_key.from_env('AWS_SECRET_ACCESS_KEY') os.environ['AWS_ACCESS_KEY_ID'] = 'KEY'
config.optional.from_env('UNDEFINED', 'default_value') os.environ['AWS_SECRET_ACCESS_KEY'] = 'SECRET'
assert config.aws.access_key_id() == 'KEY' container.config.aws.access_key_id.from_env('AWS_ACCESS_KEY_ID')
assert config.aws.secret_access_key() == 'SECRET' container.config.aws.secret_access_key.from_env('AWS_SECRET_ACCESS_KEY')
assert config.optional() == 'default_value' 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.""" """`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'} if __name__ == '__main__':
assert config.aws.access_key_id() == 'KEY' container = Container()
assert config.aws.secret_access_key() == 'SECRET'
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. """`Configuration` provider dynamic item selector."""
Details: https://github.com/ets-labs/python-dependency-injector/issues/274
"""
import dataclasses import dataclasses
from dependency_injector import providers from dependency_injector import containers, providers
@dataclasses.dataclass @dataclasses.dataclass
@ -14,7 +11,9 @@ class Foo:
option2: object option2: object
config = providers.Configuration(default={ class Container(containers.DeclarativeContainer):
config = providers.Configuration(default={
'target': 'A', 'target': 'A',
'items': { 'items': {
'A': { 'A': {
@ -26,22 +25,23 @@ config = providers.Configuration(default={
'option2': 20, 'option2': 20,
}, },
}, },
}) })
foo = providers.Factory( foo_factory = providers.Factory(
Foo, Foo,
option1=config.items[config.target].option1, option1=config.items[config.target].option1,
option2=config.items[config.target].option2, option2=config.items[config.target].option2,
) )
if __name__ == '__main__': if __name__ == '__main__':
config.target.from_env('TARGET') container = Container()
f = foo()
print(f.option1, f.option2)
container.config.target.from_env('TARGET')
foo = container.foo_factory()
print(foo.option1, foo.option2)
# $ TARGET=A python configuration_itemselector.py # $ TARGET=A python configuration_itemselector.py
# 60 80 # 60 80
# $ TARGET=B python configuration_itemselector.py # $ TARGET=B python configuration_itemselector.py
# 10 20 # 10 20

View File

@ -1,14 +1,28 @@
"""`Configuration` provider values loading example.""" """`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()
config.from_yaml('examples/providers/configuration/config.local.yml')
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'} if __name__ == '__main__':
assert config.aws.access_key_id() == 'LOCAL-KEY' container = Container()
assert config.aws.secret_access_key() == 'LOCAL-SECRET'
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 import os
from dependency_injector import providers from dependency_injector import containers, providers
class ApiClient: class ApiClient:
@ -11,24 +11,28 @@ class ApiClient:
self.timeout = timeout self.timeout = timeout
config = providers.Configuration() class Container(containers.DeclarativeContainer):
api_client_factory = providers.Factory( config = providers.Configuration()
api_client_factory = providers.Factory(
ApiClient, ApiClient,
api_key=config.api.key, api_key=config.api.key,
timeout=config.api.timeout.as_int(), timeout=config.api.timeout.as_int(),
) )
if __name__ == '__main__': if __name__ == '__main__':
container = Container()
# Emulate environment variables # Emulate environment variables
os.environ['API_KEY'] = 'secret' os.environ['API_KEY'] = 'secret'
os.environ['API_TIMEOUT'] = '5' os.environ['API_TIMEOUT'] = '5'
config.api.key.from_env('API_KEY') container.config.api.key.from_env('API_KEY')
config.api.timeout.from_env('API_TIMEOUT') 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.api_key == 'secret'
assert api_client.timeout == 5 assert api_client.timeout == 5

View File

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

View File

@ -1,13 +1,27 @@
"""`Configuration` provider values loading example.""" """`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'} if __name__ == '__main__':
assert config.aws.access_key_id() == 'KEY' container = Container()
assert config.aws.secret_access_key() == 'SECRET'
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 import asyncio
from dependency_injector import providers from dependency_injector import containers, providers
async def coroutine(arg1, arg2): async def coroutine(arg1, arg2):
@ -10,10 +10,14 @@ async def coroutine(arg1, arg2):
return 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__': if __name__ == '__main__':
arg1, arg2 = asyncio.run(coroutine_provider()) container = Container()
arg1, arg2 = asyncio.run(container.coroutine_provider())
assert (arg1, arg2) == (1, 2) 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.""" """Custom provider example."""
from dependency_injector import providers from dependency_injector import containers, providers
class CustomFactory(providers.Provider): class CustomFactory(providers.Provider):
@ -29,14 +29,18 @@ class CustomFactory(providers.Provider):
return self._factory(*args, **kwargs) return self._factory(*args, **kwargs)
factory = CustomFactory(object) class Container(containers.DeclarativeContainer):
factory = CustomFactory(object)
if __name__ == '__main__': if __name__ == '__main__':
object1 = factory() container = Container()
object1 = container.factory()
assert isinstance(object1, object) assert isinstance(object1, object)
object2 = factory() object2 = container.factory()
assert isinstance(object1, object) assert isinstance(object1, object)
assert object1 is not object2 assert object1 is not object2

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
"""`Factory` provider - passing injections to the underlying providers example.""" """`Factory` provider - passing injections to the underlying providers example."""
from dependency_injector import providers from dependency_injector import containers, providers
class Regularizer: class Regularizer:
@ -23,7 +23,9 @@ class Algorithm:
self.task = task self.task = task
algorithm_factory = providers.Factory( class Container(containers.DeclarativeContainer):
algorithm_factory = providers.Factory(
Algorithm, Algorithm,
task=providers.Factory( task=providers.Factory(
ClassificationTask, ClassificationTask,
@ -34,15 +36,18 @@ algorithm_factory = providers.Factory(
), ),
), ),
), ),
) )
if __name__ == '__main__': 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 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 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.""" """`Factory` specialization with limitation to provided type example."""
from dependency_injector import providers, errors from dependency_injector import containers, providers, errors
class BaseService: class BaseService:
@ -17,10 +17,14 @@ class ServiceProvider(providers.Factory):
# Creating service provider with a correct provided type: # 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: # Trying to create service provider an incorrect provided type:
try: try:
class Container(containers.DeclarativeContainer):
some_service_provider = ServiceProvider(object) some_service_provider = ServiceProvider(object)
except errors.Error as exception: except errors.Error as exception:
print(exception) print(exception)

View File

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

View File

@ -1,10 +1,14 @@
"""`Object` provider example.""" """`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__': if __name__ == '__main__':
assert object_provider() == 1 container = Container()
assert container.object_provider() == 1

View File

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

View File

@ -1,6 +1,6 @@
"""Example of the injecting of provided instance attributes and items.""" """Example of the injecting of provided instance attributes and items."""
from dependency_injector import providers from dependency_injector import containers, providers
class Service: class Service:
@ -23,17 +23,21 @@ class Client:
self.value4 = value4 self.value4 = value4
service = providers.Singleton(Service) class Container(containers.DeclarativeContainer):
client_factory = providers.Factory( service = providers.Singleton(Service)
client_factory = providers.Factory(
Client, Client,
value1=service.provided[0], value1=service.provided[0],
value2=service.provided.value, value2=service.provided.value,
value3=service.provided.values[0], value3=service.provided.values[0],
value4=service.provided.get_value.call(), value4=service.provided.get_value.call(),
) )
if __name__ == '__main__': if __name__ == '__main__':
client = client_factory() container = Container()
client = container.client_factory()
assert client.value1 == client.value2 == client.value3 == 'foo' 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.""" """Complex example of the injecting of provided instance attributes and items."""
from dependency_injector import providers from dependency_injector import containers, providers
class Service: class Service:
@ -12,31 +12,35 @@ class Service:
return self.value return self.value
service = providers.Singleton(Service, value=42) class Container(containers.DeclarativeContainer):
dependency = providers.Object( service = providers.Singleton(Service, value=42)
dependency = providers.Object(
{ {
'foo': { 'foo': {
'bar': 10, 'bar': 10,
'baz': lambda arg: {'arg': arg} 'baz': lambda arg: {'arg': arg}
}, },
}, },
) )
demo_list = providers.List( demo_list = providers.List(
dependency.provided['foo']['bar'], dependency.provided['foo']['bar'],
dependency.provided['foo']['baz'].call(22)['arg'], dependency.provided['foo']['baz'].call(22)['arg'],
dependency.provided['foo']['baz'].call(service)['arg'], dependency.provided['foo']['baz'].call(service)['arg'],
dependency.provided['foo']['baz'].call(service)['arg'].value, dependency.provided['foo']['baz'].call(service)['arg'].value,
dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(), dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(),
) )
if __name__ == '__main__': if __name__ == '__main__':
assert demo_list() == [ container = Container()
assert container.demo_list() == [
10, 10,
22, 22,
service(), container.service(),
42, 42,
42, 42,
] ]

View File

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

View File

@ -1,16 +1,20 @@
"""`Singleton` provider example.""" """`Singleton` provider example."""
from dependency_injector import providers from dependency_injector import containers, providers
class UserService: class UserService:
... ...
user_service_provider = providers.Singleton(UserService) class Container(containers.DeclarativeContainer):
user_service_provider = providers.Singleton(UserService)
if __name__ == '__main__': if __name__ == '__main__':
user_service1 = user_service_provider() container = Container()
user_service2 = user_service_provider()
user_service1 = container.user_service_provider()
user_service2 = container.user_service_provider()
assert user_service1 is user_service2 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.""" """`Singleton` provider resetting example."""
from dependency_injector import providers from dependency_injector import containers, providers
class UserService: class UserService:
... ...
user_service_provider = providers.Singleton(UserService) class Container(containers.DeclarativeContainer):
user_service_provider = providers.Singleton(UserService)
if __name__ == '__main__': 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() container.user_service_provider.reset()
assert users_service2 is not user_service1
user_service2 = container.user_service_provider()
assert user_service2 is not user_service1

View File

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

View File

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

View File

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

View File

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