Merge branch 'release/3.35.0' into master

This commit is contained in:
Roman Mogylatov 2020-09-01 21:40:31 -04:00
commit 89fec6c905
33 changed files with 371 additions and 573 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -7,6 +7,12 @@ 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.35.0
------
- Update documentation and rework examples for: ``Singleton``, ``Callable``, ``Coroutine``,
``Object``, ``List``, ``Configuration``, ``Selector``, and ``Dependency`` providers.
- Fix mypy stub of the ``DeclarativeContainer`` to specify the ``__init__`` interface.
3.34.0 3.34.0
------ ------
- Update ``Factory`` provider documentation. - Update ``Factory`` provider documentation.

View File

@ -1,69 +1,20 @@
Callable providers Callable provider
------------------ -----------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Function,Method,Example
:description: Callable provider helps to make dependencies injection into functions. This page
demonstrates how to use a Callable provider.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
:py:class:`Callable` provider calls wrapped callable on every call. :py:class:`Callable` provider calls a function, a method or another callable.
Callable providers and injections .. literalinclude:: ../../examples/providers/callable.py
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Callable` provider takes a various number of positional and keyword
arguments that are used as wrapped callable injections. Every time, when
:py:class:`Callable` provider is called, positional and keyword argument
injections would be passed as callable arguments.
Injections are done according to the next rules:
+ All providers (instances of :py:class:`Provider`) are called every time
when injection needs to be done.
+ Providers could be injected "as is" (delegated), if it is defined obviously.
Check out :ref:`callable_providers_delegation`.
+ All other injectable values are provided *"as is"*.
+ Positional context arguments will be appended after :py:class:`Callable`
positional injections.
+ Keyword context arguments have priority on :py:class:`Callable` keyword
injections and will be merged over them.
Example that shows usage of :py:class:`Callable` with positional argument
injections:
.. literalinclude:: ../../examples/providers/callable_args.py
:language: python :language: python
:lines: 3-
Next one example shows usage of :py:class:`Callable` with keyword argument ``Callable`` provider handles an injection of the dependencies the same way like a
injections: :ref:`factory-provider`.
.. image:: /images/providers/callable.png
:width: 100%
:align: center
.. literalinclude:: ../../examples/providers/callable_kwargs.py
:language: python
.. _callable_providers_delegation:
Callable providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Callable` provider could be delegated to any other provider via
any kind of injection.
Delegation of :py:class:`Callable` providers is the same as
:py:class:`Factory` providers delegation, please follow
:ref:`factory_providers_delegation` section for examples (with exception
of using :py:class:`DelegatedCallable` instead of
:py:class:`DelegatedFactory`).
Abstract callable providers
~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`AbstractCallable` provider is a :py:class:`Callable` provider that
must be explicitly overridden before calling.
Behaviour of :py:class:`AbstractCallable` providers is the same as of
:py:class:`AbstractFactory`, please follow :ref:`abstract_factory_providers`
section for examples (with exception of using :py:class:`AbstractCallable`
provider instead of :py:class:`AbstractFactory`).
.. disqus:: .. disqus::

View File

