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. ``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,48 +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
from unittest import mock
from .example_di import ApiClient, Service
class Container(containers.DeclarativeContainer): class ApiClient:
config = providers.Configuration() def __init__(self, api_key: str, timeout: int):
self.api_key = api_key
api_client = providers.Singleton( self.timeout = timeout
ApiClient,
api_key=config.api_key,
timeout=config.timeout,
)
service = providers.Factory(
Service,
api_client=api_client,
)
if __name__ == '__main__': class Service:
container = Container()
container.config.from_yaml('config.yml')
service = container.service() def __init__(self, api_client: ApiClient):
assert isinstance(service.api_client, ApiClient) self.api_client = api_client
with container.api_client.override(mock.Mock()):
service = container.service() class Container(containers.DeclarativeContainer):
assert isinstance(service.api_client, mock.Mock)
config = providers.Configuration()
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout,
)
service = providers.Factory(
Service,
api_client=api_client,
)
if __name__ == '__main__':
container = Container()
container.config.api_key.from_env('API_KEY')
container.config.timeout.from_env('TIMEOUT')
service = container.service()
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>`_

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 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): class Container(containers.DeclarativeContainer):
@ -22,11 +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)
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: class ApiClient:
@ -11,3 +12,7 @@ class Service:
def __init__(self, api_client: ApiClient): def __init__(self, api_client: ApiClient):
self.api_client = api_client 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): def __init__(self):
self.api_client = ApiClient() 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)