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
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
------
- 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
: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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
: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
.. literalinclude:: ../../examples/providers/callable.py
:language: python
:lines: 3-
Next one example shows usage of :py:class:`Callable` with keyword argument
injections:
.. 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`).
``Callable`` provider handles an injection of the dependencies the same way like a
:ref:`factory-provider`.
.. 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
@ -10,13 +17,13 @@ Configuration providers
:emphasize-lines: 4,9-10
: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
:py:meth:`Configuration.from_ini`:
``Configuration`` provider can load configuration from an ``ini`` file using the
:py:meth:`Configuration.from_ini` method:
.. literalinclude:: ../../examples/providers/configuration/configuration_ini.py
:language: python
@ -28,15 +35,15 @@ where ``examples/providers/configuration/config.ini`` is:
.. literalinclude:: ../../examples/providers/configuration/config.ini
:language: ini
:py:meth:`Configuration.from_ini` supports environment variables interpolation. Use
``${ENV_NAME}`` format in the configuration file to substitute value of environment
:py:meth:`Configuration.from_ini` method supports environment variables interpolation. Use
``${ENV_NAME}`` format in the configuration file to substitute value of the environment
variable ``ENV_NAME``.
Loading from ``yaml`` file
~~~~~~~~~~~~~~~~~~~~~~~~~~
Loading from a YAML file
~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Configuration` provider can load configuration from ``yaml`` file using
:py:meth:`Configuration.from_yaml`:
``Configuration`` provider can load configuration from a ``yaml`` file using the
:py:meth:`Configuration.from_yaml` method:
.. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py
:language: python
@ -48,43 +55,51 @@ where ``examples/providers/configuration/config.yml`` is:
.. literalinclude:: ../../examples/providers/configuration/config.yml
:language: ini
:py:meth:`Configuration.from_yaml` supports environment variables interpolation. Use
``${ENV_NAME}`` format in the configuration file to substitute value of environment
:py:meth:`Configuration.from_yaml` method supports environment variables interpolation. Use
``${ENV_NAME}`` format in the configuration file to substitute value of the environment
variable ``ENV_NAME``.
.. note::
Loading configuration from yaml requires ``PyYAML`` package. You can install
`Dependency Injector` with extras ``pip install dependency-injector[yaml]`` or install
``PyYAML`` separately ``pip install pyyaml``.
Loading of a yaml configuration requires ``PyYAML`` package.
Loading from ``dict``
~~~~~~~~~~~~~~~~~~~~~
You can install the ``Dependency Injector`` with an extra dependency::
:py:class:`Configuration` provider can load configuration from Python ``dict`` using
:py:meth:`Configuration.from_dict`:
pip install dependency-injector[yaml]
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
:language: python
:lines: 3-5,6-
:emphasize-lines: 6-13
Loading from environment variable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Loading from an environment variable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Configuration` provider can load configuration from environment variable using
:py:meth:`Configuration.from_env`:
``Configuration`` provider can load configuration from an environment variable using the
:py:meth:`Configuration.from_env` method:
.. literalinclude:: ../../examples/providers/configuration/configuration_env.py
:language: python
:lines: 5-7,13-21
: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 is merged recursively over existing configuration.
``Configuration`` provider can load configuration from the multiple sources. Loaded
configuration is merged recursively over the existing configuration.
.. literalinclude:: ../../examples/providers/configuration/configuration_multiple.py
:language: python
@ -96,12 +111,12 @@ where ``examples/providers/configuration/config.local.yml`` is:
.. literalinclude:: ../../examples/providers/configuration/config.local.yml
:language: ini
Specifying value type
~~~~~~~~~~~~~~~~~~~~~
Specifying the value type
~~~~~~~~~~~~~~~~~~~~~~~~~
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``.
.. literalinclude:: ../../examples/providers/configuration/configuration_type.py
@ -109,20 +124,20 @@ convert it into an ``int`` or a ``float``.
:lines: 3-
:emphasize-lines: 17
:py:class:`Configuration` provider has next helper methods:
``Configuration`` provider has next helper methods:
- ``.as_int()``
- ``.as_float()``
- ``.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
:language: python
:lines: 3-
: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
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
: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
``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
.. literalinclude:: ../../examples/providers/coroutine.py
:language: python
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.
:lines: 3-
.. 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 -
:py:class:`Coroutine`.
``Coroutine`` provider handles an injection of the dependencies the same way like a
:ref:`factory-provider`.
.. _coroutine_providers_delegation:
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`).
.. note::
``Coroutine`` provider returns ``True`` for ``asyncio.iscoroutinefunction()`` check.
.. disqus::

