mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-25 19:14:00 +03:00
Merge branch 'release/3.30.3' into master
This commit is contained in:
commit
597f6794a9
94
README.rst
94
README.rst
|
@ -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 implement the dependency injection principle.
|
It helps you in implementing the dependency injection principle.
|
||||||
|
|
||||||
What is dependency injection?
|
What is dependency injection?
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
@ -70,6 +70,9 @@ Before:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
class ApiClient:
|
class ApiClient:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -82,10 +85,18 @@ Before:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.api_client = ApiClient()
|
self.api_client = ApiClient()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
service = Service()
|
||||||
|
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
class ApiClient:
|
class ApiClient:
|
||||||
|
|
||||||
def __init__(self, api_key: str, timeout: int):
|
def __init__(self, api_key: str, timeout: int):
|
||||||
|
@ -98,55 +109,82 @@ After:
|
||||||
def __init__(self, api_client: ApiClient):
|
def __init__(self, api_client: ApiClient):
|
||||||
self.api_client = api_client
|
self.api_client = api_client
|
||||||
|
|
||||||
Who creates the objects now? Look at the next section.
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
|
||||||
|
|
||||||
|
|
||||||
|
Flexibility comes with a price: now you need to assemble your objects like this
|
||||||
|
``Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))``. The assembly code might get
|
||||||
|
duplicated and it'll become harder to change the application structure.
|
||||||
|
|
||||||
What does Dependency Injector do?
|
What does Dependency Injector do?
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
``Dependency Injector`` provides you with the container and the providers that help you build
|
``Dependency Injector`` helps you assemble the objects.
|
||||||
your application objects when you apply dependency injection principle:
|
|
||||||
|
It provides you the container and the providers that help you describe objects assembly. When you
|
||||||
|
need an object you get it from the container. The rest of the assembly work is done by the
|
||||||
|
framework:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
class ApiClient:
|
class ApiClient:
|
||||||
|
|
||||||
def __init__(self, api_key: str, timeout: int):
|
def __init__(self, api_key: str, timeout: int):
|
||||||
self.api_key = api_key
|
self.api_key = api_key
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
|
|
||||||
class Service:
|
class Service:
|
||||||
|
|
||||||
def __init__(self, api_client: ApiClient):
|
def __init__(self, api_client: ApiClient):
|
||||||
self.api_client = api_client
|
self.api_client = api_client
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
config = providers.Configuration()
|
config = providers.Configuration()
|
||||||
|
|
||||||
api_client = providers.Singleton(
|
api_client = providers.Singleton(
|
||||||
ApiClient,
|
ApiClient,
|
||||||
api_key=config.api_key,
|
api_key=config.api_key,
|
||||||
timeout=config.timeout,
|
timeout=config.timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
service = providers.Factory(
|
service = providers.Factory(
|
||||||
Service,
|
Service,
|
||||||
api_client=api_client,
|
api_client=api_client,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
container = Container()
|
container = Container()
|
||||||
container.config.from_yaml('config.yml')
|
container.config.api_key.from_env('API_KEY')
|
||||||
|
container.config.timeout.from_env('TIMEOUT')
|
||||||
|
|
||||||
service = container.service()
|
service = container.service()
|
||||||
|
assert isinstance(service.api_client, ApiClient)
|
||||||
|
|
||||||
assert isinstance(service.api_client, ApiClient)
|
Retrieving of the ``Service`` instance now is done like this ``container.service()``.
|
||||||
|
|
||||||
|
Also ``Dependency Injector`` provides a bonus in overriding any of the providers with the
|
||||||
|
``.override()`` method:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
|
||||||
|
with container.api_client.override(mock.Mock()):
|
||||||
|
service = container.service()
|
||||||
|
assert isinstance(service.api_client, mock.Mock)
|
||||||
|
|
||||||
|
It helps in a testing. Also you can use it for configuring project for the different environments:
|
||||||
|
replace an API client with a stub on the dev or stage.
|
||||||
|
|
||||||
`More examples <https://github.com/ets-labs/python-dependency-injector/tree/master/examples>`_
|
`More examples <https://github.com/ets-labs/python-dependency-injector/tree/master/examples>`_
|
||||||
|
|
||||||
|
|
|
@ -1,55 +1,43 @@
|
||||||
Declarative containers
|
Declarative container
|
||||||
----------------------
|
---------------------
|
||||||
|
|
||||||
.. currentmodule:: dependency_injector.containers
|
.. currentmodule:: dependency_injector.containers
|
||||||
|
|
||||||
:py:class:`DeclarativeContainer` is inversion of control container that
|
:py:class:`DeclarativeContainer` is a class-based style of the providers definition.
|
||||||
could be defined in declarative manner. It should cover most of the cases
|
|
||||||
when list of providers that would be included in container is deterministic
|
|
||||||
(container will not change its structure in runtime).
|
|
||||||
|
|
||||||
Declarative containers have to extend base declarative container class -
|
You create the declarative container subclass, put the providers as attributes and create the
|
||||||
:py:class:`dependency_injector.containers.DeclarativeContainer`.
|
container instance.
|
||||||
|
|
||||||
Declarative container's providers have to be defined like container's class
|
|
||||||
attributes. Every provider in container has name. This name should follow
|
|
||||||
``some_provider`` convention, that is standard naming convention for
|
|
||||||
attribute names in Python.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Declarative containers have several features that could be useful
|
|
||||||
for some kind of operations on container's providers, please visit API
|
|
||||||
documentation for getting full list of features -
|
|
||||||
:py:class:`dependency_injector.containers.DeclarativeContainer`.
|
|
||||||
|
|
||||||
Here is an simple example of defining declarative container with several
|
|
||||||
factories:
|
|
||||||
|
|
||||||
.. image:: /images/containers/declarative.png
|
|
||||||
:width: 85%
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/declarative.py
|
.. literalinclude:: ../../examples/containers/declarative.py
|
||||||
:language: python
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
Example of declarative containers inheritance:
|
The declarative container providers should only be used when you have the container instance.
|
||||||
|
Working with the providers of the container on the class level will influence all further
|
||||||
|
instances.
|
||||||
|
|
||||||
.. image:: /images/containers/declarative_inheritance.png
|
The declarative container can not have any methods or any other attributes then providers.
|
||||||
:width: 100%
|
|
||||||
:align: center
|
The container class provides next attributes:
|
||||||
|
|
||||||
|
- ``providers`` - the dictionary of all the container providers
|
||||||
|
- ``cls_providers`` - the dictionary of the container providers of the current container
|
||||||
|
- ``inherited_providers`` - the dictionary of all the inherited container providers
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/declarative_inheritance.py
|
.. literalinclude:: ../../examples/containers/declarative_inheritance.py
|
||||||
:language: python
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
Example of declarative containers's provider injections:
|
Injections in the declarative container are done the usual way:
|
||||||
|
|
||||||
.. image:: /images/containers/declarative_injections.png
|
|
||||||
:width: 100%
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/declarative_injections.py
|
.. literalinclude:: ../../examples/containers/declarative_injections.py
|
||||||
:language: python
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
|
You can override the container providers when you create the container instance:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/containers/declarative_override_providers.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -1,28 +1,25 @@
|
||||||
Dynamic containers
|
Dynamic container
|
||||||
------------------
|
-----------------
|
||||||
|
|
||||||
.. currentmodule:: dependency_injector.containers
|
.. currentmodule:: dependency_injector.containers
|
||||||
|
|
||||||
:py:class:`DynamicContainer` is an inversion of control container with dynamic
|
:py:class:`DynamicContainer` is a collection of the providers defined in the runtime.
|
||||||
structure. It should cover most of the cases when list of providers that
|
|
||||||
would be included in container is non-deterministic and depends on
|
|
||||||
application's flow or its configuration (container's structure could be
|
|
||||||
determined just after application will be started and will do some initial
|
|
||||||
work, like parsing list of container's providers from the configuration).
|
|
||||||
|
|
||||||
While :py:class:`DeclarativeContainer` acts on class-level,
|
You create the dynamic container instance and put the providers as attributes.
|
||||||
:py:class:`DynamicContainer` does the same on instance-level.
|
|
||||||
|
|
||||||
Here is an simple example of defining dynamic container with several factories:
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/dynamic.py
|
.. literalinclude:: ../../examples/containers/dynamic.py
|
||||||
:language: python
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
Next example demonstrates creation of dynamic container based on some
|
The dynamic container is good for the case when your application structure depends on the
|
||||||
configuration:
|
configuration file or some other source that you can reach only after application is already
|
||||||
|
running (database, api, etc).
|
||||||
|
|
||||||
|
In this example we use the configuration to fill in the dynamic container with the providers:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/dynamic_runtime_creation.py
|
.. literalinclude:: ../../examples/containers/dynamic_runtime_creation.py
|
||||||
:language: python
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
IoC Containers
|
Containers
|
||||||
==============
|
==========
|
||||||
|
|
||||||
Containers are collections of providers. Main purpose of containers is to group
|
Containers are collections of the providers.
|
||||||
providers.
|
|
||||||
|
|
||||||
There are, actually, several popular cases of containers usage:
|
There are several use cases how you can use containers:
|
||||||
|
|
||||||
+ Keeping all providers in a single container.
|
+ Keeping all the providers in a single container (most common).
|
||||||
+ Grouping of providers from the same architectural layer (for example,
|
+ Grouping of the providers from the same architectural layer (for example,
|
||||||
``Services``, ``Models`` and ``Forms`` containers).
|
``Services``, ``Models`` and ``Forms`` containers).
|
||||||
+ Grouping of providers from the same functional groups (for example,
|
+ Grouping of providers from the same functional groups (for example,
|
||||||
container ``Users``, that contains all functional parts of ``Users``
|
container ``Users``, that contains all functional parts of the ``users``
|
||||||
component).
|
package).
|
||||||
|
|
||||||
Also, for both of these and some other cases, it might be useful to attach
|
|
||||||
some init / shutdown functionality or something else, that deals with group
|
|
||||||
of providers.
|
|
||||||
|
|
||||||
Containers package API docs - :py:mod:`dependency_injector.containers`.
|
Containers package API docs - :py:mod:`dependency_injector.containers`.
|
||||||
|
|
||||||
|
|
|
@ -1,41 +1,24 @@
|
||||||
Overriding of containers
|
Overriding of the container
|
||||||
------------------------
|
---------------------------
|
||||||
|
|
||||||
.. currentmodule:: dependency_injector.containers
|
.. currentmodule:: dependency_injector.containers
|
||||||
|
|
||||||
Containers can be overridden by other containers. This, actually, means that
|
The container can be overridden by the other container. All of the providers from the overriding
|
||||||
all of the providers from overriding container will override providers with
|
container will override the providers with the same names in the overridden container.
|
||||||
the same names in overridden container.
|
|
||||||
|
|
||||||
There are two ways to override :py:class:`DeclarativeContainer` with another
|
.. literalinclude:: ../../examples/containers/override.py
|
||||||
container:
|
|
||||||
|
|
||||||
- Use :py:meth:`DeclarativeContainer.override` method.
|
|
||||||
- Use :py:func:`override` class decorator.
|
|
||||||
|
|
||||||
Example of overriding container using :py:meth:`DeclarativeContainer.override`
|
|
||||||
method:
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/override_declarative.py
|
|
||||||
:language: python
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
Example of overriding container using :py:func:`override` decorator:
|
It helps in a testing. Also you can use it for configuring project for the different
|
||||||
|
environments: replace an API client with a stub on the dev or stage.
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/override_declarative_decorator.py
|
The container also has:
|
||||||
:language: python
|
|
||||||
|
|
||||||
Also there are several useful :py:class:`DeclarativeContainer` methods and
|
- ``container.overridden`` - tuple of all overriding containers.
|
||||||
properties that help to work with container overridings:
|
- ``container.reset_last_overriding()`` - reset last overriding for each provider in the container.
|
||||||
|
- ``container.reset_override()`` - reset all overriding in the container.
|
||||||
- :py:attr:`DeclarativeContainer.overridden` - tuple of all overriding
|
|
||||||
containers.
|
|
||||||
- :py:meth:`DeclarativeContainer.reset_last_overriding()` - reset last
|
|
||||||
overriding provider for each container providers.
|
|
||||||
- :py:meth:`DeclarativeContainer.reset_override()` - reset all overridings
|
|
||||||
for each container providers.
|
|
||||||
|
|
||||||
:py:class:`DynamicContainer` has exactly the same functionality, except of
|
|
||||||
:py:func:`override` decorator.
|
|
||||||
|
|
||||||
|
:py:class:`DynamicContainer` has the same functionality.
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
Specialization of containers
|
Specialization of the container provider type
|
||||||
----------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
.. currentmodule:: dependency_injector.containers
|
.. currentmodule:: dependency_injector.containers
|
||||||
|
|
||||||
:py:class:`DeclarativeContainer` could be specialized for any kind of needs
|
You can make a restriction of the :py:class:`DeclarativeContainer` provider type:
|
||||||
via declaring its subclasses.
|
|
||||||
|
|
||||||
One of such `builtin` features is a limitation for providers type.
|
|
||||||
|
|
||||||
Next example shows usage of this feature with :py:class:`DeclarativeContainer`
|
|
||||||
in couple with feature of :py:class:`dependency_injector.providers.Factory`
|
|
||||||
for limitation of its provided type:
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/declarative_provider_type.py
|
.. literalinclude:: ../../examples/containers/declarative_provider_type.py
|
||||||
:language: python
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
:emphasize-lines: 29-31
|
||||||
|
|
||||||
Limitation for providers type could be used with :py:class:`DynamicContainer`
|
The emphasized lines will cause an error because ``other_provider`` is not a subtype of the
|
||||||
as well:
|
``ServiceProvider``. This helps to control the content of the container.
|
||||||
|
|
||||||
|
The same works for the :py:class:`DynamicContainer`:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/containers/dynamic_provider_type.py
|
.. literalinclude:: ../../examples/containers/dynamic_provider_type.py
|
||||||
:language: python
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
:emphasize-lines: 23
|
||||||
|
|
||||||
|
The emphasized line will also cause an error.
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 23 KiB |
Binary file not shown.
Before Width: | Height: | Size: 64 KiB |
Binary file not shown.
Before Width: | Height: | Size: 112 KiB |
|
@ -7,6 +7,11 @@ 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.30.3
|
||||||
|
------
|
||||||
|
- Update README.
|
||||||
|
- Update containers documentation and examples.
|
||||||
|
|
||||||
3.30.2
|
3.30.2
|
||||||
------
|
------
|
||||||
- Update README.
|
- Update README.
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
"""Declarative IoC container simple example."""
|
"""Declarative container example."""
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
from dependency_injector import containers, providers
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
# Defining declarative IoC container:
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
"""Example IoC container."""
|
|
||||||
|
|
||||||
factory1 = providers.Factory(object)
|
factory1 = providers.Factory(object)
|
||||||
|
|
||||||
factory2 = providers.Factory(object)
|
factory2 = providers.Factory(object)
|
||||||
|
|
||||||
|
|
||||||
# Creating some objects:
|
if __name__ == '__main__':
|
||||||
object1 = Container.factory1()
|
container = Container()
|
||||||
object2 = Container.factory2()
|
|
||||||
|
|
||||||
# Making some asserts:
|
object1 = container.factory1()
|
||||||
assert object1 is not object2
|
object2 = container.factory2()
|
||||||
assert isinstance(object1, object)
|
|
||||||
assert isinstance(object2, object)
|
print(container.providers)
|
||||||
|
# {
|
||||||
|
# 'factory1': <dependency_injector.providers.Factory(...),
|
||||||
|
# 'factory2': <dependency_injector.providers.Factory(...),
|
||||||
|
# }
|
||||||
|
|
|
@ -1,30 +1,34 @@
|
||||||
"""Declarative IoC containers inheritance example."""
|
"""Declarative containers inheritance example."""
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
from dependency_injector import containers, providers
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
class ContainerA(containers.DeclarativeContainer):
|
class ContainerA(containers.DeclarativeContainer):
|
||||||
"""Example IoC container A."""
|
|
||||||
|
|
||||||
provider1 = providers.Factory(object)
|
provider1 = providers.Factory(object)
|
||||||
|
|
||||||
|
|
||||||
class ContainerB(ContainerA):
|
class ContainerB(ContainerA):
|
||||||
"""Example IoC container B."""
|
|
||||||
|
|
||||||
provider2 = providers.Singleton(object)
|
provider2 = providers.Singleton(object)
|
||||||
|
|
||||||
|
|
||||||
# Making some asserts for `providers` attribute:
|
assert ContainerA.providers == {
|
||||||
assert ContainerA.providers == dict(provider1=ContainerA.provider1)
|
'provider1': ContainerA.provider1,
|
||||||
assert ContainerB.providers == dict(provider1=ContainerA.provider1,
|
}
|
||||||
provider2=ContainerB.provider2)
|
assert ContainerB.providers == {
|
||||||
|
'provider1': ContainerA.provider1,
|
||||||
|
'provider2': ContainerB.provider2,
|
||||||
|
}
|
||||||
|
|
||||||
# Making some asserts for `cls_providers` attribute:
|
assert ContainerA.cls_providers == {
|
||||||
assert ContainerA.cls_providers == dict(provider1=ContainerA.provider1)
|
'provider1': ContainerA.provider1,
|
||||||
assert ContainerB.cls_providers == dict(provider2=ContainerB.provider2)
|
}
|
||||||
|
assert ContainerB.cls_providers == {
|
||||||
|
'provider2': ContainerB.provider2,
|
||||||
|
}
|
||||||
|
|
||||||
# Making some asserts for `inherited_providers` attribute:
|
assert ContainerA.inherited_providers == {}
|
||||||
assert ContainerA.inherited_providers == dict()
|
assert ContainerB.inherited_providers == {
|
||||||
assert ContainerB.inherited_providers == dict(provider1=ContainerB.provider1)
|
'provider1': ContainerA.provider1,
|
||||||
|
}
|
||||||
|
|
|
@ -1,35 +1,42 @@
|
||||||
"""Declarative IoC container's provider injections example."""
|
"""Declarative container injections example."""
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import collections
|
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
from dependency_injector import containers, providers
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
UsersService = collections.namedtuple('UsersService', ['db'])
|
class UserService:
|
||||||
AuthService = collections.namedtuple('AuthService', ['db', 'users_service'])
|
def __init__(self, db: sqlite3.Connection):
|
||||||
|
self.db = db
|
||||||
|
|
||||||
|
|
||||||
class Services(containers.DeclarativeContainer):
|
class AuthService:
|
||||||
"""IoC container of service providers."""
|
def __init__(self, db: sqlite3.Connection, user_service: UserService):
|
||||||
|
self.db = db
|
||||||
|
self.user_service = user_service
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
database = providers.Singleton(sqlite3.connect, ':memory:')
|
database = providers.Singleton(sqlite3.connect, ':memory:')
|
||||||
|
|
||||||
users = providers.Factory(UsersService,
|
user_service = providers.Factory(
|
||||||
db=database)
|
UserService,
|
||||||
|
db=database,
|
||||||
|
)
|
||||||
|
|
||||||
auth = providers.Factory(AuthService,
|
auth_service = providers.Factory(
|
||||||
db=database,
|
AuthService,
|
||||||
users_service=users)
|
db=database,
|
||||||
|
user_service=user_service,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Retrieving service providers from container:
|
if __name__ == '__main__':
|
||||||
users_service = Services.users()
|
container = Container()
|
||||||
auth_service = Services.auth()
|
|
||||||
|
|
||||||
# Making some asserts:
|
user_service = container.user_service()
|
||||||
assert users_service.db is auth_service.db is Services.database()
|
auth_service = container.auth_service()
|
||||||
assert isinstance(auth_service.users_service, UsersService)
|
|
||||||
assert users_service is not Services.users()
|
assert user_service.db is auth_service.db is container.database()
|
||||||
assert auth_service is not Services.auth()
|
assert isinstance(auth_service.user_service, UserService)
|
||||||
|
|
18
examples/containers/declarative_override_providers.py
Normal file
18
examples/containers/declarative_override_providers.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
"""Declarative container provider override example."""
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
database = providers.Singleton(sqlite3.connect, ':memory:')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container(database=mock.Mock(sqlite3.Connection))
|
||||||
|
|
||||||
|
database = container.database()
|
||||||
|
assert isinstance(database, mock.Mock)
|
|
@ -1,48 +1,33 @@
|
||||||
"""Specializing declarative container and factory provider example."""
|
"""Declarative container provider type restriction example."""
|
||||||
|
|
||||||
import collections
|
import abc
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
from dependency_injector import containers, providers
|
||||||
import dependency_injector.providers as providers
|
|
||||||
import dependency_injector.errors as errors
|
|
||||||
|
|
||||||
|
|
||||||
class SequenceProvider(providers.Factory):
|
class Service(metaclass=abc.ABCMeta):
|
||||||
"""Sequence factory.
|
...
|
||||||
|
|
||||||
Can provide only sequence objects.
|
|
||||||
"""
|
|
||||||
|
|
||||||
provided_type = collections.Sequence
|
|
||||||
|
|
||||||
|
|
||||||
class SequencesContainer(containers.DeclarativeContainer):
|
class UserService(Service):
|
||||||
"""IoC container.
|
...
|
||||||
|
|
||||||
Can contain only sequence providers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
provider_type = SequenceProvider
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
class ServiceProvider(providers.Factory):
|
||||||
try:
|
|
||||||
class _SequenceContainer1(SequencesContainer):
|
|
||||||
object_provider = providers.Factory(object)
|
|
||||||
except errors.Error as exception:
|
|
||||||
print(exception)
|
|
||||||
# <class '__main__._SequenceContainer1'> can contain only
|
|
||||||
# <class '__main__.SequenceProvider'> instances
|
|
||||||
|
|
||||||
try:
|
provided_type = Service
|
||||||
class _SequenceContainer2(SequencesContainer):
|
|
||||||
object_provider = SequenceProvider(object)
|
|
||||||
except errors.Error as exception:
|
|
||||||
print(exception)
|
|
||||||
# <class '__main__.SequenceProvider'> can provide only
|
|
||||||
# <class '_abcoll.Sequence'> instances
|
|
||||||
|
|
||||||
class _SequenceContaier3(SequencesContainer):
|
|
||||||
list_provider = SequenceProvider(list)
|
|
||||||
|
|
||||||
assert _SequenceContaier3.list_provider() == list()
|
class ServiceContainer(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
provider_type = ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
|
class MyServices(ServiceContainer):
|
||||||
|
|
||||||
|
user_service = ServiceProvider(UserService)
|
||||||
|
|
||||||
|
|
||||||
|
class ImproperServices(ServiceContainer):
|
||||||
|
|
||||||
|
other_provider = providers.Factory(object)
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
"""Dynamic container simple example."""
|
"""Dynamic container example."""
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
from dependency_injector import containers, providers
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
# Defining dynamic container:
|
if __name__ == '__main__':
|
||||||
container = containers.DynamicContainer()
|
container = containers.DynamicContainer()
|
||||||
container.factory1 = providers.Factory(object)
|
container.factory1 = providers.Factory(object)
|
||||||
container.factory2 = providers.Factory(object)
|
container.factory2 = providers.Factory(object)
|
||||||
|
|
||||||
# Creating some objects:
|
object1 = container.factory1()
|
||||||
object1 = container.factory1()
|
object2 = container.factory2()
|
||||||
object2 = container.factory2()
|
|
||||||
|
|
||||||
# Making some asserts:
|
print(container.providers)
|
||||||
assert object1 is not object2
|
# {
|
||||||
assert isinstance(object1, object) and isinstance(object2, object)
|
# 'factory1': <dependency_injector.providers.Factory(...),
|
||||||
|
# 'factory2': <dependency_injector.providers.Factory(...),
|
||||||
|
# }
|
||||||
|
|
|
@ -1,41 +1,25 @@
|
||||||
"""Specializing dynamic container and factory provider example."""
|
"""Dynamic container provider type restriction example."""
|
||||||
|
|
||||||
import collections
|
import abc
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
from dependency_injector import containers, providers
|
||||||
import dependency_injector.providers as providers
|
|
||||||
import dependency_injector.errors as errors
|
|
||||||
|
|
||||||
|
|
||||||
class SequenceProvider(providers.Factory):
|
class Service(metaclass=abc.ABCMeta):
|
||||||
"""Sequence factory.
|
...
|
||||||
|
|
||||||
Can provide only sequence objects.
|
|
||||||
"""
|
|
||||||
|
|
||||||
provided_type = collections.Sequence
|
|
||||||
|
|
||||||
|
|
||||||
sequences_container = containers.DynamicContainer()
|
class UserService(Service):
|
||||||
sequences_container.provider_type = SequenceProvider
|
...
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
class ServiceProvider(providers.Factory):
|
||||||
try:
|
|
||||||
sequences_container.object_provider = providers.Factory(object)
|
|
||||||
except errors.Error as exception:
|
|
||||||
print(exception)
|
|
||||||
# <dependency_injector.containers.DynamicContainer object at
|
|
||||||
# 0x107820ed0> can contain only <class '__main__.SequenceProvider'>
|
|
||||||
# instances
|
|
||||||
|
|
||||||
try:
|
provided_type = Service
|
||||||
sequences_container.object_provider = SequenceProvider(object)
|
|
||||||
except errors.Error as exception:
|
|
||||||
print(exception)
|
|
||||||
# <class '__main__.SequenceProvider'> can provide only
|
|
||||||
# <class '_abcoll.Sequence'> instances
|
|
||||||
|
|
||||||
sequences_container.list_provider = SequenceProvider(list)
|
|
||||||
|
|
||||||
assert sequences_container.list_provider() == list()
|
services = containers.DynamicContainer()
|
||||||
|
services.provider_type = ServiceProvider
|
||||||
|
|
||||||
|
services.user_service = ServiceProvider(UserService)
|
||||||
|
services.other_provider = providers.Factory(object)
|
||||||
|
|
|
@ -1,59 +1,40 @@
|
||||||
"""Creation of dynamic container based on some configuration example."""
|
"""Creation of dynamic container based on the configuration example."""
|
||||||
|
|
||||||
import collections
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
|
||||||
|
|
||||||
|
|
||||||
# Defining several example services:
|
class UserService:
|
||||||
UsersService = collections.namedtuple('UsersService', [])
|
...
|
||||||
AuthService = collections.namedtuple('AuthService', [])
|
|
||||||
|
|
||||||
|
|
||||||
def import_cls(cls_name):
|
class AuthService:
|
||||||
"""Import class by its fully qualified name.
|
...
|
||||||
|
|
||||||
In terms of current example it is just a small helper function. Please,
|
|
||||||
don't use it in production approaches.
|
|
||||||
"""
|
|
||||||
path_components = cls_name.split('.')
|
|
||||||
module = __import__('.'.join(path_components[:-1]),
|
|
||||||
locals(),
|
|
||||||
globals(),
|
|
||||||
fromlist=path_components[-1:])
|
|
||||||
return getattr(module, path_components[-1])
|
|
||||||
|
|
||||||
|
|
||||||
# "Parsing" some configuration:
|
def populate_container(container, providers_config):
|
||||||
config = {
|
for provider_name, provider_info in providers_config.items():
|
||||||
'services': {
|
provided_cls = globals().get(provider_info['class'])
|
||||||
'users': {
|
provider_cls = getattr(providers, provider_info['provider_class'])
|
||||||
'class': '__main__.UsersService',
|
setattr(container, provider_name, provider_cls(provided_cls))
|
||||||
'provider_class': 'dependency_injector.providers.Factory',
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
services_config = {
|
||||||
|
'user': {
|
||||||
|
'class': 'UserService',
|
||||||
|
'provider_class': 'Factory',
|
||||||
},
|
},
|
||||||
'auth': {
|
'auth': {
|
||||||
'class': '__main__.AuthService',
|
'class': 'AuthService',
|
||||||
'provider_class': 'dependency_injector.providers.Factory',
|
'provider_class': 'Factory',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
services = containers.DynamicContainer()
|
||||||
|
|
||||||
# Creating empty container of service providers:
|
populate_container(services, services_config)
|
||||||
services = containers.DynamicContainer()
|
|
||||||
|
|
||||||
# Filling dynamic container with service providers using configuration:
|
user_service = services.user()
|
||||||
for service_name, service_info in config['services'].iteritems():
|
auth_service = services.auth()
|
||||||
# Runtime importing of service and service provider classes:
|
|
||||||
service_cls = import_cls(service_info['class'])
|
|
||||||
service_provider_cls = import_cls(service_info['provider_class'])
|
|
||||||
|
|
||||||
# Binding service provider to the dynamic service providers catalog:
|
assert isinstance(user_service, UserService)
|
||||||
setattr(services, service_name, service_provider_cls(service_cls))
|
assert isinstance(auth_service, AuthService)
|
||||||
|
|
||||||
# Creating some objects:
|
|
||||||
users_service = services.users()
|
|
||||||
auth_service = services.auth()
|
|
||||||
|
|
||||||
# Making some asserts:
|
|
||||||
assert isinstance(users_service, UsersService)
|
|
||||||
assert isinstance(auth_service, AuthService)
|
|
||||||
|
|
31
examples/containers/override.py
Normal file
31
examples/containers/override.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
"""Container overriding example."""
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceStub:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
service = providers.Factory(Service)
|
||||||
|
|
||||||
|
|
||||||
|
class OverridingContainer(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
service = providers.Factory(ServiceStub)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
overriding_container = OverridingContainer()
|
||||||
|
|
||||||
|
container.override(overriding_container)
|
||||||
|
|
||||||
|
service = container.service()
|
||||||
|
assert isinstance(service, ServiceStub)
|
|
@ -1,28 +0,0 @@
|
||||||
"""Declarative IoC container overriding example."""
|
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
|
||||||
"""IoC container."""
|
|
||||||
|
|
||||||
sequence_factory = providers.Factory(list)
|
|
||||||
|
|
||||||
|
|
||||||
class OverridingContainer(containers.DeclarativeContainer):
|
|
||||||
"""Overriding IoC container."""
|
|
||||||
|
|
||||||
sequence_factory = providers.Factory(tuple)
|
|
||||||
|
|
||||||
|
|
||||||
# Overriding `Container` with `OverridingContainer`:
|
|
||||||
Container.override(OverridingContainer)
|
|
||||||
|
|
||||||
# Creating some objects using overridden container:
|
|
||||||
sequence_1 = Container.sequence_factory([1, 2, 3])
|
|
||||||
sequence_2 = Container.sequence_factory([3, 2, 1])
|
|
||||||
|
|
||||||
# Making some asserts:
|
|
||||||
assert Container.overridden == (OverridingContainer,)
|
|
||||||
assert sequence_1 == (1, 2, 3) and sequence_2 == (3, 2, 1)
|
|
|
@ -1,27 +0,0 @@
|
||||||
"""Declarative IoC container overriding using `@override()` decorator."""
|
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
|
||||||
"""IoC container."""
|
|
||||||
|
|
||||||
sequence_factory = providers.Factory(list)
|
|
||||||
|
|
||||||
|
|
||||||
# Overriding `Container` with `OverridingContainer`:
|
|
||||||
@containers.override(Container)
|
|
||||||
class OverridingContainer(containers.DeclarativeContainer):
|
|
||||||
"""Overriding IoC container."""
|
|
||||||
|
|
||||||
sequence_factory = providers.Factory(tuple)
|
|
||||||
|
|
||||||
|
|
||||||
# Creating some objects using overridden container:
|
|
||||||
sequence_1 = Container.sequence_factory([1, 2, 3])
|
|
||||||
sequence_2 = Container.sequence_factory([3, 2, 1])
|
|
||||||
|
|
||||||
# Making some asserts:
|
|
||||||
assert Container.overridden == (OverridingContainer,)
|
|
||||||
assert sequence_1 == (1, 2, 3) and sequence_2 == (3, 2, 1)
|
|
|
@ -1,2 +0,0 @@
|
||||||
api_key: test-key
|
|
||||||
timeout: 5
|
|
|
@ -32,8 +32,8 @@ class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
container = Container()
|
container = Container()
|
||||||
container.config.from_yaml('config.yml')
|
container.config.api_key.from_env('API_KEY')
|
||||||
|
container.config.timeout.from_env('TIMEOUT')
|
||||||
|
|
||||||
service = container.service()
|
service = container.service()
|
||||||
|
|
||||||
assert isinstance(service.api_client, ApiClient)
|
assert isinstance(service.api_client, ApiClient)
|
||||||
|
|
18
examples/di_demo2/example_di.py
Normal file
18
examples/di_demo2/example_di.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class ApiClient:
|
||||||
|
|
||||||
|
def __init__(self, api_key: str, timeout: int):
|
||||||
|
self.api_key = api_key
|
||||||
|
self.timeout = timeout
|
||||||
|
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
|
||||||
|
def __init__(self, api_client: ApiClient):
|
||||||
|
self.api_client = api_client
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
|
18
examples/di_demo2/example_no_di.py
Normal file
18
examples/di_demo2/example_no_di.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class ApiClient:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.api_key = os.getenv('API_KEY')
|
||||||
|
self.timeout = os.getenv('TIMEOUT')
|
||||||
|
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.api_client = ApiClient()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
service = Service()
|
11
examples/di_demo2/test.py
Normal file
11
examples/di_demo2/test.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from demo import Container
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
with container.api_client.override(mock.Mock()):
|
||||||
|
service = container.service()
|
||||||
|
assert isinstance(service.api_client, mock.Mock)
|
|
@ -1,6 +1,6 @@
|
||||||
"""Dependency injector top-level package."""
|
"""Dependency injector top-level package."""
|
||||||
|
|
||||||
__version__ = '3.30.2'
|
__version__ = '3.30.3'
|
||||||
"""Version number that follows semantic versioning.
|
"""Version number that follows semantic versioning.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
Loading…
Reference in New Issue
Block a user