@ -1,5 +1,12 @@
Configuration providers Configuration provider
----------------------- ----------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection,
Option,Ini,Json,Yaml,Dict,Environment Variable,Load,Read,Get
:description: Configuration provides configuration options to the other providers. This page
demonstrates how to use Configuration provider to inject the dependencies, load
a configuration from an ini or yaml file, dictionary or an environment variable.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
@ -10,13 +17,13 @@ Configuration providers
:emphasize-lines: 4,9-10 :emphasize-lines: 4,9-10
:lines: 4-14 :lines: 4-14
It implements "use first, define later" principle. It implements the principle "use first, define later".
Loading from ``ini`` file Loading from an INI file
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Configuration` provider can load configuration from ``ini`` file using ``Configuration`` provider can load configuration from an ``ini`` file using the
:py:meth:`Configuration.from_ini`: :py:meth:`Configuration.from_ini` method:
.. literalinclude:: ../../examples/providers/configuration/configuration_ini.py .. literalinclude:: ../../examples/providers/configuration/configuration_ini.py
:language: python :language: python
@ -28,15 +35,15 @@ where ``examples/providers/configuration/config.ini`` is:
.. literalinclude:: ../../examples/providers/configuration/config.ini .. literalinclude:: ../../examples/providers/configuration/config.ini
:language: ini :language: ini
:py:meth:`Configuration.from_ini` supports environment variables interpolation. Use :py:meth:`Configuration.from_ini` method supports environment variables interpolation. Use
``${ENV_NAME}`` format in the configuration file to substitute value of environment ``${ENV_NAME}`` format in the configuration file to substitute value of the environment
variable ``ENV_NAME``. variable ``ENV_NAME``.
Loading from ``yaml`` file Loading from a YAML file
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Configuration` provider can load configuration from ``yaml`` file using ``Configuration`` provider can load configuration from a ``yaml`` file using the
:py:meth:`Configuration.from_yaml`: :py:meth:`Configuration.from_yaml` method:
.. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py .. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py
:language: python :language: python
@ -48,43 +55,51 @@ where ``examples/providers/configuration/config.yml`` is:
.. literalinclude:: ../../examples/providers/configuration/config.yml .. literalinclude:: ../../examples/providers/configuration/config.yml
:language: ini :language: ini
:py:meth:`Configuration.from_yaml` supports environment variables interpolation. Use :py:meth:`Configuration.from_yaml` method supports environment variables interpolation. Use
``${ENV_NAME}`` format in the configuration file to substitute value of environment ``${ENV_NAME}`` format in the configuration file to substitute value of the environment
variable ``ENV_NAME``. variable ``ENV_NAME``.
.. note:: .. note::
Loading configuration from yaml requires ``PyYAML`` package. You can install Loading of a yaml configuration requires ``PyYAML`` package.
`Dependency Injector` with extras ``pip install dependency-injector[yaml]`` or install
``PyYAML`` separately ``pip install pyyaml``.
Loading from ``dict`` You can install the ``Dependency Injector`` with an extra dependency::
~~~~~~~~~~~~~~~~~~~~~
:py:class:`Configuration` provider can load configuration from Python ``dict`` using pip install dependency-injector[yaml]
:py:meth:`Configuration.from_dict`:
or install ``PyYAML`` directly::
pip install pyyaml
*Don't forget to mirror the changes in the requirements file.*
Loading from a dictionary
~~~~~~~~~~~~~~~~~~~~~~~~~
``Configuration`` provider can load configuration from a Python ``dict`` using the
:py:meth:`Configuration.from_dict` method:
.. literalinclude:: ../../examples/providers/configuration/configuration_dict.py .. literalinclude:: ../../examples/providers/configuration/configuration_dict.py
:language: python :language: python
:lines: 3-5,6- :lines: 3-5,6-
:emphasize-lines: 6-13 :emphasize-lines: 6-13
Loading from environment variable Loading from an environment variable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Configuration` provider can load configuration from environment variable using ``Configuration`` provider can load configuration from an environment variable using the
:py:meth:`Configuration.from_env`: :py:meth:`Configuration.from_env` method:
.. literalinclude:: ../../examples/providers/configuration/configuration_env.py .. literalinclude:: ../../examples/providers/configuration/configuration_env.py
:language: python :language: python
:lines: 5-7,13-21 :lines: 5-7,13-21
:emphasize-lines: 6-8 :emphasize-lines: 6-8
Loading from multiple sources Loading from the multiple sources
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Configuration` provider can load configuration from multiple sources. Loaded ``Configuration`` provider can load configuration from the multiple sources. Loaded
configuration is merged recursively over existing configuration. 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
@ -96,12 +111,12 @@ where ``examples/providers/configuration/config.local.yml`` is:
.. literalinclude:: ../../examples/providers/configuration/config.local.yml .. literalinclude:: ../../examples/providers/configuration/config.local.yml
:language: ini :language: ini
Specifying value type Specifying the value type
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
You can specify the type of the injected configuration value explicitly. You can specify the type of the injected configuration value explicitly.
This helps when you read the value from the ini file or the environment variable and need to This helps when you read the value from an ini file or an environment variable and need to
convert it into an ``int`` or a ``float``. convert it into an ``int`` or a ``float``.
.. literalinclude:: ../../examples/providers/configuration/configuration_type.py .. literalinclude:: ../../examples/providers/configuration/configuration_type.py
@ -109,20 +124,20 @@ convert it into an ``int`` or a ``float``.
:lines: 3- :lines: 3-
:emphasize-lines: 17 :emphasize-lines: 17
:py:class:`Configuration` provider has next helper methods: ``Configuration`` provider has next helper methods:
- ``.as_int()`` - ``.as_int()``
- ``.as_float()`` - ``.as_float()``
- ``.as_(callback, *args, **kwargs)`` - ``.as_(callback, *args, **kwargs)``
The last method ``.as_(callback, *args, **kwargs)`` helps to implement a other conversions. The last method ``.as_(callback, *args, **kwargs)`` helps to implement other conversions.
.. 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: 16
With the ``.as_(callback, *args, **kwargs)`` you can specify the 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.

View File

