mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-29 13:04:02 +03:00
Merge branch 'release/3.41.0' into master
This commit is contained in:
commit
31f15c0a13
68
docs/examples-other/chained-factories.rst
Normal file
68
docs/examples-other/chained-factories.rst
Normal file
|
@ -0,0 +1,68 @@
|
|||
Chained Factories pattern
|
||||
=========================
|
||||
|
||||
This example demonstrates "Chained Factories" pattern.
|
||||
|
||||
The idea of the pattern is in wrapping ``Factory`` into another ``Factory`` that adds
|
||||
additional arguments.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
base_factory = providers.Factory(
|
||||
SomeClass,
|
||||
base_argument=1,
|
||||
)
|
||||
|
||||
concrete_factory = providers.Factory(
|
||||
base_factory,
|
||||
extra_argument=2,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
instance = concrete_factory()
|
||||
# Same as: # instance = SomeClass(base_argument=1, extra_argument=2)
|
||||
|
||||
Sample code
|
||||
-----------
|
||||
|
||||
Listing of the pattern example:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/factory-patterns/chained_factories.py
|
||||
:language: python
|
||||
|
||||
Arguments priority
|
||||
------------------
|
||||
|
||||
Passing of the arguments works the same way like for any other :ref:`factory-provider`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 1. Keyword arguments of upper level factory are added to lower level factory
|
||||
chained_dict_factory = providers.Factory(
|
||||
providers.Factory(dict, arg1=1),
|
||||
arg2=2,
|
||||
)
|
||||
print(chained_dict_factory()) # prints: {'arg1': 1, 'arg2': 2}
|
||||
|
||||
# 2. Keyword arguments of upper level factory have priority
|
||||
chained_dict_factory = providers.Factory(
|
||||
providers.Factory(dict, arg1=1),
|
||||
arg1=2,
|
||||
)
|
||||
print(chained_dict_factory()) # prints: {'arg1': 2}
|
||||
|
||||
# 3. Keyword arguments provided from context have the most priority
|
||||
chained_dict_factory = providers.Factory(
|
||||
providers.Factory(dict, arg1=1),
|
||||
arg1=2,
|
||||
)
|
||||
print(chained_dict_factory(arg1=3)) # prints: {'arg1': 3}
|
||||
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
The "Chained Factories" pattern was suggested by the ``Dependency Injector`` users.
|
||||
|
||||
.. disqus::
|
|
@ -1,21 +0,0 @@
|
|||
Chained Factories pattern
|
||||
=========================
|
||||
|
||||
This example demonstrate implementation of "Chained Factories" pattern.
|
||||
Main idea of this pattern is about wrapping :py:class:`Factory` into
|
||||
another :py:class:`Factory` that mix additional arguments or keyword
|
||||
arguments to a wrapped one.
|
||||
|
||||
Listing of ``data.py``, demonstrates sample classes structure:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/factory_patterns/data.py
|
||||
:language: python
|
||||
|
||||
Listing of ``chained_factories.py``, demonstrates "Chained Factories"
|
||||
pattern and provide some explanation:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/factory_patterns/chained_factories.py
|
||||
:language: python
|
||||
|
||||
|
||||
.. disqus::
|
74
docs/examples-other/factory-of-factories.rst
Normal file
74
docs/examples-other/factory-of-factories.rst
Normal file
|
@ -0,0 +1,74 @@
|
|||
Factory of Factories pattern
|
||||
============================
|
||||
|
||||
This example demonstrates "Factory of Factories" pattern.
|
||||
|
||||
The idea of the pattern is in creating a ``Factory`` that creates another ``Factory`` and adds
|
||||
additional arguments.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
base_factory = providers.Factory(
|
||||
providers.Factory
|
||||
SomeClass,
|
||||
base_argument=1,
|
||||
)
|
||||
|
||||
concrete_factory = providers.Factory(
|
||||
OtherClass,
|
||||
instance=base_factory(extra_argument=1),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
instance = concrete_factory()
|
||||
# Same as: # instance = SomeClass(base_argument=1, extra_argument=2)
|
||||
|
||||
Sample code
|
||||
-----------
|
||||
|
||||
Listing of the pattern example:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/factory-patterns/factory_of_factories.py
|
||||
:language: python
|
||||
|
||||
Arguments priority
|
||||
------------------
|
||||
|
||||
Passing of the arguments works the same way like for any other :ref:`factory-provider`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 1. Keyword arguments of upper level factory are added to lower level factory
|
||||
factory_of_dict_factories = providers.Factory(
|
||||
providers.Factory,
|
||||
dict,
|
||||
arg1=1,
|
||||
)
|
||||
dict_factory = factory_of_dict_factories(arg2=2)
|
||||
print(dict_factory()) # prints: {'arg1': 1, 'arg2': 2}
|
||||
|
||||
# 2. Keyword arguments of upper level factory have priority
|
||||
factory_of_dict_factories = providers.Factory(
|
||||
providers.Factory,
|
||||
dict,
|
||||
arg1=1,
|
||||
)
|
||||
dict_factory = factory_of_dict_factories(arg1=2)
|
||||
print(dict_factory()) # prints: {'arg1': 2}
|
||||
|
||||
# 3. Keyword arguments provided from context have the most priority
|
||||
factory_of_dict_factories = providers.Factory(
|
||||
providers.Factory,
|
||||
dict,
|
||||
arg1=1,
|
||||
)
|
||||
dict_factory = factory_of_dict_factories(arg1=2)
|
||||
print(dict_factory(arg1=3)) # prints: {'arg1': 3}
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
The "Factory of Factories" pattern was suggested by the ``Dependency Injector`` users.
|
||||
|
||||
.. disqus::
|
|
@ -1,20 +0,0 @@
|
|||
Factory of Factories pattern
|
||||
============================
|
||||
|
||||
This example demonstrate implementation of "Factory of Factories" pattern.
|
||||
Main idea of this pattern is about creation of a :py:class:`Factory` that
|
||||
creates another :py:class:`Factory` and mix additional arguments to it.
|
||||
|
||||
Listing of ``data.py``, demonstrates sample classes structure:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/factory_patterns/data.py
|
||||
:language: python
|
||||
|
||||
Listing of ``factory_of_factories.py``, demonstrates "Chained Factories"
|
||||
pattern and provide some explanation:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/factory_patterns/factory_of_factories.py
|
||||
:language: python
|
||||
|
||||
|
||||
.. disqus::
|
|
@ -2,18 +2,16 @@ 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.
|
||||
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
|
||||
Framework
|
||||
:description: This sections contains assorted Dependency Injector examples.
|
||||
|
||||
This sections contains assorted ``Dependency Injector`` examples.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
use_cases_miniapp
|
||||
password_hashing_miniapp
|
||||
chained_factories
|
||||
factory_of_factories
|
||||
use-cases
|
||||
password-hashing
|
||||
chained-factories
|
||||
factory-of-factories
|
||||
|
|
30
docs/examples-other/password-hashing.rst
Normal file
30
docs/examples-other/password-hashing.rst
Normal file
|
@ -0,0 +1,30 @@
|
|||
Password hashing example
|
||||
========================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
|
||||
Framework,Callable
|
||||
:description: This example demonstrates a usage of the Callable provider.
|
||||
|
||||
This example demonstrates an injection of the ``Callable`` provider.
|
||||
|
||||
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/password-hashing>`_.
|
||||
|
||||
Sample code
|
||||
-----------
|
||||
|
||||
Listing of the pattern example:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/password-hashing/example.py
|
||||
:language: python
|
||||
|
||||
Run the example
|
||||
---------------
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python example.py
|
||||
|
||||
.. disqus::
|
|
@ -1,18 +0,0 @@
|
|||
Dependency injection and password hashing in Python
|
||||
===================================================
|
||||
|
||||
Small example that demonstrates using of dependency injection for user
|
||||
password hashing.
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python example.py
|
||||
|
||||
Listing of ``example.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/password_hashing/example.py
|
||||
:language: python
|
||||
|
||||
.. disqus::
|
74
docs/examples-other/use-cases.rst
Normal file
74
docs/examples-other/use-cases.rst
Normal file
|
@ -0,0 +1,74 @@
|
|||
Use cases example
|
||||
=================
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,Dependency Injection,Inversion of Control,Container,Example,Application,
|
||||
Framework,DependenciesContainer
|
||||
:description: This example demonstrates a usage of the DependenciesContainer provider.
|
||||
|
||||
This example demonstrates a usage of the ``DependenciesContainer`` provider.
|
||||
|
||||
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/decoupled-packages>`_.
|
||||
|
||||
Application structure
|
||||
---------------------
|
||||
|
||||
Example application has next structure:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
||||
./
|
||||
└── example/
|
||||
├── __init__.py
|
||||
├── __main__.py
|
||||
├── adapters.py
|
||||
├── containers.py
|
||||
└── usecases.py
|
||||
|
||||
Containers
|
||||
----------
|
||||
|
||||
Listing of the ``example/containers.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/use-cases/example/containers.py
|
||||
:language: python
|
||||
|
||||
Main module
|
||||
-----------
|
||||
|
||||
Listing of the ``example/__main__.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/use-cases/example/__main__.py
|
||||
:language: python
|
||||
|
||||
|
||||
Run the application
|
||||
-------------------
|
||||
|
||||
Instructions for running in the "test" mode:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py test example@example.com
|
||||
|
||||
Instructions for running in the "prod" mode:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py prod example@example.com
|
||||
|
||||
Adapters and use cases
|
||||
----------------------
|
||||
|
||||
Listing of the ``example/adapters.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/use-cases/example/adapters.py
|
||||
:language: python
|
||||
|
||||
Listing of the ``example/usecases.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/use-cases/example/usecases.py
|
||||
:language: python
|
||||
|
||||
.. disqus::
|
|
@ -1,55 +0,0 @@
|
|||
Use cases mini application example
|
||||
----------------------------------
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
"Use cases" miniapp demonstrate usage of :py:class:`DependenciesContainer`
|
||||
provider.
|
||||
|
||||
Example application
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
"Use cases" mini application has next structure:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
use_cases/
|
||||
example/ <-- Example package
|
||||
__init__.py
|
||||
adapters.py
|
||||
use_cases.py
|
||||
containers.py <-- Dependency injection containers
|
||||
run.py <-- Entrypoint
|
||||
|
||||
|
||||
IoC containers
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``use_cases/containers.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/use_cases/containers.py
|
||||
:language: python
|
||||
|
||||
Run application
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Listing of ``run.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/miniapps/use_cases/run.py
|
||||
:language: python
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py prod example@example.com # Running in "production" environment
|
||||
python run.py test example@example.com # Running in "testing" environment
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
|
||||
+ `Dependency Injector <https://github.com/ets-labs/python-dependency-injector/>`_
|
||||
+ `Full example sources <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/use_cases>`_
|
||||
|
||||
|
||||
.. disqus::
|
|
@ -1,3 +1,5 @@
|
|||
.. _decoupled-packages:
|
||||
|
||||
Decoupled packages example (multiple containers)
|
||||
================================================
|
||||
|
||||
|
|
|
@ -134,6 +134,7 @@ Choose one of the following as a next step:
|
|||
- Look at application examples:
|
||||
- :ref:`application-single-container`
|
||||
- :ref:`application-multiple-containers`
|
||||
- :ref:`decoupled-packages`
|
||||
- Pass the tutorials:
|
||||
- :ref:`flask-tutorial`
|
||||
- :ref:`aiohttp-tutorial`
|
||||
|
|
|
@ -178,6 +178,7 @@ Choose one of the following as a next step:
|
|||
- Look at application examples:
|
||||
- :ref:`application-single-container`
|
||||
- :ref:`application-multiple-containers`
|
||||
- :ref:`decoupled-packages`
|
||||
- Pass the tutorials:
|
||||
- :ref:`flask-tutorial`
|
||||
- :ref:`aiohttp-tutorial`
|
||||
|
|
|
@ -7,6 +7,17 @@ that were made in every particular version.
|
|||
From version 0.7.6 *Dependency Injector* framework strictly
|
||||
follows `Semantic versioning`_
|
||||
|
||||
3.41.0
|
||||
------
|
||||
- Refactor "use cases" example.
|
||||
- Refactor "password hashing" example.
|
||||
- Refactor "chained factories" pattern example.
|
||||
- Refactor "factory of factories" pattern example.
|
||||
- Fix declarative container mypy stub to ``__init__`` to accept not only providers.
|
||||
- Refactor main module of the "decoupled packages" example.
|
||||
- Delete "api client" example mini app.
|
||||
- Delete "mail service" example mini app.
|
||||
|
||||
3.40.0
|
||||
------
|
||||
- Add "Decoupled packages" example.
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
"""API client module."""
|
||||
|
||||
|
||||
class ApiClient:
|
||||
"""Some API client."""
|
||||
|
||||
def __init__(self, host, api_key):
|
||||
"""Initialize instance."""
|
||||
self.host = host
|
||||
self.api_key = api_key
|
||||
|
||||
def call(self, operation, data):
|
||||
"""Make some network operations."""
|
||||
print('API call [{0}:{1}], method - {2}, data - {3}'.format(
|
||||
self.host, self.api_key, operation, repr(data)))
|
|
@ -1,36 +0,0 @@
|
|||
"""Main module."""
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
import api
|
||||
import models
|
||||
|
||||
|
||||
# Creating ApiClient and User providers:
|
||||
api_client = providers.Singleton(api.ApiClient,
|
||||
host='production.com',
|
||||
api_key='PROD_API_KEY')
|
||||
user_factory = providers.Factory(models.User,
|
||||
api_client=api_client)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Creating several users and register them:
|
||||
user1 = user_factory(1)
|
||||
user1.register()
|
||||
# API call [production.com:PROD_API_KEY], method - register, data -
|
||||
# {'id': 1}
|
||||
|
||||
user2 = user_factory(2)
|
||||
user2.register()
|
||||
# API call [production.com:PROD_API_KEY], method - register, data -
|
||||
# {'id': 2}
|
||||
|
||||
# Overriding of ApiClient on dev environment:
|
||||
api_client.override(providers.Singleton(api.ApiClient,
|
||||
host='localhost',
|
||||
api_key='DEV_API_KEY'))
|
||||
|
||||
user3 = user_factory(3)
|
||||
user3.register()
|
||||
# API call [localhost:DEV_API_KEY], method - register, data - {'id': 3}
|
|
@ -1,14 +0,0 @@
|
|||
"""Models module."""
|
||||
|
||||
|
||||
class User:
|
||||
"""User model."""
|
||||
|
||||
def __init__(self, id, api_client):
|
||||
"""Initialize instance."""
|
||||
self.id = id
|
||||
self.api_client = api_client
|
||||
|
||||
def register(self):
|
||||
"""Register user."""
|
||||
self.api_client.call('register', {'id': self.id})
|
|
@ -1,12 +0,0 @@
|
|||
"""Tests module."""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import main
|
||||
import api
|
||||
|
||||
# Mock ApiClient for testing:
|
||||
with main.api_client.override(Mock(api.ApiClient)) as api_client_mock:
|
||||
user = main.user_factory('test')
|
||||
user.register()
|
||||
api_client_mock().call.assert_called_with('register', {'id': 'test'})
|
|
@ -3,7 +3,7 @@
|
|||
from .containers import ApplicationContainer
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def main() -> None:
|
||||
application = ApplicationContainer()
|
||||
application.config.from_ini('config.ini')
|
||||
|
||||
|
@ -22,3 +22,7 @@ if __name__ == '__main__':
|
|||
assert aggregation_service.user_repository is user_repository
|
||||
assert aggregation_service.photo_repository is photo_repository
|
||||
print('Aggregate analytics from user and photo packages')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
73
examples/miniapps/factory-patterns/chained_factories.py
Normal file
73
examples/miniapps/factory-patterns/chained_factories.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
"""`Chained Factories` pattern."""
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
|
||||
|
||||
class SqlAlchemyDatabaseService:
|
||||
|
||||
def __init__(self, session, base_class):
|
||||
self.session = session
|
||||
self.base_class = base_class
|
||||
|
||||
|
||||
class TokensService:
|
||||
|
||||
def __init__(self, id_generator, database):
|
||||
self.id_generator = id_generator
|
||||
self.database = database
|
||||
|
||||
|
||||
class Token:
|
||||
...
|
||||
|
||||
|
||||
class UsersService:
|
||||
|
||||
def __init__(self, id_generator, database):
|
||||
self.id_generator = id_generator
|
||||
self.database = database
|
||||
|
||||
|
||||
class User:
|
||||
...
|
||||
|
||||
|
||||
# Sample objects
|
||||
session = object()
|
||||
id_generator = object()
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
database = providers.Factory(
|
||||
SqlAlchemyDatabaseService,
|
||||
session=session,
|
||||
)
|
||||
|
||||
token_service = providers.Factory(
|
||||
TokensService,
|
||||
id_generator=id_generator,
|
||||
database=providers.Factory(
|
||||
database,
|
||||
base_class=Token,
|
||||
),
|
||||
)
|
||||
|
||||
user_service = providers.Factory(
|
||||
UsersService,
|
||||
id_generator=id_generator,
|
||||
database=providers.Factory(
|
||||
database,
|
||||
base_class=User,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
container = Container()
|
||||
|
||||
token_service = container.token_service()
|
||||
assert token_service.database.base_class is Token
|
||||
|
||||
user_service = container.user_service()
|
||||
assert user_service.database.base_class is User
|
68
examples/miniapps/factory-patterns/factory_of_factories.py
Normal file
68
examples/miniapps/factory-patterns/factory_of_factories.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
"""`Factory of Factories` pattern."""
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
|
||||
|
||||
class SqlAlchemyDatabaseService:
|
||||
|
||||
def __init__(self, session, base_class):
|
||||
self.session = session
|
||||
self.base_class = base_class
|
||||
|
||||
|
||||
class TokensService:
|
||||
|
||||
def __init__(self, id_generator, database):
|
||||
self.id_generator = id_generator
|
||||
self.database = database
|
||||
|
||||
|
||||
class Token:
|
||||
...
|
||||
|
||||
|
||||
class UsersService:
|
||||
|
||||
def __init__(self, id_generator, database):
|
||||
self.id_generator = id_generator
|
||||
self.database = database
|
||||
|
||||
|
||||
class User:
|
||||
...
|
||||
|
||||
|
||||
# Sample objects
|
||||
session = object()
|
||||
id_generator = object()
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
database_factory = providers.Factory(
|
||||
providers.Factory,
|
||||
SqlAlchemyDatabaseService,
|
||||
session=session,
|
||||
)
|
||||
|
||||
token_service = providers.Factory(
|
||||
TokensService,
|
||||
id_generator=id_generator,
|
||||
database=database_factory(base_class=Token),
|
||||
)
|
||||
|
||||
user_service = providers.Factory(
|
||||
UsersService,
|
||||
id_generator=id_generator,
|
||||
database=database_factory(base_class=User),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
container = Container()
|
||||
|
||||
token_service = container.token_service()
|
||||
assert token_service.database.base_class is Token
|
||||
|
||||
user_service = container.user_service()
|
||||
assert user_service.database.base_class is User
|
|
@ -1,59 +0,0 @@
|
|||
"""`Chained Factories` pattern."""
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
from data import (
|
||||
id_generator,
|
||||
session,
|
||||
SqlAlchemyDatabaseService,
|
||||
TokensService,
|
||||
Token,
|
||||
UsersService,
|
||||
User,
|
||||
)
|
||||
|
||||
|
||||
# "Chained Factories" pattern
|
||||
|
||||
database = providers.Factory(SqlAlchemyDatabaseService, session=session)
|
||||
|
||||
tokens = providers.Factory(
|
||||
TokensService,
|
||||
id_generator=id_generator,
|
||||
database=providers.Factory(database, base_class=Token),
|
||||
)
|
||||
|
||||
users = providers.Factory(
|
||||
UsersService,
|
||||
id_generator=id_generator,
|
||||
database=providers.Factory(database, base_class=User),
|
||||
)
|
||||
|
||||
tokens_service = tokens()
|
||||
assert tokens_service.database.base_class is Token
|
||||
|
||||
users_service = users()
|
||||
assert users_service.database.base_class is User
|
||||
|
||||
# Explanation & some more examples
|
||||
|
||||
# 1. Keyword arguments of upper level factory are added to lower level factory
|
||||
chained_dict_factory = providers.Factory(
|
||||
providers.Factory(dict, arg1=1),
|
||||
arg2=2,
|
||||
)
|
||||
print(chained_dict_factory()) # prints: {'arg1': 1, 'arg2': 2}
|
||||
|
||||
# 2. Keyword arguments of upper level factory have priority
|
||||
chained_dict_factory = providers.Factory(
|
||||
providers.Factory(dict, arg1=1),
|
||||
arg1=2,
|
||||
)
|
||||
print(chained_dict_factory()) # prints: {'arg1': 2}
|
||||
|
||||
# 3. Keyword arguments provided from context have most priority
|
||||
chained_dict_factory = providers.Factory(
|
||||
providers.Factory(dict, arg1=1),
|
||||
arg1=2,
|
||||
)
|
||||
print(chained_dict_factory(arg1=3)) # prints: {'arg1': 3}
|
|
@ -1,41 +0,0 @@
|
|||
"""Sample data classes."""
|
||||
|
||||
|
||||
class SqlAlchemyDatabaseService:
|
||||
"""Database service of an entity."""
|
||||
|
||||
def __init__(self, session, base_class):
|
||||
"""Initialize object."""
|
||||
self.session = session
|
||||
self.base_class = base_class
|
||||
|
||||
|
||||
class TokensService:
|
||||
"""Tokens service."""
|
||||
|
||||
def __init__(self, id_generator, database):
|
||||
"""Initialize object."""
|
||||
self.id_generator = id_generator
|
||||
self.database = database
|
||||
|
||||
|
||||
class Token:
|
||||
"""Token entity."""
|
||||
|
||||
|
||||
class UsersService:
|
||||
"""Users service."""
|
||||
|
||||
def __init__(self, id_generator, database):
|
||||
"""Initialize object."""
|
||||
self.id_generator = id_generator
|
||||
self.database = database
|
||||
|
||||
|
||||
class User:
|
||||
"""User entity."""
|
||||
|
||||
|
||||
# Sample objects
|
||||
session = object()
|
||||
id_generator = object()
|
|
@ -1,69 +0,0 @@
|
|||
"""`Factory of Factories` pattern."""
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
from data import (
|
||||
id_generator,
|
||||
session,
|
||||
SqlAlchemyDatabaseService,
|
||||
TokensService,
|
||||
Token,
|
||||
UsersService,
|
||||
User,
|
||||
)
|
||||
|
||||
|
||||
# "Factory of Factories" pattern
|
||||
|
||||
database_factory = providers.Factory(
|
||||
providers.Factory,
|
||||
SqlAlchemyDatabaseService,
|
||||
session=session,
|
||||
)
|
||||
|
||||
tokens = providers.Factory(
|
||||
TokensService,
|
||||
id_generator=id_generator,
|
||||
database=database_factory(base_class=Token),
|
||||
)
|
||||
|
||||
users = providers.Factory(
|
||||
UsersService,
|
||||
id_generator=id_generator,
|
||||
database=database_factory(base_class=User),
|
||||
)
|
||||
|
||||
tokens_service = tokens()
|
||||
assert tokens_service.database.base_class is Token
|
||||
|
||||
users_service = users()
|
||||
assert users_service.database.base_class is User
|
||||
|
||||
# Explanation & some more examples
|
||||
|
||||
# 1. Keyword arguments of upper level factory are added to lower level factory
|
||||
factory_of_dict_factories = providers.Factory(
|
||||
providers.Factory,
|
||||
dict,
|
||||
arg1=1,
|
||||
)
|
||||
dict_factory = factory_of_dict_factories(arg2=2)
|
||||
print(dict_factory()) # prints: {'arg1': 1, 'arg2': 2}
|
||||
|
||||
# 2. Keyword arguments of upper level factory have priority
|
||||
factory_of_dict_factories = providers.Factory(
|
||||
providers.Factory,
|
||||
dict,
|
||||
arg1=1,
|
||||
)
|
||||
dict_factory = factory_of_dict_factories(arg1=2)
|
||||
print(dict_factory()) # prints: {'arg1': 2}
|
||||
|
||||
# 3. Keyword arguments provided from context have most priority
|
||||
factory_of_dict_factories = providers.Factory(
|
||||
providers.Factory,
|
||||
dict,
|
||||
arg1=1,
|
||||
)
|
||||
dict_factory = factory_of_dict_factories(arg1=2)
|
||||
print(dict_factory(arg1=3)) # prints: {'arg1': 3}
|
|
@ -1,41 +0,0 @@
|
|||
"""Mail service and user registration DI container example."""
|
||||
|
||||
from dependency_injector.containers import DeclarativeContainer
|
||||
from dependency_injector.providers import Callable, Singleton
|
||||
|
||||
import example
|
||||
|
||||
|
||||
class Container(DeclarativeContainer):
|
||||
"""DI container."""
|
||||
|
||||
mail_service = Singleton(example.MailService,
|
||||
host='localhost',
|
||||
port=587,
|
||||
login='my_login',
|
||||
password='super_secret_password')
|
||||
|
||||
add_user = Callable(example.add_user,
|
||||
mailer=mail_service)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('Using real mail service:')
|
||||
Container.add_user('sample@mail.com', 'password')
|
||||
# Using real mail service:
|
||||
# Connecting server localhost:587 with my_login:super_secret_password
|
||||
# Sending "Your password is password" to "sample@mail.com"
|
||||
|
||||
print('Using mail service stub:')
|
||||
Container.add_user('sample@mail.com', 'password',
|
||||
mailer=example.MailServiceStub())
|
||||
# Using mail service stub:
|
||||
# Emulating sending "Your password is password" to "sample@mail.com"
|
||||
|
||||
# Also you can override provider by another provider:
|
||||
Container.mail_service.override(Singleton(example.MailServiceStub))
|
||||
print('Using mail service stub by overriding mail service provider:')
|
||||
Container.add_user('sample@mail.com', 'password')
|
||||
# Using mail service stub by overriding mail service provider:
|
||||
# Emulating sending "Your password is password" to "sample@mail.com"
|
||||
Container.mail_service.reset_override() # Resetting provider overriding
|
|
@ -1,39 +0,0 @@
|
|||
"""Mail service and user registration example."""
|
||||
|
||||
|
||||
class AbstractMailService:
|
||||
"""Abstract mail service."""
|
||||
|
||||
def send(self, email, body):
|
||||
"""Send email."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class MailService(AbstractMailService):
|
||||
"""Mail service."""
|
||||
|
||||
def __init__(self, host, port, login, password):
|
||||
"""Initialize instance."""
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._login = login
|
||||
self._password = password
|
||||
|
||||
def send(self, email, body):
|
||||
"""Send email."""
|
||||
print('Connecting server {0}:{1} with {2}:{3}'.format(
|
||||
self._host, self._port, self._login, self._password))
|
||||
print('Sending "{0}" to "{1}"'.format(body, email))
|
||||
|
||||
|
||||
class MailServiceStub(AbstractMailService):
|
||||
"""Mail service stub."""
|
||||
|
||||
def send(self, email, body):
|
||||
"""Send email."""
|
||||
print('Emulating sending "{0}" to "{1}"'.format(body, email))
|
||||
|
||||
|
||||
def add_user(email, password, mailer):
|
||||
"""Register user."""
|
||||
mailer.send(email, 'Your password is {0}'.format(password))
|
10
examples/miniapps/password-hashing/README.rst
Normal file
10
examples/miniapps/password-hashing/README.rst
Normal file
|
@ -0,0 +1,10 @@
|
|||
Password hashing
|
||||
================
|
||||
|
||||
This example demonstrates an injection of the ``Callable`` provider.
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python example.py
|
50
examples/miniapps/password-hashing/example.py
Normal file
50
examples/miniapps/password-hashing/example.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
"""Password hashing example."""
|
||||
|
||||
from typing import Callable, Dict
|
||||
|
||||
import passlib.hash
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
|
||||
|
||||
class UserService:
|
||||
|
||||
def __init__(self, password_hasher: Callable[[str], str]) -> None:
|
||||
self._password_hasher = password_hasher
|
||||
|
||||
def create_user(self, name: str, password: str) -> Dict[str, str]:
|
||||
hashed_password = self._password_hasher(password)
|
||||
return {
|
||||
'name': name,
|
||||
'password': hashed_password,
|
||||
}
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration()
|
||||
|
||||
password_hasher = providers.Callable(
|
||||
passlib.hash.sha256_crypt.hash,
|
||||
salt_size=config.salt_size,
|
||||
rounds=config.rounds,
|
||||
)
|
||||
|
||||
user_service = providers.Factory(
|
||||
UserService,
|
||||
password_hasher=password_hasher.provider,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
container = Container(
|
||||
config={
|
||||
'salt_size': 16,
|
||||
'rounds': 10000,
|
||||
},
|
||||
)
|
||||
|
||||
user_service = container.user_service()
|
||||
|
||||
user = user_service.create_user(name='Roman', password='secret1')
|
||||
print(user)
|
|
@ -1,12 +0,0 @@
|
|||
Dependency injection and password hashing in Python
|
||||
===================================================
|
||||
|
||||
Small example that demonstrates using of dependency injection for user
|
||||
password hashing.
|
||||
|
||||
|
||||
instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python example.py
|
|
@ -1,42 +0,0 @@
|
|||
"""Example of dependency injection and password hashing in Python."""
|
||||
|
||||
import passlib.hash
|
||||
|
||||
import dependency_injector.containers as containers
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
|
||||
class UsersService:
|
||||
"""Users service."""
|
||||
|
||||
def __init__(self, password_hasher):
|
||||
"""Initialize instance."""
|
||||
self._password_hasher = password_hasher
|
||||
|
||||
def create_user(self, name, password):
|
||||
"""Create user with hashed password."""
|
||||
hashed_password = self._password_hasher(password)
|
||||
return dict(name=name, password=hashed_password)
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
"""Inversion of control container."""
|
||||
|
||||
password_hasher = providers.Callable(
|
||||
passlib.hash.sha256_crypt.hash,
|
||||
salt_size=16,
|
||||
rounds=10000)
|
||||
|
||||
users_service = providers.Factory(
|
||||
UsersService,
|
||||
password_hasher=password_hasher.provider)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
container = Container()
|
||||
users_service = container.users_service()
|
||||
|
||||
user1 = users_service.create_user(name='Roman', password='secret1')
|
||||
user2 = users_service.create_user(name='Vitaly', password='secret2')
|
||||
|
||||
print(user1, user2)
|
11
examples/miniapps/use-cases/README.rst
Normal file
11
examples/miniapps/use-cases/README.rst
Normal file
|
@ -0,0 +1,11 @@
|
|||
Use cases
|
||||
=========
|
||||
|
||||
This example demonstrates a usage of the ``DependenciesContainer`` provider.
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python -m example prod example@example.com
|
||||
python -m example test example@example.com
|
1
examples/miniapps/use-cases/example/__init__.py
Normal file
1
examples/miniapps/use-cases/example/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Top-level package."""
|
|
@ -1,19 +1,23 @@
|
|||
"""Run 'Use Cases' example application."""
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from containers import Adapters, TestAdapters, UseCases
|
||||
from .containers import UseCases, Adapters, TestAdapters
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
environment, email = sys.argv[1:]
|
||||
|
||||
def main(environment: str, email: str) -> None:
|
||||
if environment == 'prod':
|
||||
adapters = Adapters()
|
||||
elif environment == 'test':
|
||||
adapters = TestAdapters()
|
||||
else:
|
||||
raise RuntimeError('Unknown environment')
|
||||
|
||||
use_cases = UseCases(adapters=adapters)
|
||||
|
||||
use_case = use_cases.signup()
|
||||
use_case.execute(email)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(*sys.argv[1:])
|
22
examples/miniapps/use-cases/example/adapters.py
Normal file
22
examples/miniapps/use-cases/example/adapters.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
"""Adapters module."""
|
||||
|
||||
import abc
|
||||
|
||||
|
||||
class EmailSender(metaclass=abc.ABCMeta):
|
||||
|
||||
@abc.abstractmethod
|
||||
def send(self, to: str, body: str) -> None:
|
||||
...
|
||||
|
||||
|
||||
class SmtpEmailSender:
|
||||
|
||||
def send(self, to: str, body: str) -> None:
|
||||
print(f'Sending an email to {to} over SMTP, body="{body}"')
|
||||
|
||||
|
||||
class EchoEmailSender:
|
||||
|
||||
def send(self, to: str, body: str) -> None:
|
||||
print(f'Fake sending an email to {to}, body="{body}"')
|
25
examples/miniapps/use-cases/example/containers.py
Normal file
25
examples/miniapps/use-cases/example/containers.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
"""Containers module."""
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
|
||||
from . import adapters, usecases
|
||||
|
||||
|
||||
class Adapters(containers.DeclarativeContainer):
|
||||
|
||||
email_sender = providers.Singleton(adapters.SmtpEmailSender)
|
||||
|
||||
|
||||
class TestAdapters(containers.DeclarativeContainer):
|
||||
|
||||
email_sender = providers.Singleton(adapters.EchoEmailSender)
|
||||
|
||||
|
||||
class UseCases(containers.DeclarativeContainer):
|
||||
|
||||
adapters = providers.DependenciesContainer()
|
||||
|
||||
signup = providers.Factory(
|
||||
usecases.SignupUseCase,
|
||||
email_sender=adapters.email_sender,
|
||||
)
|
22
examples/miniapps/use-cases/example/usecases.py
Normal file
22
examples/miniapps/use-cases/example/usecases.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
"""Use cases module."""
|
||||
|
||||
import abc
|
||||
|
||||
from .adapters import EmailSender
|
||||
|
||||
|
||||
class UseCase(metaclass=abc.ABCMeta):
|
||||
|
||||
@abc.abstractmethod
|
||||
def execute(self) -> None:
|
||||
...
|
||||
|
||||
|
||||
class SignupUseCase:
|
||||
|
||||
def __init__(self, email_sender: EmailSender) -> None:
|
||||
self.email_sender = email_sender
|
||||
|
||||
def execute(self, email: str) -> None:
|
||||
print(f'Sign up user {email}')
|
||||
self.email_sender.send(email, f'Welcome, {email}')
|
|
@ -1,9 +0,0 @@
|
|||
Dependency Injector Use Cases example
|
||||
=====================================
|
||||
|
||||
Instructions for running
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py prod example@example.com # Running in "production" environment
|
||||
python run.py test example@example.com # Running in "testing" environment
|
|
@ -1,30 +0,0 @@
|
|||
"""Dependency injection containers for 'Use Cases' example application."""
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
|
||||
from example.adapters import SmtpEmailSender, EchoEmailSender
|
||||
from example.use_cases import SignupUseCase
|
||||
|
||||
|
||||
class Adapters(containers.DeclarativeContainer):
|
||||
"""Adapters container."""
|
||||
|
||||
email_sender = providers.Singleton(SmtpEmailSender)
|
||||
|
||||
|
||||
class TestAdapters(containers.DeclarativeContainer):
|
||||
"""Adapters container.
|
||||
|
||||
This container is used for testing purposes.
|
||||
"""
|
||||
|
||||
email_sender = providers.Singleton(EchoEmailSender)
|
||||
|
||||
|
||||
class UseCases(containers.DeclarativeContainer):
|
||||
"""Use cases container."""
|
||||
|
||||
adapters = providers.DependenciesContainer()
|
||||
|
||||
signup = providers.Factory(SignupUseCase,
|
||||
email_sender=adapters.email_sender)
|
|
@ -1 +0,0 @@
|
|||
"""Example top-level package."""
|
|
@ -1,25 +0,0 @@
|
|||
"""Example adapters package."""
|
||||
|
||||
|
||||
class EmailSender:
|
||||
"""Abstract email sender."""
|
||||
|
||||
def send(self, to, body):
|
||||
"""Send email to specified email."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class SmtpEmailSender:
|
||||
"""SMTP email sender uses SMTP protocol for sending emails."""
|
||||
|
||||
def send(self, to, body):
|
||||
"""Send email to specified email."""
|
||||
# Send email via SMTP
|
||||
|
||||
|
||||
class EchoEmailSender:
|
||||
"""Echo email sender prints emails to stdout."""
|
||||
|
||||
def send(self, to, body):
|
||||
"""Send email to specified email."""
|
||||
print('Sending email to "{0}", body = "{1}"'.format(to, body))
|
|
@ -1,22 +0,0 @@
|
|||
"""Example use cases package."""
|
||||
|
||||
|
||||
class UseCase:
|
||||
"""Abstract use case."""
|
||||
|
||||
def execute(self):
|
||||
"""Execute use case handling."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class SignupUseCase:
|
||||
"""Sign up use cases registers users."""
|
||||
|
||||
def __init__(self, email_sender):
|
||||
"""Initialize instance."""
|
||||
self.email_sender = email_sender
|
||||
|
||||
def execute(self, email):
|
||||
"""Execute use case handling."""
|
||||
print('Sign up user {0}'.format(email))
|
||||
self.email_sender.send(email, 'Welcome, "{}"'.format(email))
|
|
@ -1,6 +1,6 @@
|
|||
"""Dependency injector top-level package."""
|
||||
|
||||
__version__ = '3.40.0'
|
||||
__version__ = '3.41.0'
|
||||
"""Version number that follows semantic versioning.
|
||||
|
||||
:type: str
|
||||
|
|
|
@ -24,7 +24,7 @@ class DynamicContainer(Container): ...
|
|||
class DeclarativeContainer(Container):
|
||||
cls_providers: ClassVar[Dict[str, Provider]]
|
||||
inherited_providers: ClassVar[Dict[str, Provider]]
|
||||
def __init__(self, **overriding_providers: Provider) -> None: ...
|
||||
def __init__(self, **overriding_providers: Union[Provider, Any]) -> None: ...
|
||||
|
||||
|
||||
def override(container: Container) -> _Callable[[Container], Container]: ...
|
||||
|
|
Loading…
Reference in New Issue
Block a user