Update DI Demo 2

This commit is contained in:
Roman Mogylatov 2020-08-15 11:01:58 -04:00
parent e479e2cb94
commit e0fa746d7f
6 changed files with 108 additions and 38 deletions

View File

@ -52,7 +52,7 @@ What is ``Dependency Injector``?
``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?
-----------------------------
@ -70,6 +70,9 @@ Before:
.. code-block:: python
import os
class ApiClient:
def __init__(self):
@ -82,10 +85,18 @@ Before:
def __init__(self):
self.api_client = ApiClient()
if __name__ == '__main__':
service = Service()
After:
.. code-block:: python
import os
class ApiClient:
def __init__(self, api_key: str, timeout: int):
@ -98,20 +109,40 @@ After:
def __init__(self, api_client: ApiClient):
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?
---------------------------------
``Dependency Injector`` provides you with the container and the providers that help you build
your application objects when you apply dependency injection principle:
``Dependency Injector`` helps you assemble the objects.
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
from dependency_injector import containers, providers
from unittest import mock
from .example_di import ApiClient, Service
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
class Container(containers.DeclarativeContainer):
@ -132,15 +163,29 @@ your application objects when you apply dependency injection principle:
if __name__ == '__main__':
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()
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>`_
Installation

View File

@ -1,2 +0,0 @@
api_key: test-key
timeout: 5

View File

@ -1,7 +1,17 @@
from dependency_injector import containers, providers
from unittest import mock
from .example_di import ApiClient, Service
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
class Container(containers.DeclarativeContainer):
@ -22,11 +32,8 @@ class Container(containers.DeclarativeContainer):
if __name__ == '__main__':
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()
assert isinstance(service.api_client, ApiClient)
with container.api_client.override(mock.Mock()):
service = container.service()
assert isinstance(service.api_client, mock.Mock)

View File

@ -1,3 +1,4 @@
import os
class ApiClient:
@ -11,3 +12,7 @@ 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')))

View File

@ -12,3 +12,7 @@ class Service:
def __init__(self):
self.api_client = ApiClient()
if __name__ == '__main__':
service = Service()

11
examples/di_demo2/test.py Normal file
View 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)