@ -1,72 +1,27 @@
Coroutine providers Coroutine provider
------------------- ------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Coroutine,Asynchronous,
Asyncio,Example
:description: Coroutine provider creates a coroutine. This page demonstrates how to use a
Coroutine provider.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
:py:class:`Coroutine` provider create wrapped coroutine on every call. :py:class:`Coroutine` provider creates a coroutine.
:py:class:`Coroutine` provider is designed for making better integration with .. literalinclude:: ../../examples/providers/coroutine.py
``asyncio`` coroutines. In particular, :py:class:`Coroutine` provider returns
``True`` for ``asyncio.iscoroutinefunction()`` checks.
.. note::
:py:class:`Coroutine` provider works only for Python 3.4+.
Example of usage :py:class:`Coroutine` provider with ``async / await``-based
coroutine:
.. literalinclude:: ../../examples/providers/coroutine_async_await.py
:language: python :language: python
:lines: 3-
Coroutine providers and injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Coroutine` provider takes a various number of positional and keyword
arguments that are used as wrapped coroutine injections. Every time, when
:py:class:`Coroutine` provider is called, positional and keyword argument
injections would be passed as coroutine arguments.
Injections are done according to the next rules:
+ All providers (instances of :py:class:`Provider`) are called every time
when injection needs to be done.
+ Providers could be injected "as is" (delegated), if it is defined obviously.
Check out :ref:`coroutine_providers_delegation`.
+ All other injectable values are provided *"as is"*.
+ Positional context arguments will be appended after :py:class:`Coroutine`
positional injections.
+ Keyword context arguments have priority on :py:class:`Coroutine` keyword
injections and will be merged over them.
.. note:: .. note::
The example works on Python 3.7+. For earlier versions use ``loop.run_until_complete()``.
Examples of making injections could be found in API docs - ``Coroutine`` provider handles an injection of the dependencies the same way like a
:py:class:`Coroutine`. :ref:`factory-provider`.
.. _coroutine_providers_delegation: .. note::
``Coroutine`` provider returns ``True`` for ``asyncio.iscoroutinefunction()`` check.
Coroutine providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Coroutine` provider could be delegated to any other provider via
any kind of injection.
Delegation of :py:class:`Coroutine` providers is the same as
:py:class:`Factory` providers delegation, please follow
:ref:`factory_providers_delegation` section for examples (with exception
of using :py:class:`DelegatedCoroutine` instead of
:py:class:`DelegatedFactory`).
Abstract coroutine providers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`AbstractCoroutine` provider is a :py:class:`Coroutine` provider that
must be explicitly overridden before calling.
Behaviour of :py:class:`AbstractCoroutine` providers is the same as of
:py:class:`AbstractFactory`, please follow :ref:`abstract_factory_providers`
section for examples (with exception of using :py:class:`AbstractCoroutine`
provider instead of :py:class:`AbstractFactory`).
.. disqus:: .. disqus::

View File

@ -1,43 +1,21 @@
Dependency providers Dependency provider
-------------------- -------------------
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
:py:class:`Dependency` provider can be useful for development of :py:class:`Dependency` provider is a placeholder for the dependency of the specified type.
self-sufficient libraries / modules / applications that have required external
dependencies.
For example, you have created self-sufficient library / module / application, The first argument of the ``Dependency`` provider specifies a type of the dependency. It is
that has dependency on *database connection*. called ``instance_of``. ``Dependency`` provider controls the type of the returned object to be an
instance of the ``instance_of`` type.
Second step you want to do is to make this software component to be easy The ``Dependency`` provider must be overridden before usage. It can be overridden by any type of
reusable by wide amount of developers and to be easily integrated into many the provider. The only rule is that overriding provider must return an instance of ``instance_of``
applications. dependency type.
It may be good idea, to move all external dependencies (like
*database connection*) to the top level and make them to be injected on your
software component's initialization. It will make third party developers feel
themselves free about integration of your component in their applications,
because they would be able to find right place / right way for doing this
in their application's architectures.
At the same time, you can be sure, that your external dependency will be
satisfied with appropriate instance.
Example:
.. note::
Class ``UsersService`` is a part of some library. ``UsersService`` has
dependency on database connection, which can be satisfied with any
DBAPI 2.0 database connection. Being a self-sufficient library,
``UsersService`` doesn't hardcode any kind of database management logic.
Instead of this, ``UsersService`` has external dependency, that has to
be satisfied by client's code, out of library's scope.
.. image:: /images/providers/dependency.png
.. literalinclude:: ../../examples/providers/dependency.py .. literalinclude:: ../../examples/providers/dependency.py
:language: python :language: python
:lines: 3-
:emphasize-lines: 26
.. disqus:: .. disqus::

View File

@ -1,5 +1,7 @@
.. _factory-provider:
Factory provider Factory provider
---------------- ================
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Factory,Abstract Factory, :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Factory,Abstract Factory,
@ -100,6 +102,8 @@ attribute of the provider that you're going to inject.
.. note:: Any provider has a ``.provider`` attribute. .. note:: Any provider has a ``.provider`` attribute.
.. _factory-specialize-provided-type:
Specializing the provided type Specializing the provided type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -112,6 +116,8 @@ class attribute.
:lines: 3- :lines: 3-
:emphasize-lines: 12-14 :emphasize-lines: 12-14
.. _abstract-factory:
Abstract factory Abstract factory
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~

