Merge branch 'release/3.39.0' into master

This commit is contained in:
Roman Mogylatov 2020-09-04 23:23:16 -04:00
commit 06bc0f1bac
45 changed files with 704 additions and 520 deletions

View 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

View 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::

View 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::

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -2,23 +2,15 @@ Examples
======== ========
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Example
:description: Current section of documentation is designed to provide :description: Python dependency injection examples.
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 Explore the examples to see the ``Dependency Injector`` in action.
applications that are built according to the inversion of control principle
and powered by *Dependency Injector* framework.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
services_miniapp_v1 application-single-container
services_miniapp_v2 application-multiple-containers
bundles_miniapp
use_cases_miniapp .. disqus::
password_hashing_miniapp
chained_factories
factory_of_factories

View File

@ -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::

View File

@ -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

View File

@ -129,11 +129,11 @@ Contents
:maxdepth: 2 :maxdepth: 2
introduction/index introduction/index
main/installation examples/index
tutorials/index tutorials/index
providers/index providers/index
containers/index containers/index
examples/index examples-other/index
api/index api/index
main/feedback main/feedback
main/changelog main/changelog

View File

@ -3,90 +3,90 @@ Dependency injection and inversion of control in Python
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control :keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes benefits of dependency injection and :description: This article describes benefits of dependency injection and
inversion of control for Python applications. Also it inversion of control for Python applications. Also it
contains some Python examples that show how dependency contains some Python examples that show how dependency
injection and inversion could be implemented. In addition, it injection and inversion could be implemented. In addition, it
demonstrates usage of dependency injection framework, demonstrates usage of dependency injection framework,
IoC container and such popular design pattern as Factory. IoC container and such popular design pattern as Factory.
History History
~~~~~~~ ~~~~~~~
Originally, dependency injection pattern got popular in languages with static Originally, dependency injection pattern got popular in languages with static
typing, like Java. Dependency injection framework can typing, like Java. Dependency injection framework can
significantly improve flexibility of the language with static typing. Also, significantly improve flexibility of the language with static typing. Also,
implementation of dependency injection framework for language with static implementation of dependency injection framework for language with static
typing is not something that one can do shortly, it could be quite complex typing is not something that one can do shortly, it could be quite complex
thing to be done well. thing to be done well.
While Python is very flexible interpreted language with dynamic typing, there 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 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 for Java. Also there is a meaning that dependency injection framework is
something that Python developer would not ever need, cause dependency injection something that Python developer would not ever need, cause dependency injection
could be implemented easily using language fundamentals. could be implemented easily using language fundamentals.
Discussion Discussion
~~~~~~~~~~ ~~~~~~~~~~
It is true. It is true.
Partly. 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): advantages that are common for each language (including Python):
+ Dependency Injection decreases coupling between a class and its dependency. + Dependency Injection decreases coupling between a class and its dependency.
+ Because dependency injection doesn't require any change in code behavior it + 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 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 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 stubs or mock objects that simulate other objects not under test. This ease
of testing is often the first benefit noticed when using dependency of testing is often the first benefit noticed when using dependency
injection. injection.
+ Dependency injection can be used to externalize a system's configuration + Dependency injection can be used to externalize a system's configuration
details into configuration files allowing the system to be reconfigured details into configuration files allowing the system to be reconfigured
without recompilation (rebuilding). Separate configurations can be written without recompilation (rebuilding). Separate configurations can be written
for different situations that require different implementations of for different situations that require different implementations of
components. This includes, but is not limited to, testing. 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. initialize or set up dependencies is handled by a provider component.
+ Dependency injection allows a client to remove all knowledge of a concrete + 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 implementation that it needs to use. This helps isolate the client from the
impact of design changes and defects. It promotes reusability, testability impact of design changes and defects. It promotes reusability, testability
and maintainability. and maintainability.
+ Dependency injection allows a client the flexibility of being configurable. + Dependency injection allows a client the flexibility of being configurable.
Only the client's behavior is fixed. The client may act on anything that Only the client's behavior is fixed. The client may act on anything that
supports the intrinsic interface the client expects. supports the intrinsic interface the client expects.
.. note:: .. note::
While improved testability is one the first benefits of using dependency While improved testability is one the first benefits of using dependency
injection, it could be easily overwhelmed by monkey-patching technique, injection, it could be easily overwhelmed by monkey-patching technique,
that works absolutely great in Python (you can monkey-patch anything, that works absolutely great in Python (you can monkey-patch anything,
anytime). At the same time, monkey-patching has nothing similar with anytime). At the same time, monkey-patching has nothing similar with
other advantages defined above. Also monkey-patching technique is other advantages defined above. Also monkey-patching technique is
something that could be considered like too dirty to be used in production. something that could be considered like too dirty to be used in production.
The complexity of dependency injection pattern implementation in Python is The complexity of dependency injection pattern implementation in Python is
definitely quite lower than in other languages (even with dynamic typing). definitely quite lower than in other languages (even with dynamic typing).
.. note:: .. note::
Low complexity of dependency injection pattern implementation in Python Low complexity of dependency injection pattern implementation in Python
still means that some code should be written, reviewed, tested and still means that some code should be written, reviewed, tested and
supported. 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. 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. it extensible.
Main design purposes of using inversion of control are: Main design purposes of using inversion of control are:
+ To decouple the execution of a task from implementation. + To decouple the execution of a task from implementation.
+ To focus a module on the task it is designed for. + 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. instead rely on contracts.
+ To prevent side effects when replacing a module. + To prevent side effects when replacing a module.
@ -115,9 +115,9 @@ Next example demonstrates creation of several cars with different engines:
:language: python :language: python
While previous example demonstrates advantages of dependency injection, there While previous example demonstrates advantages of dependency injection, there
is a disadvantage demonstration as well - creation of car requires additional is a disadvantage demonstration as well - creation of car requires additional
code for specification of dependencies. Nevertheless, this disadvantage could code for specification of dependencies. Nevertheless, this disadvantage could
be easily avoided by using a dependency injection framework for creation of be easily avoided by using a dependency injection framework for creation of
inversion of control container (IoC container). inversion of control container (IoC container).
Example of creation of several inversion of control containers (IoC containers) 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: Choose one of the following as a next step:
+ Pass one of the dependency injection tutorials: - Look at application examples:
+ :ref:`flask-tutorial` - :ref:`application-single-container`
+ :ref:`aiohttp-tutorial` - :ref:`application-multiple-containers`
+ :ref:`asyncio-daemon-tutorial` - Pass the tutorials:
+ :ref:`cli-tutorial` - :ref:`flask-tutorial`
+ Know more about the :ref:`providers` - :ref:`aiohttp-tutorial`
+ Go to the :ref:`contents` - :ref:`asyncio-daemon-tutorial`
- :ref:`cli-tutorial`
- Know more about the :ref:`providers`
- Go to the :ref:`contents`
Useful links 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: that could be used for further reading:
+ https://en.wikipedia.org/wiki/Dependency_injection + https://en.wikipedia.org/wiki/Dependency_injection

