mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-01-30 19:24:31 +03:00
Update DI in Python page
This commit is contained in:
parent
65c883ad57
commit
f7d3ada92f
|
@ -77,10 +77,14 @@ Before:
|
|||
self.api_client = ApiClient() # <-- the dependency
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
service = Service()
|
||||
def main() -> None:
|
||||
service = Service() # <-- the dependency
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
After:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -101,8 +105,19 @@ After:
|
|||
self.api_client = api_client # <-- the dependency is injected
|
||||
|
||||
|
||||
def main(service: Service): # <-- the dependency is injected
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
|
||||
main(
|
||||
service=Service(
|
||||
api_client=ApiClient(
|
||||
api_key=os.getenv('API_KEY'),
|
||||
timeout=os.getenv('TIMEOUT'),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a
|
||||
timeout from a configuration file or even get them from a database.
|
||||
|
@ -110,11 +125,22 @@ timeout from a configuration file or even get them from a database.
|
|||
``Service`` is decoupled from the ``ApiClient``. It does not create it anymore. You can provide a
|
||||
stub or other compatible object.
|
||||
|
||||
Function ``main()`` is decoupled from ``Service``. It receives it as an argument.
|
||||
|
||||
Flexibility comes with a price.
|
||||
|
||||
Now you need to assemble the objects like this::
|
||||
Now you need to assemble and inject the objects like this:
|
||||
|
||||
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
|
||||
.. code-block:: python
|
||||
|
||||
main(
|
||||
service=Service(
|
||||
api_client=ApiClient(
|
||||
api_key=os.getenv('API_KEY'),
|
||||
timeout=os.getenv('TIMEOUT'),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
The assembly code might get duplicated and it'll become harder to change the application structure.
|
||||
|
||||
|
@ -123,18 +149,20 @@ Here comes the ``Dependency Injector``.
|
|||
What does the Dependency Injector do?
|
||||
-------------------------------------
|
||||
|
||||
With the dependency injection pattern objects loose the responsibility of assembling the
|
||||
dependencies. The ``Dependency Injector`` absorbs that responsibility.
|
||||
With the dependency injection pattern objects loose the responsibility of assembling
|
||||
the dependencies. The ``Dependency Injector`` absorbs that responsibilities.
|
||||
|
||||
``Dependency Injector`` helps to assemble the objects.
|
||||
``Dependency Injector`` helps to assemble and inject the dependencies.
|
||||
|
||||
It provides a container and providers that help you with the objects assembly. When you
|
||||
need an object you get it from the container. The rest of the assembly work is done by the
|
||||
framework:
|
||||
It provides a container and providers that help you with the objects assembly.
|
||||
When you need an object you place a ``Provide`` marker as a default value of a
|
||||
function argument. When you call this function framework assembles and injects
|
||||
the dependency.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -153,22 +181,29 @@ framework:
|
|||
)
|
||||
|
||||
|
||||
def main(service: Service = Provide[Container.service]):
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
container = Container()
|
||||
|
||||
container.config.api_key.from_env('API_KEY')
|
||||
container.config.timeout.from_env('TIMEOUT')
|
||||
|
||||
service = container.service()
|
||||
container.wire(modules=[sys.modules[__name__]])
|
||||
|
||||
Retrieving of the ``Service`` instance now is done like this::
|
||||
main()
|
||||
|
||||
service = container.service()
|
||||
When you call ``main()`` function the ``Service`` dependency is assembled and injected::
|
||||
|
||||
main()
|
||||
|
||||
Objects assembling is consolidated in the container. When you need to make a change you do it in
|
||||
one place.
|
||||
|
||||
When doing a testing you call the ``container.api_client.override()`` to replace the real API
|
||||
client with a mock:
|
||||
client with a mock. When you call ``main()`` the mock is injected:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -176,7 +211,7 @@ client with a mock:
|
|||
|
||||
|
||||
with container.api_client.override(mock.Mock()):
|
||||
service = container.service()
|
||||
main()
|
||||
|
||||
You can override any provider with another provider.
|
||||
|
||||
|
@ -261,6 +296,7 @@ Choose one of the following as a next step:
|
|||
- :ref:`cli-tutorial`
|
||||
- Know more about the ``Dependency Injector`` :ref:`key-features`
|
||||
- Know more about the :ref:`providers`
|
||||
- Know more about the :ref:`wiring`
|
||||
- Go to the :ref:`contents`
|
||||
|
||||
Useful links
|
||||
|
|
29
examples/demo/after.py
Normal file
29
examples/demo/after.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
import os
|
||||
|
||||
|
||||
class ApiClient:
|
||||
|
||||
def __init__(self, api_key: str, timeout: int):
|
||||
self.api_key = api_key # <-- the dependency is injected
|
||||
self.timeout = timeout # <-- the dependency is injected
|
||||
|
||||
|
||||
class Service:
|
||||
|
||||
def __init__(self, api_client: ApiClient):
|
||||
self.api_client = api_client # <-- the dependency is injected
|
||||
|
||||
|
||||
def main(service: Service): # <-- the dependency is injected
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(
|
||||
service=Service(
|
||||
api_client=ApiClient(
|
||||
api_key=os.getenv('API_KEY'),
|
||||
timeout=os.getenv('TIMEOUT'),
|
||||
),
|
||||
),
|
||||
)
|
23
examples/demo/before.py
Normal file
23
examples/demo/before.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
import os
|
||||
|
||||
|
||||
class ApiClient:
|
||||
|
||||
def __init__(self):
|
||||
self.api_key = os.getenv('API_KEY') # <-- the dependency
|
||||
self.timeout = os.getenv('TIMEOUT') # <-- the dependency
|
||||
|
||||
|
||||
class Service:
|
||||
|
||||
def __init__(self):
|
||||
self.api_client = ApiClient() # <-- the dependency
|
||||
|
||||
|
||||
def main() -> None:
|
||||
service = Service() # <-- the dependency
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,28 +0,0 @@
|
|||
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
|
||||
|
||||
|
||||
def main() -> None:
|
||||
service = Service(
|
||||
api_client=ApiClient(
|
||||
api_key=os.getenv('API_KEY'),
|
||||
timeout=os.getenv('TIMEOUT'),
|
||||
),
|
||||
)
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,23 +0,0 @@
|
|||
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()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
service = Service()
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -4,7 +4,7 @@ from unittest import mock
|
|||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
|
||||
from di import ApiClient, Service
|
||||
from after import ApiClient, Service
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
Loading…
Reference in New Issue
Block a user