View File

@ -1,5 +1,10 @@
List providers List provider
-------------- -------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,List,Injection
:description: List provider helps to inject a list of the dependencies. This page demonstrates
how to use a List provider.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
@ -7,28 +12,12 @@ List providers
.. literalinclude:: ../../examples/providers/list.py .. literalinclude:: ../../examples/providers/list.py
:language: python :language: python
:emphasize-lines: 6-9
:lines: 6-8, 23-29
:py:class:`List` provider is needed for injecting a list of dependencies. It handles
positional argument injections the same way as :py:class:`Factory` provider:
+ All providers (instances of :py:class:`Provider`) are called every time
when injection needs to be done.
+ Providers could be injected "as is" (delegated), if it is defined explicitly. Check out
:ref:`factory_providers_delegation`.
+ All other values are injected *"as is"*.
+ Positional context arguments will be appended after :py:class:`List` positional injections.
Full example:
.. literalinclude:: ../../examples/providers/list.py
:language: python
:emphasize-lines: 23-26
:lines: 3- :lines: 3-
:emphasize-lines: 19-22
``List`` provider handles positional arguments the same way as a :ref:`factory-provider`.
.. note:: .. note::
Keyword argument are not supported.
Keyword argument injections are not supported.
.. disqus:: .. disqus::

View File

@ -1,14 +1,17 @@
Object providers Object provider
---------------- ---------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Object
:description: Object provider provides an object "as is". This page demonstrates how to use an
Object provider.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
:py:class:`Object` provider returns provided instance "as is". :py:class:`Object` provider returns an object "as is".
Example:
.. literalinclude:: ../../examples/providers/object.py .. literalinclude:: ../../examples/providers/object.py
:language: python :language: python
:lines: 3-
.. disqus:: .. disqus::

View File

@ -3,29 +3,31 @@
Selector providers Selector providers
------------------ ------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Configuration,Injection,
Selector,Polymorphism,Environment Variable,Flexibility
:description: Selector selects provider based on a configuration value or another callable.
This page demonstrates how to implement the polymorphism and increase the
flexibility of your application using the Selector provider.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
:py:class:`Selector` provider selects provider based on the configuration value or other callable. :py:class:`Selector` provider selects provider based on a configuration value or another callable.
.. literalinclude:: ../../examples/providers/selector.py .. literalinclude:: ../../examples/providers/selector.py
:language: python :language: python
:emphasize-lines: 6-10
:lines: 3-5,14-20
:py:class:`Selector` provider has a callable called ``selector`` and a dictionary of providers.
The ``selector`` callable is provided as a first positional argument. It can be
:py:class:`Configuration` provider or any other callable. It has to return a string value.
This value is used as a key for selecting the provider from the dictionary of providers.
The providers are provided as keyword arguments. Argument name is used as a key for
selecting the provider.
Full example:
.. literalinclude:: ../../examples/providers/selector.py
:language: python
:emphasize-lines: 14-18
:lines: 3- :lines: 3-
:emphasize-lines: 14-18
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
string value. This value is used as a key for selecting the provider.
The providers are provided as keyword arguments. Argument name is used as a key for selecting the
provider.
When a ``Selector`` provider is called, it gets a ``selector`` value and delegates the work to
the provider with a matching name. The ``selector`` callable works as a switch: when the returned
value is changed the ``Selector`` provider will delegate the work to another provider.
.. disqus:: .. disqus::

View File

