mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 01:26:51 +03:00
Refactor services miniapps (#291)
* Refactor services mini app with single container * Make few little fixes to single container app * Update requirements.txt for single container example * Refactor multiple containers example * Add single container docs page * Create multiple containers page
This commit is contained in:
parent
d16e8817db
commit
bf978601ba
22
docs/examples-other/index.rst
Normal file
22
docs/examples-other/index.rst
Normal file
|
@ -0,0 +1,22 @@
|
|||
Other examples
|
||||
==============
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
|
||||
:description: Current section of documentation is designed to provide
|
||||
several example mini applications that are built on the top
|
||||
of inversion of control principle and powered by
|
||||
"Dependency Injector" framework.
|
||||
|
||||
Current section of documentation is designed to provide several example mini
|
||||
applications that are built according to the inversion of control principle
|
||||
and powered by *Dependency Injector* framework.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
bundles_miniapp
|
||||
use_cases_miniapp
|
||||
password_hashing_miniapp
|
||||
chained_factories
|
||||
factory_of_factories
|
87
docs/examples/application-multiple-containers.rst
Normal file
87
docs/examples/application-multiple-containers.rst
Normal file
|
@ -0,0 +1,87 @@
|
|||
.. _application-multiple-containers:
|
||||
|
||||
Application example (multiple containers)
|
||||
=========================================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
|
||||
Framework,AWS,boto3,client
|
||||
:description: This example shows how you can create an application using multiple declarative
|
||||
containers. We build an example Python micro application following the dependency
|
||||
injection principle. It consists from several services with a domain logic that
|
||||
have dependencies on database & AWS S3.
|
||||
|
||||
This example shows how you can create an application using multiple declarative containers. Using
|
||||
multiple declarative containers is a good choice for a large application. For
|
||||
building a moderate or a small size application refer to :ref:`application-single-container`.
|
||||
|
||||
We build an example micro application following the dependency injection principle. It consists
|
||||
of several services with a domain logic. The services have dependencies on database & AWS S3.
|
||||
|
||||
.. image:: images/application.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
Start from the scratch or jump to the section:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:backlinks: none
|
||||
|
||||
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/application-multiple-containers>`_.
|
||||
|
||||
Application structure
|
||||
---------------------
|
||||
|
||||
Application consists of an ``example`` package, a configuration file and a ``requirements.txt``
|
||||
file.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./
|
||||
├── example/
|
||||
│ ├── __init__.py
|
||||
│ ├── __main__.py
|
||||
│ ├── containers.py
|
||||
│ └── services.py
|
||||
├── config.yml
|
||||
└── requirements.txt
|
||||
|
||||
Containers
|
||||
----------
|
||||
|
||||
Listing of the ``example/containers.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/containers.py
|
||||
:language: python
|
||||
|
||||
Main module
|
||||
-----------
|
||||
|
||||
Listing of the ``example/__main__.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/__main__.py
|
||||
:language: python
|
||||
|
||||
Services
|
||||
--------
|
||||
|
||||
Listing of the ``example/services.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-multiple-containers/example/services.py
|
||||
:language: python
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Listing of the ``config.yml``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-multiple-containers/config.yml
|
||||
:language: yaml
|
||||
|
||||
Run the application
|
||||
-------------------
|
||||
|
||||
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/application-multiple-containers>`_.
|
||||
|
||||
.. disqus::
|
93
docs/examples/application-single-container.rst
Normal file
93
docs/examples/application-single-container.rst
Normal file
|
@ -0,0 +1,93 @@
|
|||
.. _application-single-container:
|
||||
|
||||
Application example (single container)
|
||||
======================================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
|
||||
Framework,AWS,boto3,client
|
||||
:description: This example shows how you can create an application using a single declarative
|
||||
container. We build an example Python micro application following the dependency
|
||||
injection principle. It consists from several services with a domain logic that
|
||||
have dependencies on database & AWS S3.
|
||||
|
||||
This example shows how you can create an application using a single declarative container. Using
|
||||
a single declarative container is a good choice for small or moderate size application. For
|
||||
building a large application refer to :ref:`application-multiple-containers`.
|
||||
|
||||
We build an example micro application following the dependency injection principle. It consists
|
||||
of several services with a domain logic. The services have dependencies on database & AWS S3.
|
||||
|
||||
.. image:: images/application.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
Start from the scratch or jump to the section:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:backlinks: none
|
||||
|
||||
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/application-single-container>`_.
|
||||
|
||||
Application structure
|
||||
---------------------
|
||||
|
||||
Application consists of an ``example`` package, several configuration files and a
|
||||
``requirements.txt`` file.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./
|
||||
├── example/
|
||||
│ ├── __init__.py
|
||||
│ ├── __main__.py
|
||||
│ ├── containers.py
|
||||
│ └── services.py
|
||||
├── config.ini
|
||||
├── logging.ini
|
||||
└── requirements.txt
|
||||
|
||||
Container
|
||||
---------
|
||||
|
||||
Listing of the ``example/containers.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-single-container/example/containers.py
|
||||
:language: python
|
||||
|
||||
Main module
|
||||
-----------
|
||||
|
||||
Listing of the ``example/__main__.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-single-container/example/__main__.py
|
||||
:language: python
|
||||
|
||||
Services
|
||||
--------
|
||||
|
||||
Listing of the ``example/services.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-single-container/example/services.py
|
||||
:language: python
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Listing of the ``config.ini``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-single-container/config.ini
|
||||
:language: ini
|
||||
|
||||
Listing of the ``logging.ini``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/application-single-container/logging.ini
|
||||
:language: ini
|
||||
|
||||
Run the application
|
||||
-------------------
|
||||
|
||||
You can find the source code and instructions for running on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/application-single-container>`_.
|
||||
|
||||
.. disqus::
|
BIN
docs/examples/images/application.png
Normal file
BIN
docs/examples/images/application.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
|
@ -2,23 +2,15 @@ Examples
|
|||
========
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
|
||||
:description: Current section of documentation is designed to provide
|
||||
several example mini applications that are built on the top
|
||||
of inversion of control principle and powered by
|
||||
"Dependency Injector" framework.
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Example
|
||||
:description: Python dependency injection examples.
|
||||
|
||||
Current section of documentation is designed to provide several example mini
|
||||
applications that are built according to the inversion of control principle
|
||||
and powered by *Dependency Injector* framework.
|
||||
Explore the examples to see the ``Dependency Injector`` in action.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
services_miniapp_v1
|
||||
services_miniapp_v2
|
||||
bundles_miniapp
|
||||
use_cases_miniapp
|
||||
password_hashing_miniapp
|
||||
chained_factories
|
||||
factory_of_factories
|
||||
application-single-container
|
||||
application-multiple-containers
|
||||
|
||||
.. disqus::
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
Services mini application example (v1 - multiple containers)
|
||||
------------------------------------------------------------
|
||||
|
||||
.. meta::
|
||||
:description: "Services miniapp" is an example mini application that
|
||||
consists from several services that have dependencies on
|
||||
some standard and 3rd-party libraries for logging,
|
||||
interaction with database and remote service via API.
|
||||
"Services miniapp" example demonstrates usage of
|
||||
Dependency Injector for creating several inversion of control /
|
||||
dependency injection containers.
|
||||
|
||||
"Services miniapp" is an example mini application that consists from several
|
||||
services that have dependencies on some standard and 3rd-party libraries for
|
||||
logging, interaction with database and remote service calls via API.
|
||||
|
||||
"Services miniapp" example demonstrates usage of
|
||||
:doc:`Dependency Injector <../index>` for creating several IoC containers.
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py 1 secret photo.jpg
|
||||
|
||||
Example application
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Classes diagram:
|
||||
|
||||
.. image:: /images/miniapps/services/classes.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
|
||||
Example application structure:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/example
|
||||
/__init__.py
|
||||
/main.py
|
||||
/services.py
|
||||
|
||||
|
||||
Listing of ``example/services.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/services_v1/example/services.py
|
||||
:language: python
|
||||
|
||||
Listing of ``example/main.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/services_v1/example/main.py
|
||||
:language: python
|
||||
|
||||
IoC containers
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``containers.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/services_v1/containers.py
|
||||
:language: python
|
||||
|
||||
Run application
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``run.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/services_v1/run.py
|
||||
:language: python
|
||||
|
||||
|
||||
.. disqus::
|
|
@ -1,73 +0,0 @@
|
|||
Services mini application example (v2 - single container)
|
||||
---------------------------------------------------------
|
||||
|
||||
.. meta::
|
||||
:description: "Services miniapp" is an example mini application that
|
||||
consists from several services that have dependencies on
|
||||
some standard and 3rd-party libraries for logging,
|
||||
interaction with database and remote service via API.
|
||||
"Services miniapp" example demonstrates usage of
|
||||
Dependency Injector for creating inversion of control /
|
||||
dependency injection container.
|
||||
|
||||
"Services miniapp" is an example mini application that consists from several
|
||||
services that have dependencies on some standard and 3rd-party libraries for
|
||||
logging, interaction with database and remote service calls via API.
|
||||
|
||||
"Services miniapp" example demonstrates usage of
|
||||
:doc:`Dependency Injector <../index>` for creating IoC container.
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py 1 secret photo.jpg
|
||||
|
||||
Example application
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Classes diagram:
|
||||
|
||||
.. image:: /images/miniapps/services/classes.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
|
||||
Example application structure:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/example
|
||||
/__init__.py
|
||||
/main.py
|
||||
/services.py
|
||||
|
||||
|
||||
Listing of ``example/services.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/services_v2/example/services.py
|
||||
:language: python
|
||||
|
||||
Listing of ``example/main.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/services_v2/example/main.py
|
||||
:language: python
|
||||
|
||||
IoC container
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``container.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/services_v2/container.py
|
||||
:language: python
|
||||
|
||||
Run application
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``run.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/services_v2/run.py
|
||||
:language: python
|
||||
|
||||
|
||||
.. disqus::
|
Binary file not shown.
Before Width: | Height: | Size: 49 KiB |
|
@ -129,11 +129,11 @@ Contents
|
|||
:maxdepth: 2
|
||||
|
||||
introduction/index
|
||||
main/installation
|
||||
examples/index
|
||||
tutorials/index
|
||||
providers/index
|
||||
containers/index
|
||||
examples/index
|
||||
examples-other/index
|
||||
api/index
|
||||
main/feedback
|
||||
main/changelog
|
||||
|
|
|
@ -3,90 +3,90 @@ Dependency injection and inversion of control in Python
|
|||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
|
||||
:description: This article describes benefits of dependency injection and
|
||||
inversion of control for Python applications. Also it
|
||||
contains some Python examples that show how dependency
|
||||
injection and inversion could be implemented. In addition, it
|
||||
demonstrates usage of dependency injection framework,
|
||||
:description: This article describes benefits of dependency injection and
|
||||
inversion of control for Python applications. Also it
|
||||
contains some Python examples that show how dependency
|
||||
injection and inversion could be implemented. In addition, it
|
||||
demonstrates usage of dependency injection framework,
|
||||
IoC container and such popular design pattern as Factory.
|
||||
|
||||
History
|
||||
~~~~~~~
|
||||
|
||||
Originally, dependency injection pattern got popular in languages with static
|
||||
typing, like Java. Dependency injection framework can
|
||||
Originally, dependency injection pattern got popular in languages with static
|
||||
typing, like Java. Dependency injection framework can
|
||||
significantly improve flexibility of the language with static typing. Also,
|
||||
implementation of dependency injection framework for language with static
|
||||
typing is not something that one can do shortly, it could be quite complex
|
||||
implementation of dependency injection framework for language with static
|
||||
typing is not something that one can do shortly, it could be quite complex
|
||||
thing to be done well.
|
||||
|
||||
While Python is very flexible interpreted language with dynamic typing, there
|
||||
is a meaning that dependency injection doesn't work for it as well, as it does
|
||||
for Java. Also there is a meaning that dependency injection framework is
|
||||
While Python is very flexible interpreted language with dynamic typing, there
|
||||
is a meaning that dependency injection doesn't work for it as well, as it does
|
||||
for Java. Also there is a meaning that dependency injection framework is
|
||||
something that Python developer would not ever need, cause dependency injection
|
||||
could be implemented easily using language fundamentals.
|
||||
|
||||
Discussion
|
||||
~~~~~~~~~~
|
||||
|
||||
It is true.
|
||||
It is true.
|
||||
|
||||
Partly.
|
||||
|
||||
Dependency injection, as a software design pattern, has number of
|
||||
Dependency injection, as a software design pattern, has number of
|
||||
advantages that are common for each language (including Python):
|
||||
|
||||
+ Dependency Injection decreases coupling between a class and its dependency.
|
||||
+ Because dependency injection doesn't require any change in code behavior it
|
||||
can be applied to legacy code as a refactoring. The result is clients that
|
||||
are more independent and that are easier to unit test in isolation using
|
||||
stubs or mock objects that simulate other objects not under test. This ease
|
||||
of testing is often the first benefit noticed when using dependency
|
||||
+ Because dependency injection doesn't require any change in code behavior it
|
||||
can be applied to legacy code as a refactoring. The result is clients that
|
||||
are more independent and that are easier to unit test in isolation using
|
||||
stubs or mock objects that simulate other objects not under test. This ease
|
||||
of testing is often the first benefit noticed when using dependency
|
||||
injection.
|
||||
+ Dependency injection can be used to externalize a system's configuration
|
||||
details into configuration files allowing the system to be reconfigured
|
||||
without recompilation (rebuilding). Separate configurations can be written
|
||||
for different situations that require different implementations of
|
||||
+ Dependency injection can be used to externalize a system's configuration
|
||||
details into configuration files allowing the system to be reconfigured
|
||||
without recompilation (rebuilding). Separate configurations can be written
|
||||
for different situations that require different implementations of
|
||||
components. This includes, but is not limited to, testing.
|
||||
+ Reduction of boilerplate code in the application objects since all work to
|
||||
+ Reduction of boilerplate code in the application objects since all work to
|
||||
initialize or set up dependencies is handled by a provider component.
|
||||
+ Dependency injection allows a client to remove all knowledge of a concrete
|
||||
implementation that it needs to use. This helps isolate the client from the
|
||||
impact of design changes and defects. It promotes reusability, testability
|
||||
+ Dependency injection allows a client to remove all knowledge of a concrete
|
||||
implementation that it needs to use. This helps isolate the client from the
|
||||
impact of design changes and defects. It promotes reusability, testability
|
||||
and maintainability.
|
||||
+ Dependency injection allows a client the flexibility of being configurable.
|
||||
Only the client's behavior is fixed. The client may act on anything that
|
||||
+ Dependency injection allows a client the flexibility of being configurable.
|
||||
Only the client's behavior is fixed. The client may act on anything that
|
||||
supports the intrinsic interface the client expects.
|
||||
|
||||
.. note::
|
||||
|
||||
While improved testability is one the first benefits of using dependency
|
||||
injection, it could be easily overwhelmed by monkey-patching technique,
|
||||
that works absolutely great in Python (you can monkey-patch anything,
|
||||
anytime). At the same time, monkey-patching has nothing similar with
|
||||
other advantages defined above. Also monkey-patching technique is
|
||||
While improved testability is one the first benefits of using dependency
|
||||
injection, it could be easily overwhelmed by monkey-patching technique,
|
||||
that works absolutely great in Python (you can monkey-patch anything,
|
||||
anytime). At the same time, monkey-patching has nothing similar with
|
||||
other advantages defined above. Also monkey-patching technique is
|
||||
something that could be considered like too dirty to be used in production.
|
||||
|
||||
The complexity of dependency injection pattern implementation in Python is
|
||||
definitely quite lower than in other languages (even with dynamic typing).
|
||||
The complexity of dependency injection pattern implementation in Python is
|
||||
definitely quite lower than in other languages (even with dynamic typing).
|
||||
|
||||
.. note::
|
||||
.. note::
|
||||
|
||||
Low complexity of dependency injection pattern implementation in Python
|
||||
still means that some code should be written, reviewed, tested and
|
||||
Low complexity of dependency injection pattern implementation in Python
|
||||
still means that some code should be written, reviewed, tested and
|
||||
supported.
|
||||
|
||||
Talking about inversion of control, it is a software design principle that
|
||||
Talking about inversion of control, it is a software design principle that
|
||||
also works for each programming language, not depending on its typing type.
|
||||
|
||||
Inversion of control is used to increase modularity of the program and make
|
||||
Inversion of control is used to increase modularity of the program and make
|
||||
it extensible.
|
||||
|
||||
Main design purposes of using inversion of control are:
|
||||
|
||||
+ To decouple the execution of a task from implementation.
|
||||
+ To focus a module on the task it is designed for.
|
||||
+ To free modules from assumptions about how other systems do what they do and
|
||||
+ To free modules from assumptions about how other systems do what they do and
|
||||
instead rely on contracts.
|
||||
+ To prevent side effects when replacing a module.
|
||||
|
||||
|
@ -115,9 +115,9 @@ Next example demonstrates creation of several cars with different engines:
|
|||
:language: python
|
||||
|
||||
While previous example demonstrates advantages of dependency injection, there
|
||||
is a disadvantage demonstration as well - creation of car requires additional
|
||||
code for specification of dependencies. Nevertheless, this disadvantage could
|
||||
be easily avoided by using a dependency injection framework for creation of
|
||||
is a disadvantage demonstration as well - creation of car requires additional
|
||||
code for specification of dependencies. Nevertheless, this disadvantage could
|
||||
be easily avoided by using a dependency injection framework for creation of
|
||||
inversion of control container (IoC container).
|
||||
|
||||
Example of creation of several inversion of control containers (IoC containers)
|
||||
|
@ -131,18 +131,21 @@ What's next?
|
|||
|
||||
Choose one of the following as a next step:
|
||||
|
||||
+ Pass one of the dependency injection tutorials:
|
||||
+ :ref:`flask-tutorial`
|
||||
+ :ref:`aiohttp-tutorial`
|
||||
+ :ref:`asyncio-daemon-tutorial`
|
||||
+ :ref:`cli-tutorial`
|
||||
+ Know more about the :ref:`providers`
|
||||
+ Go to the :ref:`contents`
|
||||
- Look at application examples:
|
||||
- :ref:`application-single-container`
|
||||
- :ref:`application-multiple-containers`
|
||||
- Pass the tutorials:
|
||||
- :ref:`flask-tutorial`
|
||||
- :ref:`aiohttp-tutorial`
|
||||
- :ref:`asyncio-daemon-tutorial`
|
||||
- :ref:`cli-tutorial`
|
||||
- Know more about the :ref:`providers`
|
||||
- Go to the :ref:`contents`
|
||||
|
||||
Useful links
|
||||
~~~~~~~~~~~~
|
||||
|
||||
There are some useful links related to dependency injection design pattern
|
||||
There are some useful links related to dependency injection design pattern
|
||||
that could be used for further reading:
|
||||
|
||||
+ https://en.wikipedia.org/wiki/Dependency_injection
|
||||
|
|
|
@ -17,3 +17,4 @@ dependency injection pattern, inversion of control principle and
|
|||
what_is_di
|
||||
di_in_python
|
||||
key_features
|
||||
installation
|
||||
|
|
|
@ -31,7 +31,7 @@ Verification of currently installed version could be done using
|
|||
|
||||
>>> import dependency_injector
|
||||
>>> dependency_injector.__version__
|
||||
'3.15.2'
|
||||
'3.38.0'
|
||||
|
||||
.. _PyPi: https://pypi.org/project/dependency-injector/
|
||||
.. _GitHub: https://github.com/ets-labs/python-dependency-injector
|
|
@ -175,13 +175,16 @@ What's next?
|
|||
|
||||
Choose one of the following as a next step:
|
||||
|
||||
+ Pass one of the tutorials:
|
||||
+ :ref:`cli-tutorial`
|
||||
+ :ref:`flask-tutorial`
|
||||
+ :ref:`aiohttp-tutorial`
|
||||
+ :ref:`asyncio-daemon-tutorial`
|
||||
+ Know more about the :ref:`providers`
|
||||
+ Go to the :ref:`contents`
|
||||
- Look at application examples:
|
||||
- :ref:`application-single-container`
|
||||
- :ref:`application-multiple-containers`
|
||||
- Pass the tutorials:
|
||||
- :ref:`flask-tutorial`
|
||||
- :ref:`aiohttp-tutorial`
|
||||
- :ref:`asyncio-daemon-tutorial`
|
||||
- :ref:`cli-tutorial`
|
||||
- Know more about the :ref:`providers`
|
||||
- Go to the :ref:`contents`
|
||||
|
||||
.. disqus::
|
||||
|
||||
|
|
29
examples/miniapps/application-multiple-containers/README.rst
Normal file
29
examples/miniapps/application-multiple-containers/README.rst
Normal file
|
@ -0,0 +1,29 @@
|
|||
Application example (multiple containers)
|
||||
=========================================
|
||||
|
||||
Create virtual env:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 -m venv venv
|
||||
. venv/bin/activate
|
||||
|
||||
Install requirements:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
Run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python -m example user@example.com secret photo.jpg
|
||||
|
||||
You should see:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
[2020-09-04 16:06:00,750] [DEBUG] [example.services.UserService]: User user@example.com has been found in database
|
||||
[2020-09-04 16:06:00,750] [DEBUG] [example.services.AuthService]: User user@example.com has been successfully authenticated
|
||||
[2020-09-04 16:06:00,750] [DEBUG] [example.services.PhotoService]: Photo photo.jpg has been successfully uploaded by user user@example.com
|
30
examples/miniapps/application-multiple-containers/config.yml
Normal file
30
examples/miniapps/application-multiple-containers/config.yml
Normal file
|
@ -0,0 +1,30 @@
|
|||
core:
|
||||
|
||||
logging:
|
||||
version: 1
|
||||
formatters:
|
||||
formatter:
|
||||
format: "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s"
|
||||
handlers:
|
||||
console:
|
||||
class: "logging.StreamHandler"
|
||||
level: "DEBUG"
|
||||
formatter: "formatter"
|
||||
stream: "ext://sys.stderr"
|
||||
root:
|
||||
level: "DEBUG"
|
||||
handlers: ["console"]
|
||||
|
||||
gateways:
|
||||
|
||||
database:
|
||||
dsn: ":memory:"
|
||||
|
||||
aws:
|
||||
access_key_id: "KEY"
|
||||
secret_access_key: "SECRET"
|
||||
|
||||
services:
|
||||
|
||||
auth:
|
||||
token_ttl: 3600
|
|
@ -0,0 +1,24 @@
|
|||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from .containers import Application
|
||||
|
||||
|
||||
def main(email: str, password: str, photo: str) -> None:
|
||||
application = Application()
|
||||
|
||||
application.config.from_yaml('config.yml')
|
||||
application.core.configure_logging()
|
||||
|
||||
user_service = application.services.user()
|
||||
auth_service = application.services.auth()
|
||||
photo_service = application.services.photo()
|
||||
|
||||
user = user_service.get_user(email)
|
||||
auth_service.authenticate(user, password)
|
||||
photo_service.upload_photo(user, photo)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(*sys.argv[1:])
|
|
@ -0,0 +1,80 @@
|
|||
"""Containers module."""
|
||||
|
||||
import logging.config
|
||||
import sqlite3
|
||||
|
||||
import boto3
|
||||
from dependency_injector import containers, providers
|
||||
|
||||
from . import services
|
||||
|
||||
|
||||
class Core(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration()
|
||||
|
||||
configure_logging = providers.Callable(
|
||||
logging.config.dictConfig,
|
||||
config=config.logging,
|
||||
)
|
||||
|
||||
|
||||
class Gateways(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration()
|
||||
|
||||
database_client = providers.Singleton(
|
||||
sqlite3.connect,
|
||||
config.database.dsn,
|
||||
)
|
||||
|
||||
s3_client = providers.Singleton(
|
||||
boto3.client,
|
||||
service_name='s3',
|
||||
aws_access_key_id=config.aws.access_key_id,
|
||||
aws_secret_access_key=config.aws.secret_access_key,
|
||||
)
|
||||
|
||||
|
||||
class Services(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration()
|
||||
gateways = providers.DependenciesContainer()
|
||||
|
||||
user = providers.Factory(
|
||||
services.UserService,
|
||||
db=gateways.database_client,
|
||||
)
|
||||
|
||||
auth = providers.Factory(
|
||||
services.AuthService,
|
||||
db=gateways.database_client,
|
||||
token_ttl=config.auth.token_ttl.as_int(),
|
||||
)
|
||||
|
||||
photo = providers.Factory(
|
||||
services.PhotoService,
|
||||
db=gateways.database_client,
|
||||
s3=gateways.s3_client,
|
||||
)
|
||||
|
||||
|
||||
class Application(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration()
|
||||
|
||||
core = providers.Container(
|
||||
Core,
|
||||
config=config.core,
|
||||
)
|
||||
|
||||
gateways = providers.Container(
|
||||
Gateways,
|
||||
config=config.gateways,
|
||||
)
|
||||
|
||||
services = providers.Container(
|
||||
Services,
|
||||
config=config.services,
|
||||
gateways=gateways,
|
||||
)
|
|
@ -0,0 +1,56 @@
|
|||
"""Services module."""
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Dict
|
||||
|
||||
from mypy_boto3_s3 import S3Client
|
||||
|
||||
|
||||
class BaseService:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.logger = logging.getLogger(
|
||||
f'{__name__}.{self.__class__.__name__}',
|
||||
)
|
||||
|
||||
|
||||
class UserService(BaseService):
|
||||
|
||||
def __init__(self, db: sqlite3.Connection) -> None:
|
||||
self.db = db
|
||||
super().__init__()
|
||||
|
||||
def get_user(self, email: str) -> Dict[str, str]:
|
||||
self.logger.debug('User %s has been found in database', email)
|
||||
return {'email': email, 'password_hash': '...'}
|
||||
|
||||
|
||||
class AuthService(BaseService):
|
||||
|
||||
def __init__(self, db: sqlite3.Connection, token_ttl: int) -> None:
|
||||
self.db = db
|
||||
self.token_ttl = token_ttl
|
||||
super().__init__()
|
||||
|
||||
def authenticate(self, user: Dict[str, str], password: str) -> None:
|
||||
assert password is not None
|
||||
self.logger.debug(
|
||||
'User %s has been successfully authenticated',
|
||||
user['email'],
|
||||
)
|
||||
|
||||
|
||||
class PhotoService(BaseService):
|
||||
|
||||
def __init__(self, db: sqlite3.Connection, s3: S3Client) -> None:
|
||||
self.db = db
|
||||
self.s3 = s3
|
||||
super().__init__()
|
||||
|
||||
def upload_photo(self, user: Dict[str, str], photo_path: str) -> None:
|
||||
self.logger.debug(
|
||||
'Photo %s has been successfully uploaded by user %s',
|
||||
photo_path,
|
||||
user['email'],
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
dependency-injector[yaml]
|
||||
boto3
|
||||
boto3-stubs[s3]
|
29
examples/miniapps/application-single-container/README.rst
Normal file
29
examples/miniapps/application-single-container/README.rst
Normal file
|
@ -0,0 +1,29 @@
|
|||
Application example (single container)
|
||||
======================================
|
||||
|
||||
Create virtual env:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 -m venv venv
|
||||
. venv/bin/activate
|
||||
|
||||
Install requirements:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
Run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python -m example user@example.com secret photo.jpg
|
||||
|
||||
You should see:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
[2020-09-04 15:27:27,727] [DEBUG] [example.services.UserService]: User user@example.com has been found in database
|
||||
[2020-09-04 15:27:27,727] [DEBUG] [example.services.AuthService]: User user@example.com has been successfully authenticated
|
||||
[2020-09-04 15:27:27,727] [DEBUG] [example.services.PhotoService]: Photo photo.jpg has been successfully uploaded by user user@example.com
|
|
@ -0,0 +1,9 @@
|
|||
[database]
|
||||
dsn=:memory:
|
||||
|
||||
[aws]
|
||||
access_key_id=KEY
|
||||
secret_access_key=SECRET
|
||||
|
||||
[auth]
|
||||
token_ttl=3600
|
|
@ -0,0 +1,24 @@
|
|||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from .containers import Container
|
||||
|
||||
|
||||
def main(email: str, password: str, photo: str) -> None:
|
||||
container = Container()
|
||||
|
||||
container.configure_logging()
|
||||
container.config.from_ini('config.ini')
|
||||
|
||||
user_service = container.user_service()
|
||||
auth_service = container.auth_service()
|
||||
photo_service = container.photo_service()
|
||||
|
||||
user = user_service.get_user(email)
|
||||
auth_service.authenticate(user, password)
|
||||
photo_service.upload_photo(user, photo)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(*sys.argv[1:])
|
|
@ -0,0 +1,52 @@
|
|||
"""Containers module."""
|
||||
|
||||
import logging.config
|
||||
import sqlite3
|
||||
|
||||
import boto3
|
||||
from dependency_injector import containers, providers
|
||||
|
||||
from . import services
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration()
|
||||
|
||||
configure_logging = providers.Callable(
|
||||
logging.config.fileConfig,
|
||||
fname='logging.ini',
|
||||
)
|
||||
|
||||
# Gateways
|
||||
|
||||
database_client = providers.Singleton(
|
||||
sqlite3.connect,
|
||||
config.database.dsn,
|
||||
)
|
||||
|
||||
s3_client = providers.Singleton(
|
||||
boto3.client,
|
||||
service_name='s3',
|
||||
aws_access_key_id=config.aws.access_key_id,
|
||||
aws_secret_access_key=config.aws.secret_access_key,
|
||||
)
|
||||
|
||||
# Services
|
||||
|
||||
user_service = providers.Factory(
|
||||
services.UserService,
|
||||
db=database_client,
|
||||
)
|
||||
|
||||
auth_service = providers.Factory(
|
||||
services.AuthService,
|
||||
db=database_client,
|
||||
token_ttl=config.auth.token_ttl.as_int(),
|
||||
)
|
||||
|
||||
photo_service = providers.Factory(
|
||||
services.PhotoService,
|
||||
db=database_client,
|
||||
s3=s3_client,
|
||||
)
|
|
@ -0,0 +1,56 @@
|
|||
"""Services module."""
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
from typing import Dict
|
||||
|
||||
from mypy_boto3_s3 import S3Client
|
||||
|
||||
|
||||
class BaseService:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.logger = logging.getLogger(
|
||||
f'{__name__}.{self.__class__.__name__}',
|
||||
)
|
||||
|
||||
|
||||
class UserService(BaseService):
|
||||
|
||||
def __init__(self, db: sqlite3.Connection) -> None:
|
||||
self.db = db
|
||||
super().__init__()
|
||||
|
||||
def get_user(self, email: str) -> Dict[str, str]:
|
||||
self.logger.debug('User %s has been found in database', email)
|
||||
return {'email': email, 'password_hash': '...'}
|
||||
|
||||
|
||||
class AuthService(BaseService):
|
||||
|
||||
def __init__(self, db: sqlite3.Connection, token_ttl: int) -> None:
|
||||
self.db = db
|
||||
self.token_ttl = token_ttl
|
||||
super().__init__()
|
||||
|
||||
def authenticate(self, user: Dict[str, str], password: str) -> None:
|
||||
assert password is not None
|
||||
self.logger.debug(
|
||||
'User %s has been successfully authenticated',
|
||||
user['email'],
|
||||
)
|
||||
|
||||
|
||||
class PhotoService(BaseService):
|
||||
|
||||
def __init__(self, db: sqlite3.Connection, s3: S3Client) -> None:
|
||||
self.db = db
|
||||
self.s3 = s3
|
||||
super().__init__()
|
||||
|
||||
def upload_photo(self, user: Dict[str, str], photo_path: str) -> None:
|
||||
self.logger.debug(
|
||||
'Photo %s has been successfully uploaded by user %s',
|
||||
photo_path,
|
||||
user['email'],
|
||||
)
|
21
examples/miniapps/application-single-container/logging.ini
Normal file
21
examples/miniapps/application-single-container/logging.ini
Normal file
|
@ -0,0 +1,21 @@
|
|||
[loggers]
|
||||
keys=root
|
||||
|
||||
[handlers]
|
||||
keys=stream_handler
|
||||
|
||||
[formatters]
|
||||
keys=formatter
|
||||
|
||||
[logger_root]
|
||||
level=DEBUG
|
||||
handlers=stream_handler
|
||||
|
||||
[handler_stream_handler]
|
||||
class=StreamHandler
|
||||
level=DEBUG
|
||||
formatter=formatter
|
||||
args=(sys.stderr,)
|
||||
|
||||
[formatter_formatter]
|
||||
format=[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s
|
|
@ -0,0 +1,3 @@
|
|||
dependency-injector
|
||||
boto3
|
||||
boto3-stubs[s3]
|
|
@ -1,8 +0,0 @@
|
|||
Dependency Injector IoC containers example
|
||||
==========================================
|
||||
|
||||
Instructions for running
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py 1 secret photo.jpg
|
|
@ -1,58 +0,0 @@
|
|||
"""Example of dependency injection in Python."""
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
|
||||
import boto3
|
||||
|
||||
import example.main
|
||||
import example.services
|
||||
|
||||
import dependency_injector.containers as containers
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
|
||||
class Core(containers.DeclarativeContainer):
|
||||
"""IoC container of core component providers."""
|
||||
|
||||
config = providers.Configuration('config')
|
||||
|
||||
logger = providers.Singleton(logging.Logger, name='example')
|
||||
|
||||
|
||||
class Gateways(containers.DeclarativeContainer):
|
||||
"""IoC container of gateway (API clients to remote services) providers."""
|
||||
|
||||
database = providers.Singleton(sqlite3.connect, Core.config.database.dsn)
|
||||
|
||||
s3 = providers.Singleton(
|
||||
boto3.client, 's3',
|
||||
aws_access_key_id=Core.config.aws.access_key_id,
|
||||
aws_secret_access_key=Core.config.aws.secret_access_key)
|
||||
|
||||
|
||||
class Services(containers.DeclarativeContainer):
|
||||
"""IoC container of business service providers."""
|
||||
|
||||
users = providers.Factory(example.services.UsersService,
|
||||
db=Gateways.database,
|
||||
logger=Core.logger)
|
||||
|
||||
auth = providers.Factory(example.services.AuthService,
|
||||
db=Gateways.database,
|
||||
logger=Core.logger,
|
||||
token_ttl=Core.config.auth.token_ttl)
|
||||
|
||||
photos = providers.Factory(example.services.PhotosService,
|
||||
db=Gateways.database,
|
||||
s3=Gateways.s3,
|
||||
logger=Core.logger)
|
||||
|
||||
|
||||
class Application(containers.DeclarativeContainer):
|
||||
"""IoC container of application component providers."""
|
||||
|
||||
main = providers.Callable(example.main.main,
|
||||
users_service=Services.users,
|
||||
auth_service=Services.auth,
|
||||
photos_service=Services.photos)
|
|
@ -1,8 +0,0 @@
|
|||
"""Example main module."""
|
||||
|
||||
|
||||
def main(uid, password, photo, users_service, auth_service, photos_service):
|
||||
"""Authenticate user and upload photo."""
|
||||
user = users_service.get_user_by_id(uid)
|
||||
auth_service.authenticate(user, password)
|
||||
photos_service.upload_photo(user['uid'], photo)
|
|
@ -1,50 +0,0 @@
|
|||
"""Example business services module."""
|
||||
|
||||
|
||||
class BaseService:
|
||||
"""Service base class."""
|
||||
|
||||
|
||||
class UsersService(BaseService):
|
||||
"""Users service."""
|
||||
|
||||
def __init__(self, logger, db):
|
||||
"""Initialize instance."""
|
||||
self.logger = logger
|
||||
self.db = db
|
||||
|
||||
def get_user_by_id(self, uid):
|
||||
"""Return user's data by identifier."""
|
||||
self.logger.debug('User %s has been found in database', uid)
|
||||
return dict(uid=uid, password_hash='secret_hash')
|
||||
|
||||
|
||||
class AuthService(BaseService):
|
||||
"""Authentication service."""
|
||||
|
||||
def __init__(self, logger, db, token_ttl):
|
||||
"""Initialize instance."""
|
||||
self.logger = logger
|
||||
self.db = db
|
||||
self.token_ttl = token_ttl
|
||||
|
||||
def authenticate(self, user, password):
|
||||
"""Authenticate user."""
|
||||
assert user['password_hash'] == '_'.join((password, 'hash'))
|
||||
self.logger.debug('User %s has been successfully authenticated',
|
||||
user['uid'])
|
||||
|
||||
|
||||
class PhotosService(BaseService):
|
||||
"""Photos service."""
|
||||
|
||||
def __init__(self, logger, db, s3):
|
||||
"""Initialize instance."""
|
||||
self.logger = logger
|
||||
self.db = db
|
||||
self.s3 = s3
|
||||
|
||||
def upload_photo(self, uid, photo_path):
|
||||
"""Upload user photo."""
|
||||
self.logger.debug('Photo %s has been successfully uploaded by user %s',
|
||||
photo_path, uid)
|
|
@ -1,20 +0,0 @@
|
|||
"""Run example application."""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from containers import Core, Application
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Configure platform:
|
||||
Core.config.override({'database': {'dsn': ':memory:'},
|
||||
'aws': {'access_key_id': 'KEY',
|
||||
'secret_access_key': 'SECRET'},
|
||||
'auth': {'token_ttl': 3600}})
|
||||
Core.logger().addHandler(logging.StreamHandler(sys.stdout))
|
||||
|
||||
# Run application:
|
||||
Application.main(uid=sys.argv[1],
|
||||
password=sys.argv[2],
|
||||
photo=sys.argv[3])
|
|
@ -1,8 +0,0 @@
|
|||
Dependency Injector IoC containers example
|
||||
==========================================
|
||||
|
||||
Instructions for running
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py 1 secret photo.jpg
|
|
@ -1,57 +0,0 @@
|
|||
"""Example of dependency injection in Python."""
|
||||
|
||||
import logging
|
||||
import sqlite3
|
||||
|
||||
import boto3
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from example import services, main
|
||||
|
||||
|
||||
class IocContainer(containers.DeclarativeContainer):
|
||||
"""Application IoC container."""
|
||||
|
||||
config = providers.Configuration('config')
|
||||
logger = providers.Singleton(logging.Logger, name='example')
|
||||
|
||||
# Gateways
|
||||
|
||||
database_client = providers.Singleton(sqlite3.connect, config.database.dsn)
|
||||
|
||||
s3_client = providers.Singleton(
|
||||
boto3.client, 's3',
|
||||
aws_access_key_id=config.aws.access_key_id,
|
||||
aws_secret_access_key=config.aws.secret_access_key,
|
||||
)
|
||||
|
||||
# Services
|
||||
|
||||
users_service = providers.Factory(
|
||||
services.UsersService,
|
||||
db=database_client,
|
||||
logger=logger,
|
||||
)
|
||||
|
||||
auth_service = providers.Factory(
|
||||
services.AuthService,
|
||||
token_ttl=config.auth.token_ttl,
|
||||
db=database_client,
|
||||
logger=logger,
|
||||
)
|
||||
|
||||
photos_service = providers.Factory(
|
||||
services.PhotosService,
|
||||
db=database_client,
|
||||
s3=s3_client,
|
||||
logger=logger,
|
||||
)
|
||||
|
||||
# Misc
|
||||
|
||||
main = providers.Callable(
|
||||
main.main,
|
||||
users_service=users_service,
|
||||
auth_service=auth_service,
|
||||
photos_service=photos_service,
|
||||
)
|
|
@ -1,8 +0,0 @@
|
|||
"""Example main module."""
|
||||
|
||||
|
||||
def main(uid, password, photo, users_service, auth_service, photos_service):
|
||||
"""Authenticate user and upload photo."""
|
||||
user = users_service.get_user_by_id(uid)
|
||||
auth_service.authenticate(user, password)
|
||||
photos_service.upload_photo(user['uid'], photo)
|
|
@ -1,50 +0,0 @@
|
|||
"""Example business services module."""
|
||||
|
||||
|
||||
class BaseService:
|
||||
"""Service base class."""
|
||||
|
||||
|
||||
class UsersService(BaseService):
|
||||
"""Users service."""
|
||||
|
||||
def __init__(self, logger, db):
|
||||
"""Initialize instance."""
|
||||
self.logger = logger
|
||||
self.db = db
|
||||
|
||||
def get_user_by_id(self, uid):
|
||||
"""Return user's data by identifier."""
|
||||
self.logger.debug('User %s has been found in database', uid)
|
||||
return dict(uid=uid, password_hash='secret_hash')
|
||||
|
||||
|
||||
class AuthService(BaseService):
|
||||
"""Authentication service."""
|
||||
|
||||
def __init__(self, logger, db, token_ttl):
|
||||
"""Initialize instance."""
|
||||
self.logger = logger
|
||||
self.db = db
|
||||
self.token_ttl = token_ttl
|
||||
|
||||
def authenticate(self, user, password):
|
||||
"""Authenticate user."""
|
||||
assert user['password_hash'] == '_'.join((password, 'hash'))
|
||||
self.logger.debug('User %s has been successfully authenticated',
|
||||
user['uid'])
|
||||
|
||||
|
||||
class PhotosService(BaseService):
|
||||
"""Photos service."""
|
||||
|
||||
def __init__(self, logger, db, s3):
|
||||
"""Initialize instance."""
|
||||
self.logger = logger
|
||||
self.db = db
|
||||
self.s3 = s3
|
||||
|
||||
def upload_photo(self, uid, photo_path):
|
||||
"""Upload user photo."""
|
||||
self.logger.debug('Photo %s has been successfully uploaded by user %s',
|
||||
photo_path, uid)
|
|
@ -1,28 +0,0 @@
|
|||
"""Run example of dependency injection in Python."""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from container import IocContainer
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Configure container:
|
||||
container = IocContainer(
|
||||
config={
|
||||
'database': {
|
||||
'dsn': ':memory:',
|
||||
},
|
||||
'aws': {
|
||||
'access_key_id': 'KEY',
|
||||
'secret_access_key': 'SECRET',
|
||||
},
|
||||
'auth': {
|
||||
'token_ttl': 3600,
|
||||
},
|
||||
}
|
||||
)
|
||||
container.logger().addHandler(logging.StreamHandler(sys.stdout))
|
||||
|
||||
# Run application:
|
||||
container.main(*sys.argv[1:])
|
Loading…
Reference in New Issue
Block a user