View File

@ -1,43 +1,21 @@
Dependency providers
--------------------
Dependency provider
-------------------
.. currentmodule:: dependency_injector.providers
:py:class:`Dependency` provider can be useful for development of
self-sufficient libraries / modules / applications that have required external
dependencies.
:py:class:`Dependency` provider is a placeholder for the dependency of the specified type.
For example, you have created self-sufficient library / module / application,
that has dependency on *database connection*.
The first argument of the ``Dependency`` provider specifies a type of the dependency. It is
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
reusable by wide amount of developers and to be easily integrated into many
applications.
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
The ``Dependency`` provider must be overridden before usage. It can be overridden by any type of
the provider. The only rule is that overriding provider must return an instance of ``instance_of``
dependency type.
.. literalinclude:: ../../examples/providers/dependency.py
:language: python
:lines: 3-
:emphasize-lines: 26
.. disqus::

View File

@ -1,5 +1,7 @@
.. _factory-provider:
Factory provider
----------------
================
.. meta::
: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.
.. _factory-specialize-provided-type:
Specializing the provided type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -112,6 +116,8 @@ class attribute.
:lines: 3-
:emphasize-lines: 12-14
.. _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
@ -7,28 +12,12 @@ List providers
.. literalinclude:: ../../examples/providers/list.py
: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-
:emphasize-lines: 19-22
``List`` provider handles positional arguments the same way as a :ref:`factory-provider`.
.. note::
Keyword argument injections are not supported.
Keyword argument are not supported.
.. 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
:py:class:`Object` provider returns provided instance "as is".
Example:
:py:class:`Object` provider returns an object "as is".
.. literalinclude:: ../../examples/providers/object.py
:language: python
:lines: 3-
.. disqus::

View File