@ -1,111 +1,100 @@
Singleton providers Singleton provider
------------------- ------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Singleton,Pattern,Example,
Threads,Multithreading,Scoped
:description: Singleton provider helps to provide a single object. This page
demonstrates how to use a Singleton provider. It also provides the example
of using a singleton and thread locals singleton in the multi-threaded
environment.
.. currentmodule:: dependency_injector.providers .. currentmodule:: dependency_injector.providers
:py:class:`Singleton` provider creates new instance of specified class on :py:class:`Singleton` provider provides single object. It memorizes the first created object and
first call and returns same instance on every next call. returns it on the rest of the calls.
Example:
.. image:: /images/providers/singleton.png
:width: 80%
:align: center
.. literalinclude:: ../../examples/providers/singleton.py .. literalinclude:: ../../examples/providers/singleton.py
:language: python :language: python
:lines: 3-
Singleton providers resetting ``Singleton`` provider handles an injection of the dependencies the same way like a
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :ref:`factory-provider`.
Created and memorized by :py:class:`Singleton` instance can be reset. Reset of
:py:class:`Singleton`'s memorized instance is done by clearing reference to
it. Further lifecycle of memorized instance is out of :py:class:`Singleton`
provider's control and depends on garbage collection strategy.
Example:
.. literalinclude:: ../../examples/providers/singleton_resetting.py
:language: python
Singleton providers and injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Singleton` provider has same interface as :py:class:`Factory`
provider, so, all of the rules about injections are the same, as for
:py:class:`Factory` provider.
.. note:: .. note::
Due that :py:class:`Singleton` provider creates specified class instance ``Singleton`` provider does dependencies injection only when creates the object. When the object
only on the first call, all injections are done once, during the first is created and memorized ``Singleton`` provider just returns it without applying the injections.
call. Every next call, while instance has been already created
and memorized, no injections are done, :py:class:`Singleton` provider just
returns memorized earlier instance.
This may cause some problems, for example, in case of trying to bind Specialization of the provided type and abstract singletons work the same like like for the
:py:class:`Factory` provider with :py:class:`Singleton` provider (provided factories:
by dependent :py:class:`Factory` instance will be injected only once,
during the first call). Be aware that such behaviour was made with opened
eyes and is not a bug.
By the way, in such case, :py:class:`Delegate` or - :ref:`factory-specialize-provided-type`
:py:class:`DelegatedSingleton` provider can be useful - :ref:`abstract-factory`
. It makes possible to inject providers *as is*. Please check out
`Singleton providers delegation`_ section.
Singleton providers delegation Resetting memorized object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Singleton` provider could be delegated to any other provider via To reset a memorized object you need to call the ``.reset()`` method of the ``Singleton``
any kind of injection. provider.
Delegation of :py:class:`Singleton` providers is the same as .. literalinclude:: ../../examples/providers/singleton_resetting.py
:py:class:`Factory` providers delegation, please follow :language: python
:ref:`factory_providers_delegation` section for examples (with exception :lines: 3-
of using :py:class:`DelegatedSingleton` instead of :emphasize-lines: 14
:py:class:`DelegatedFactory`).
Singleton providers specialization .. note::
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Resetting of the memorized object clears the reference to it. Further object's lifecycle is
managed by the garbage collector.
:py:class:`Singleton` provider could be specialized for any kind of needs via Using singleton with multiple threads
declaring its subclasses. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Specialization of :py:class:`Singleton` providers is the same as ``Singleton`` provider is NOT thread-safe. You need to explicitly establish a synchronization for
:py:class:`Factory` providers specialization, please follow using the ``Singleton`` provider in the multi-threading application. Otherwise you could trap
:ref:`factory_providers_specialization` section for examples. into the race condition problem: ``Singleton`` will create multiple objects.
Abstract singleton providers There are two thread-safe singleton implementations out of the box:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`AbstractSingleton` provider is a :py:class:`Singleton` provider that + :py:class:`ThreadSafeSingleton` - is a thread-safe version of a ``Singleton`` provider. You can use
must be explicitly overridden before calling. in multi-threading applications without additional synchronization.
+ :py:class:`ThreadLocalSingleton` - is a singleton provider that uses thread-locals as a storage.
Behaviour of :py:class:`AbstractSingleton` providers is the same as of This type of singleton will manage multiple objects - the one object for the one thread.
:py:class:`AbstractFactory`, please follow :ref:`abstract_factory_providers`
section for examples (with exception of using :py:class:`AbstractSingleton`
provider instead of :py:class:`AbstractFactory`).
Singleton providers and multi-threading
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Singleton` provider is NOT thread-safe and should be used in
multi-threading applications with manually controlled locking.
:py:class:`ThreadSafeSingleton` is a thread-safe version of
:py:class:`Singleton` and could be used in multi-threading applications
without any additional locking.
Also there could be a need to use thread-scoped singletons and there is a
special provider for such case - :py:class:`ThreadLocalSingleton`.
:py:class:`ThreadLocalSingleton` provider creates instance once for each
thread and returns it on every call.
Example:
.. literalinclude:: ../../examples/providers/singleton_thread_locals.py .. literalinclude:: ../../examples/providers/singleton_thread_locals.py
:language: python :language: python
:lines: 3-
:emphasize-lines: 11,12
Implementing scopes
~~~~~~~~~~~~~~~~~~~
To implement a scoped singleton provider use a ``Singleton`` provider and reset its scope when
needed.
.. literalinclude:: ../../examples/providers/singleton_scoped.py
:language: python
:lines: 3-
The output should look like this (each request a ``Service`` object has a different address):
.. code-block::
* Serving Flask app "singleton_scoped" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
<__main__.Service object at 0x1099a9d90>
127.0.0.1 - - [25/Aug/2020 17:33:11] "GET / HTTP/1.1" 200 -
<__main__.Service object at 0x1099a9cd0>
127.0.0.1 - - [25/Aug/2020 17:33:17] "GET / HTTP/1.1" 200 -
<__main__.Service object at 0x1099a9d00>
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
<__main__.Service object at 0x1099a9e50>
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
<__main__.Service object at 0x1099a9d90>
127.0.0.1 - - [25/Aug/2020 17:33:18] "GET / HTTP/1.1" 200 -
.. disqus:: .. disqus::

View File

@ -0,0 +1,18 @@
"""`Callable` provider example."""
import passlib.hash
from dependency_injector import providers
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)

View File

@ -1,16 +0,0 @@
"""`Callable` providers with positional arguments example."""
import dependency_injector.providers as providers
# Creating even and odd filter providers:
even_filter = providers.Callable(filter, lambda x: x % 2 == 0)
odd_filter = providers.Callable(filter, lambda x: x % 2 != 0)
# Creating even and odd ranges using range() and filter providers:
even_range = even_filter(range(1, 10))
odd_range = odd_filter(range(1, 10))
# Making some asserts:
assert even_range == [2, 4, 6, 8]
assert odd_range == [1, 3, 5, 7, 9]

View File

@ -1,16 +0,0 @@
"""`Callable` providers with keyword arguments example."""
import passlib.hash
import dependency_injector.providers as providers
# Password hasher and verifier providers:
password_hasher = providers.Callable(passlib.hash.sha256_crypt.encrypt,
salt_size=16,
rounds=10000)
password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify)
# Making some asserts:
hashed_password = password_hasher('super secret')
assert password_verifier('super secret', hashed_password)

View File

@ -1,26 +1,19 @@
"""`Coroutine` providers example with @asyncio.coroutine decorator. """`Coroutine` providers example with async / await syntax."""
Current example works only fot Python 3.4+.
"""
import asyncio import asyncio
import dependency_injector.providers as providers from dependency_injector import providers
@asyncio.coroutine async def coroutine(arg1, arg2):
def coroutine_function(arg1, arg2): await asyncio.sleep(0.1)
"""Sample coroutine function."""
yield from asyncio.sleep(0.1)
return arg1, arg2 return arg1, arg2
coroutine_provider = providers.Coroutine(coroutine_function, arg1=1, arg2=2) coroutine_provider = providers.Coroutine(coroutine, arg1=1, arg2=2)
if __name__ == '__main__': if __name__ == '__main__':
loop = asyncio.get_event_loop() arg1, arg2 = asyncio.run(coroutine_provider())
arg1, arg2 = loop.run_until_complete(coroutine_provider())
assert (arg1, arg2) == (1, 2) assert (arg1, arg2) == (1, 2)
assert asyncio.iscoroutinefunction(coroutine_provider) assert asyncio.iscoroutinefunction(coroutine_provider)

View File

@ -1,25 +0,0 @@
"""`Coroutine` providers example with async / await syntax.
Current example works only fot Python 3.5+.
"""
import asyncio
import dependency_injector.providers as providers
async def coroutine_function(arg1, arg2):
"""Sample coroutine function."""
await asyncio.sleep(0.1)
return arg1, arg2
coroutine_provider = providers.Coroutine(coroutine_function, arg1=1, arg2=2)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
arg1, arg2 = loop.run_until_complete(coroutine_provider())
assert (arg1, arg2) == (1, 2)
assert asyncio.iscoroutinefunction(coroutine_provider)

View File

@ -1,73 +1,50 @@
"""`Dependency` providers example.""" """`Dependency` provider example."""
import sqlite3 import abc
import contextlib import dataclasses
import dependency_injector.providers as providers from dependency_injector import containers, providers, errors
class UsersService: class DbAdapter(metaclass=abc.ABCMeta):
"""Example class UsersService. ...
UsersService has dependency on DBAPI 2.0 database connection.
"""
def __init__(self, database): class SqliteDbAdapter(DbAdapter):
"""Initialize instance. ...
:param database: Database connection.
:type database: sqlite3.dbapi2.Connection
"""
self.database = database
self.database.row_factory = sqlite3.dbapi2.Row
def init_database(self): class PostgresDbAdapter(DbAdapter):
"""Initialize database, if it has not been initialized yet.""" ...
with contextlib.closing(self.database.cursor()) as cursor:
cursor.execute("""
CREATE TABLE IF NOT EXISTS users( @dataclasses.dataclass
id INTEGER PRIMARY KEY AUTOINCREMENT, class UserService:
name VARCHAR(32) database: DbAdapter
class Container(containers.DeclarativeContainer):
database = providers.Dependency(instance_of=DbAdapter)
user_service = providers.Factory(
UserService,
database=database,
) )
""")
def create(self, name):
"""Create user with provided name and return his id."""
with contextlib.closing(self.database.cursor()) as cursor:
cursor.execute('INSERT INTO users(name) VALUES (?)', (name,))
return cursor.lastrowid
def get_by_id(self, id):
"""Return user info by user id."""
with contextlib.closing(self.database.cursor()) as cursor:
cursor.execute('SELECT id, name FROM users WHERE id=?', (id,))
return cursor.fetchone()
# Database and UsersService providers: if __name__ == '__main__':
database = providers.Dependency(instance_of=sqlite3.dbapi2.Connection) container1 = Container(database=providers.Singleton(SqliteDbAdapter))
users_service_factory = providers.Factory(UsersService, container2 = Container(database=providers.Singleton(PostgresDbAdapter))
database=database)
# Out of library's scope. assert isinstance(container1.user_service().database, SqliteDbAdapter)
# assert isinstance(container2.user_service().database, PostgresDbAdapter)
# Setting database provider:
database.provided_by(providers.Singleton(sqlite3.dbapi2.Connection,
database=':memory:',
timeout=30,
detect_types=True,
isolation_level='EXCLUSIVE'))
# Creating UsersService instance: container3 = Container(database=providers.Singleton(object))
users_service = users_service_factory() try:
container3.user_service()
# Initializing UsersService database: except errors.Error as exception:
users_service.init_database() print(exception)
# The output is:
# Creating test user and retrieving full information about him: # <object object at 0x107ce5c40> is not an
test_user_id = users_service.create(name='test_user') # instance of <class '__main__.DbAdapter'>
test_user = users_service.get_by_id(test_user_id)
# Making some asserts:
assert test_user['id'] == 1
assert test_user['name'] == 'test_user'

View File

@ -1,4 +1,4 @@
"""`Factory` providers example.""" """`Factory` provider example."""
from dependency_injector import providers from dependency_injector import providers

View File

@ -1,4 +1,4 @@
"""`Factory` providers delegation example.""" """`Factory` provider delegation example."""
from typing import Callable, List from typing import Callable, List

View File

@ -1,4 +1,4 @@
"""`Factory` providers init injections example.""" """`Factory` provider init injections example."""
from dependency_injector import providers from dependency_injector import providers

View File

@ -1,4 +1,4 @@
"""`Factory` providers - building a complex object graph with deep init injections example.""" """`Factory` provider - passing injections to the underlying providers example."""
from dependency_injector import providers from dependency_injector import providers

View File

@ -8,15 +8,11 @@ from dependency_injector import providers
@dataclasses.dataclass @dataclasses.dataclass
class Module: class Module:
"""Example module."""
name: str name: str
@dataclasses.dataclass @dataclasses.dataclass
class Dispatcher: class Dispatcher:
"""Example dispatcher."""
modules: List[Module] modules: List[Module]
@ -28,6 +24,7 @@ dispatcher_factory = providers.Factory(
), ),
) )
if __name__ == '__main__': if __name__ == '__main__':
dispatcher = dispatcher_factory() dispatcher = dispatcher_factory()
@ -35,11 +32,10 @@ if __name__ == '__main__':
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 of dispatcher_factory() is equivalent to: # Call "dispatcher = dispatcher_factory()" is an equivalent for:
# dispatcher = Dispatcher(
dispatcher = Dispatcher( # modules=[
modules=[ # Module(name='m1'),
Module(name='m1'), # Module(name='m2'),
Module(name='m2'), # ],
], # )
)

View File

@ -1,10 +1,10 @@
"""Object providers example.""" """`Object` provider example."""
import dependency_injector.providers as providers from dependency_injector import providers
# Creating object provider:
object_provider = providers.Object(1) object_provider = providers.Object(1)
# Making some asserts:
assert object_provider() == 1 if __name__ == '__main__':
assert object_provider() == 1

View File

@ -19,10 +19,11 @@ selector = providers.Selector(
another=providers.Factory(SomeOtherClass), another=providers.Factory(SomeOtherClass),
) )
config.override({'one_or_another': 'one'}) if __name__ == '__main__':
instance_1 = selector() config.override({'one_or_another': 'one'})
assert isinstance(instance_1, SomeClass) instance_1 = selector()
assert isinstance(instance_1, SomeClass)
config.override({'one_or_another': 'another'}) config.override({'one_or_another': 'another'})
instance_2 = selector() instance_2 = selector()
assert isinstance(instance_2, SomeOtherClass) assert isinstance(instance_2, SomeOtherClass)

View File

@ -1,19 +1,16 @@
"""`Singleton` providers example.""" """`Singleton` provider example."""
import collections from dependency_injector import providers
import dependency_injector.providers as providers
UsersService = collections.namedtuple('UsersService', []) class UserService:
...
# Singleton provider creates new instance of specified class on first call
# and returns same instance on every next call.
users_service_provider = providers.Singleton(UsersService)
# Retrieving several UserService objects: user_service_provider = providers.Singleton(UserService)
users_service1 = users_service_provider()
users_service2 = users_service_provider()
# Making some asserts:
assert users_service1 is users_service2 if __name__ == '__main__':
user_service1 = user_service_provider()
user_service2 = user_service_provider()
assert user_service1 is user_service2

View File

@ -1,35 +0,0 @@
"""`Singleton` specialization for limitation to provided type example."""
import dependency_injector.providers as providers
import dependency_injector.errors as errors
class BaseService:
"""Base service class."""
class UsersService(BaseService):
"""Users service."""
class PhotosService(BaseService):
"""Photos service."""
class ServiceProvider(providers.Singleton):
"""Service provider."""
provided_type = BaseService
# Creating several service providers with BaseService instances:
users_service_provider = ServiceProvider(UsersService)
photos_service_provider = ServiceProvider(PhotosService)
# Trying to create service provider with not a BaseService instance:
try:
some_service_provider = ServiceProvider(object)
except errors.Error as exception:
print(exception)
# <class '__main__.ServiceProvider'> can provide only
# <class '__main__.BaseService'> instances

View File

@ -1,27 +1,19 @@
"""`Singleton` providers resetting example.""" """`Singleton` provider resetting example."""
import collections from dependency_injector import providers
import dependency_injector.providers as providers
UsersService = collections.namedtuple('UsersService', []) class UserService:
...
# Users service singleton provider:
users_service_provider = providers.Singleton(UsersService)
# Retrieving several UsersService objects: user_service_provider = providers.Singleton(UserService)
users_service1 = users_service_provider()
users_service2 = users_service_provider()
# Making some asserts:
assert users_service1 is users_service2
# Resetting of memorized instance: if __name__ == '__main__':
users_service_provider.reset() user_service1 = user_service_provider()
# Retrieving one more UserService object: user_service_provider.reset()
users_service3 = users_service_provider()
# Making some asserts: users_service2 = user_service_provider()
assert users_service3 is not users_service1 assert users_service2 is not user_service1

View File

@ -0,0 +1,32 @@
"""`Singleton` - flask request scope example."""
from dependency_injector import providers
from flask import Flask
class Service:
...
service_provider = providers.Singleton(Service)
def index_view():
service_1 = service_provider()
service_2 = service_provider()
assert service_1 is service_2
print(service_1)
return 'Hello World!'
def teardown_context(request):
service_provider.reset()
return request
app = Flask(__name__)
app.add_url_rule('/', 'index', view_func=index_view)
app.after_request(teardown_context)
if __name__ == '__main__':
app.run()

View File

@ -1,53 +1,42 @@
"""`ThreadLocalSingleton` providers example.""" """`ThreadLocalSingleton` provider example."""
import threading import threading
import queue import queue
import dependency_injector.providers as providers from dependency_injector import providers
def example(example_object, queue_object): def put_in_queue(example_object, queue_object):
"""Put provided object in the provided queue."""
queue_object.put(example_object) queue_object.put(example_object)
# Create thread-local singleton provider for some object (main thread):
thread_local_object = providers.ThreadLocalSingleton(object) thread_local_object = providers.ThreadLocalSingleton(object)
queue_provider = providers.ThreadSafeSingleton(queue.Queue)
# Create singleton provider for thread-safe queue: put_in_queue = providers.Callable(
queue_factory = providers.ThreadSafeSingleton(queue.Queue) put_in_queue,
# Create callable provider for example(), inject dependencies:
example = providers.DelegatedCallable(
example,
example_object=thread_local_object, example_object=thread_local_object,
queue_object=queue_factory, queue_object=queue_provider,
)
thread_factory = providers.Factory(
threading.Thread,
target=put_in_queue.provider,
) )
# Create factory for threads that are targeted to execute example():
thread_factory = providers.Factory(threading.Thread, target=example)
if __name__ == '__main__': if __name__ == '__main__':
# Create 10 threads for concurrent execution of example():
threads = [] threads = []
for thread_number in range(10): for thread_number in range(10):
threads.append( threads.append(
thread_factory(name='Thread{0}'.format(thread_number)), thread_factory(name='Thread{0}'.format(thread_number)),
) )
# Start execution of all created threads:
for thread in threads: for thread in threads:
thread.start() thread.start()
# Wait while threads would complete their work:
for thread in threads: for thread in threads:
thread.join() thread.join()
# Making some asserts (main thread):
all_objects = set() all_objects = set()
while not queue_provider().empty():
while not queue_factory().empty(): all_objects.add(queue_provider().get())
all_objects.add(queue_factory().get())
assert len(all_objects) == len(threads) assert len(all_objects) == len(threads)
# Queue contains same number of objects as number of threads where # Queue contains same number of objects as number of threads where

View File

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

View File

@ -24,6 +24,7 @@ class DynamicContainer(Container): ...
class DeclarativeContainer(Container): class DeclarativeContainer(Container):
cls_providers: ClassVar[Dict[str, Provider]] cls_providers: ClassVar[Dict[str, Provider]]
inherited_providers: ClassVar[Dict[str, Provider]] inherited_providers: ClassVar[Dict[str, Provider]]
def __init__(self, **overriding_providers: Provider) -> None: ...
def override(container: Container) -> _Callable[[Container], Container]: ... def override(container: Container) -> _Callable[[Container], Container]: ...