Merge branch 'release/3.41.0' into master

This commit is contained in:
Roman Mogylatov 2020-09-07 22:07:22 -04:00
commit 31f15c0a13
42 changed files with 566 additions and 598 deletions

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

View File

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

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

View File

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

View File

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

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

View File

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

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

View File

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

View File

@ -1,3 +1,5 @@
.. _decoupled-packages:
Decoupled packages example (multiple containers)
================================================

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

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

View File

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

View File

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

View 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

View File

@ -0,0 +1 @@
"""Top-level package."""

View File

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

View 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}"')

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

View 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}')

View File

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

View File

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

View File

@ -1 +0,0 @@
"""Example top-level package."""

View File

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

View File

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

View File

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

View File

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