View File

@ -17,3 +17,4 @@ dependency injection pattern, inversion of control principle and
what_is_di what_is_di
di_in_python di_in_python
key_features key_features
installation

View File

@ -31,7 +31,7 @@ Verification of currently installed version could be done using
>>> import dependency_injector >>> import dependency_injector
>>> dependency_injector.__version__ >>> dependency_injector.__version__
'3.15.2' '3.38.0'
.. _PyPi: https://pypi.org/project/dependency-injector/ .. _PyPi: https://pypi.org/project/dependency-injector/
.. _GitHub: https://github.com/ets-labs/python-dependency-injector .. _GitHub: https://github.com/ets-labs/python-dependency-injector

View File

@ -175,13 +175,16 @@ What's next?
Choose one of the following as a next step: Choose one of the following as a next step:
+ Pass one of the tutorials: - Look at application examples:
+ :ref:`cli-tutorial` - :ref:`application-single-container`
+ :ref:`flask-tutorial` - :ref:`application-multiple-containers`
+ :ref:`aiohttp-tutorial` - Pass the tutorials:
+ :ref:`asyncio-daemon-tutorial` - :ref:`flask-tutorial`
+ Know more about the :ref:`providers` - :ref:`aiohttp-tutorial`
+ Go to the :ref:`contents` - :ref:`asyncio-daemon-tutorial`
- :ref:`cli-tutorial`
- Know more about the :ref:`providers`
- Go to the :ref:`contents`
.. disqus:: .. disqus::

View File

@ -7,6 +7,14 @@ 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.39.0
------
- Add application examples with single and multiple containers.
- Remove "Services" application examples.
- Split examples page into "Examples" with main examples and "Other Examples" with secondary
examples.
- Move "Installation" page to "Introduction" section.
3.38.1 3.38.1
------ ------
- Fix README. - Fix README.

View 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

View 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

View File

@ -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:])

View File

@ -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,
)

View File

@ -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'],
)

View File

@ -0,0 +1,3 @@
dependency-injector[yaml]
boto3
boto3-stubs[s3]

View 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

View File

@ -0,0 +1,9 @@
[database]
dsn=:memory:
[aws]
access_key_id=KEY
secret_access_key=SECRET
[auth]
token_ttl=3600

View File

@ -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:])

View File

@ -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,
)

View File

@ -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'],
)

View 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

View File

@ -0,0 +1,3 @@
dependency-injector
boto3
boto3-stubs[s3]

View File

@ -1,8 +0,0 @@
Dependency Injector IoC containers example
==========================================
Instructions for running
.. code-block:: bash
python run.py 1 secret photo.jpg

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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])

View File

@ -1,8 +0,0 @@
Dependency Injector IoC containers example
==========================================
Instructions for running
.. code-block:: bash
python run.py 1 secret photo.jpg

View File

@ -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,
)

View File

@ -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)

View File

@ -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)

View File

@ -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:])

View File

@ -1,6 +1,6 @@
"""Dependency injector top-level package.""" """Dependency injector top-level package."""
__version__ = '3.38.1' __version__ = '3.39.0'
"""Version number that follows semantic versioning. """Version number that follows semantic versioning.
:type: str :type: str