@ -3,29 +3,31 @@
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
: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
: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-
: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::

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
:py:class:`Singleton` provider creates new instance of specified class on
first call and returns same instance on every next call.
Example:
.. image:: /images/providers/singleton.png
:width: 80%
:align: center
:py:class:`Singleton` provider provides single object. It memorizes the first created object and
returns it on the rest of the calls.
.. literalinclude:: ../../examples/providers/singleton.py
:language: python
:lines: 3-
Singleton providers resetting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
``Singleton`` provider handles an injection of the dependencies the same way like a
:ref:`factory-provider`.
.. note::
Due that :py:class:`Singleton` provider creates specified class instance
only on the first call, all injections are done once, during the first
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.
``Singleton`` provider does dependencies injection only when creates the object. When the object
is created and memorized ``Singleton`` provider just returns it without applying the injections.
This may cause some problems, for example, in case of trying to bind
:py:class:`Factory` provider with :py:class:`Singleton` provider (provided
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.
Specialization of the provided type and abstract singletons work the same like like for the
factories:
By the way, in such case, :py:class:`Delegate` or
:py:class:`DelegatedSingleton` provider can be useful
. It makes possible to inject providers *as is*. Please check out
`Singleton providers delegation`_ section.
- :ref:`factory-specialize-provided-type`
- :ref:`abstract-factory`
Singleton providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Resetting memorized object
~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Singleton` provider could be delegated to any other provider via
any kind of injection.
To reset a memorized object you need to call the ``.reset()`` method of the ``Singleton``
provider.
Delegation of :py:class:`Singleton` 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:`DelegatedSingleton` instead of
:py:class:`DelegatedFactory`).
.. literalinclude:: ../../examples/providers/singleton_resetting.py
:language: python
:lines: 3-
:emphasize-lines: 14
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
declaring its subclasses.
Using singleton with multiple threads
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Specialization of :py:class:`Singleton` providers is the same as
:py:class:`Factory` providers specialization, please follow
:ref:`factory_providers_specialization` section for examples.
``Singleton`` provider is NOT thread-safe. You need to explicitly establish a synchronization for
using the ``Singleton`` provider in the multi-threading application. Otherwise you could trap
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
must be explicitly overridden before calling.
Behaviour of :py:class:`AbstractSingleton` providers is the same as of
: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:
+ :py:class:`ThreadSafeSingleton` - is a thread-safe version of a ``Singleton`` provider. You can use
in multi-threading applications without additional synchronization.
+ :py:class:`ThreadLocalSingleton` - is a singleton provider that uses thread-locals as a storage.
This type of singleton will manage multiple objects - the one object for the one thread.
.. literalinclude:: ../../examples/providers/singleton_thread_locals.py
: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::

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.
Current example works only fot Python 3.4+.
"""
"""`Coroutine` providers example with async / await syntax."""
import asyncio
import dependency_injector.providers as providers
from dependency_injector import providers
@asyncio.coroutine
def coroutine_function(arg1, arg2):
"""Sample coroutine function."""
yield from asyncio.sleep(0.1)
async def coroutine(arg1, arg2):
await asyncio.sleep(0.1)
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__':
loop = asyncio.get_event_loop()
arg1, arg2 = loop.run_until_complete(coroutine_provider())
arg1, arg2 = asyncio.run(coroutine_provider())
assert (arg1, arg2) == (1, 2)
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 contextlib
import abc
import dataclasses
import dependency_injector.providers as providers
from dependency_injector import containers, providers, errors
class UsersService:
"""Example class UsersService.
UsersService has dependency on DBAPI 2.0 database connection.
"""
def __init__(self, database):
"""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):
"""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(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(32)
)
""")
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()
class DbAdapter(metaclass=abc.ABCMeta):
...
# Database and UsersService providers:
database = providers.Dependency(instance_of=sqlite3.dbapi2.Connection)
users_service_factory = providers.Factory(UsersService,
database=database)
class SqliteDbAdapter(DbAdapter):
...
# Out of library's scope.
#
# Setting database provider:
database.provided_by(providers.Singleton(sqlite3.dbapi2.Connection,
database=':memory:',
timeout=30,
detect_types=True,
isolation_level='EXCLUSIVE'))
# Creating UsersService instance:
users_service = users_service_factory()
class PostgresDbAdapter(DbAdapter):
...
# Initializing UsersService database:
users_service.init_database()
# Creating test user and retrieving full information about him:
test_user_id = users_service.create(name='test_user')
test_user = users_service.get_by_id(test_user_id)
@dataclasses.dataclass
class UserService:
database: DbAdapter
# Making some asserts:
assert test_user['id'] == 1
assert test_user['name'] == 'test_user'
class Container(containers.DeclarativeContainer):
database = providers.Dependency(instance_of=DbAdapter)
user_service = providers.Factory(
UserService,
database=database,
)
if __name__ == '__main__':
container1 = Container(database=providers.Singleton(SqliteDbAdapter))
container2 = Container(database=providers.Singleton(PostgresDbAdapter))
assert isinstance(container1.user_service().database, SqliteDbAdapter)
assert isinstance(container2.user_service().database, PostgresDbAdapter)
container3 = Container(database=providers.Singleton(object))
try:
container3.user_service()
except errors.Error as exception:
print(exception)
# The output is:
# <object object at 0x107ce5c40> is not an
# instance of <class '__main__.DbAdapter'>

View File

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

View File

@ -1,4 +1,4 @@
"""`Factory` providers delegation example."""
"""`Factory` provider delegation example."""
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

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

View File

@ -8,15 +8,11 @@ from dependency_injector import providers
@dataclasses.dataclass
class Module:
"""Example module."""
name: str
@dataclasses.dataclass
class Dispatcher:
"""Example dispatcher."""
modules: List[Module]
@ -28,6 +24,7 @@ dispatcher_factory = providers.Factory(
),
)
if __name__ == '__main__':
dispatcher = dispatcher_factory()
@ -35,11 +32,10 @@ if __name__ == '__main__':
assert dispatcher.modules[0].name == 'm1'
assert dispatcher.modules[1].name == 'm2'
# Call of dispatcher_factory() is equivalent to:
dispatcher = Dispatcher(
modules=[
Module(name='m1'),
Module(name='m2'),
],
)
# Call "dispatcher = dispatcher_factory()" is an equivalent for:
# dispatcher = Dispatcher(
# modules=[
# Module(name='m1'),
# 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)
# 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),
)
config.override({'one_or_another': 'one'})
instance_1 = selector()
assert isinstance(instance_1, SomeClass)
if __name__ == '__main__':
config.override({'one_or_another': 'one'})
instance_1 = selector()
assert isinstance(instance_1, SomeClass)
config.override({'one_or_another': 'another'})
instance_2 = selector()
assert isinstance(instance_2, SomeOtherClass)
config.override({'one_or_another': 'another'})
instance_2 = selector()
assert isinstance(instance_2, SomeOtherClass)

View File

@ -1,19 +1,16 @@
"""`Singleton` providers example."""
"""`Singleton` provider example."""
import collections
import dependency_injector.providers as providers
from dependency_injector import 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:
users_service1 = users_service_provider()
users_service2 = users_service_provider()
user_service_provider = providers.Singleton(UserService)
# 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
import dependency_injector.providers as providers
from dependency_injector import providers
UsersService = collections.namedtuple('UsersService', [])
class UserService:
...
# Users service singleton provider:
users_service_provider = providers.Singleton(UsersService)
# Retrieving several UsersService objects:
users_service1 = users_service_provider()
users_service2 = users_service_provider()
user_service_provider = providers.Singleton(UserService)
# Making some asserts:
assert users_service1 is users_service2
# Resetting of memorized instance:
users_service_provider.reset()
if __name__ == '__main__':
user_service1 = user_service_provider()
# Retrieving one more UserService object:
users_service3 = users_service_provider()
user_service_provider.reset()
# Making some asserts:
assert users_service3 is not users_service1
users_service2 = user_service_provider()
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 queue
import dependency_injector.providers as providers
from dependency_injector import providers
def example(example_object, queue_object):
"""Put provided object in the provided queue."""
def put_in_queue(example_object, queue_object):
queue_object.put(example_object)
# Create thread-local singleton provider for some object (main thread):
thread_local_object = providers.ThreadLocalSingleton(object)
# Create singleton provider for thread-safe queue:
queue_factory = providers.ThreadSafeSingleton(queue.Queue)
# Create callable provider for example(), inject dependencies:
example = providers.DelegatedCallable(
example,
queue_provider = providers.ThreadSafeSingleton(queue.Queue)
put_in_queue = providers.Callable(
put_in_queue,
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__':
# Create 10 threads for concurrent execution of example():
threads = []
for thread_number in range(10):
threads.append(
thread_factory(name='Thread{0}'.format(thread_number)),
)
# Start execution of all created threads:
for thread in threads:
thread.start()
# Wait while threads would complete their work:
for thread in threads:
thread.join()
# Making some asserts (main thread):
all_objects = set()
while not queue_factory().empty():
all_objects.add(queue_factory().get())
while not queue_provider().empty():
all_objects.add(queue_provider().get())
assert len(all_objects) == len(threads)
# Queue contains same number of objects as number of threads where

View File

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

View File

@ -24,6 +24,7 @@ class DynamicContainer(Container): ...
class DeclarativeContainer(Container):
cls_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]: ...