mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-02-18 12:30:50 +03:00
Merge branch 'release/3.12.1' into master
This commit is contained in:
commit
83053ad49e
|
@ -1,7 +1,8 @@
|
||||||
sudo: false
|
sudo: false
|
||||||
install:
|
install:
|
||||||
- pip install tox
|
- pip install tox
|
||||||
script:
|
- if [[ $TRAVIS_PYTHON_VERSION == 3.3 ]]; then pip install virtualenv==15.2.0; fi
|
||||||
|
script:
|
||||||
- tox
|
- tox
|
||||||
language:
|
language:
|
||||||
- python
|
- python
|
||||||
|
|
107
README.rst
107
README.rst
|
@ -270,9 +270,9 @@ Dependency Injector in action
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
Brief example below is a simplified version of inversion of control
|
Brief example below is a simplified version of inversion of control
|
||||||
containters from one of the real-life applications. This example demonstrates
|
container from one of the real-life applications. This example demonstrates
|
||||||
usage of *Dependency Injector* inversion of control containers & providers
|
usage of *Dependency Injector* inversion of control container & providers
|
||||||
for specifying all application components and their dependencies beetween
|
for specifying all application components and their dependencies between
|
||||||
each other in one module. Besides other listed above advantages, it gives a
|
each other in one module. Besides other listed above advantages, it gives a
|
||||||
great opportunity to control & manage application's structure in one place.
|
great opportunity to control & manage application's structure in one place.
|
||||||
|
|
||||||
|
@ -285,82 +285,89 @@ great opportunity to control & manage application's structure in one place.
|
||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
|
|
||||||
import example.main
|
from dependency_injector import containers, providers
|
||||||
import example.services
|
from example import services, main
|
||||||
|
|
||||||
import dependency_injector.containers as containers
|
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
class Core(containers.DeclarativeContainer):
|
class IocContainer(containers.DeclarativeContainer):
|
||||||
"""IoC container of core component providers."""
|
"""Application IoC container."""
|
||||||
|
|
||||||
config = providers.Configuration('config')
|
config = providers.Configuration('config')
|
||||||
|
|
||||||
logger = providers.Singleton(logging.Logger, name='example')
|
logger = providers.Singleton(logging.Logger, name='example')
|
||||||
|
|
||||||
|
# Gateways
|
||||||
|
|
||||||
class Gateways(containers.DeclarativeContainer):
|
database_client = providers.Singleton(sqlite3.connect, config.database.dsn)
|
||||||
"""IoC container of gateway (API clients to remote services) providers."""
|
|
||||||
|
|
||||||
database = providers.Singleton(sqlite3.connect, Core.config.database.dsn)
|
s3_client = providers.Singleton(
|
||||||
|
|
||||||
s3 = providers.Singleton(
|
|
||||||
boto3.client, 's3',
|
boto3.client, 's3',
|
||||||
aws_access_key_id=Core.config.aws.access_key_id,
|
aws_access_key_id=config.aws.access_key_id,
|
||||||
aws_secret_access_key=Core.config.aws.secret_access_key)
|
aws_secret_access_key=config.aws.secret_access_key
|
||||||
|
)
|
||||||
|
|
||||||
|
# Services
|
||||||
|
|
||||||
class Services(containers.DeclarativeContainer):
|
users_service = providers.Factory(
|
||||||
"""IoC container of business service providers."""
|
services.UsersService,
|
||||||
|
db=database_client,
|
||||||
|
logger=logger
|
||||||
|
)
|
||||||
|
|
||||||
users = providers.Factory(example.services.UsersService,
|
auth_service = providers.Factory(
|
||||||
db=Gateways.database,
|
services.AuthService,
|
||||||
logger=Core.logger)
|
token_ttl=config.auth.token_ttl,
|
||||||
|
db=database_client,
|
||||||
|
logger=logger
|
||||||
|
)
|
||||||
|
|
||||||
auth = providers.Factory(example.services.AuthService,
|
photos_service = providers.Factory(
|
||||||
db=Gateways.database,
|
services.PhotosService,
|
||||||
logger=Core.logger,
|
db=database_client,
|
||||||
token_ttl=Core.config.auth.token_ttl)
|
s3=s3_client,
|
||||||
|
logger=logger
|
||||||
|
)
|
||||||
|
|
||||||
photos = providers.Factory(example.services.PhotosService,
|
# Misc
|
||||||
db=Gateways.database,
|
|
||||||
s3=Gateways.s3,
|
|
||||||
logger=Core.logger)
|
|
||||||
|
|
||||||
|
main = providers.Callable(
|
||||||
class Application(containers.DeclarativeContainer):
|
main.main,
|
||||||
"""IoC container of application component providers."""
|
users_service=users_service,
|
||||||
|
auth_service=auth_service,
|
||||||
main = providers.Callable(example.main.main,
|
photos_service=photos_service
|
||||||
users_service=Services.users,
|
)
|
||||||
auth_service=Services.auth,
|
|
||||||
photos_service=Services.photos)
|
|
||||||
|
|
||||||
Next example demonstrates run of example application defined above:
|
Next example demonstrates run of example application defined above:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
"""Run example application."""
|
"""Run example of dependency injection in Python."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from containers import Core, Application
|
from container import IocContainer
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Configure platform:
|
# Configure container:
|
||||||
Core.config.override({'database': {'dsn': ':memory:'},
|
container = IocContainer(
|
||||||
'aws': {'access_key_id': 'KEY',
|
config={
|
||||||
'secret_access_key': 'SECRET'},
|
'database': {
|
||||||
'auth': {'token_ttl': 3600}})
|
'dsn': ':memory:'
|
||||||
Core.logger().addHandler(logging.StreamHandler(sys.stdout))
|
},
|
||||||
|
'aws': {
|
||||||
|
'access_key_id': 'KEY',
|
||||||
|
'secret_access_key': 'SECRET'
|
||||||
|
},
|
||||||
|
'auth': {
|
||||||
|
'token_ttl': 3600
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
container.logger().addHandler(logging.StreamHandler(sys.stdout))
|
||||||
|
|
||||||
# Run application:
|
# Run application:
|
||||||
Application.main(uid=sys.argv[1],
|
container.main(*sys.argv[1:])
|
||||||
password=sys.argv[2],
|
|
||||||
photo=sys.argv[3])
|
|
||||||
|
|
||||||
You can get more *Dependency Injector* examples in ``/examples`` directory on
|
You can get more *Dependency Injector* examples in ``/examples`` directory on
|
||||||
GitHub:
|
GitHub:
|
||||||
|
|
|
@ -16,6 +16,8 @@ and powered by *Dependency Injector* framework.
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
movie_lister
|
movie_lister
|
||||||
services_miniapp
|
services_miniapp_v1
|
||||||
|
services_miniapp_v2
|
||||||
bundles_miniapp
|
bundles_miniapp
|
||||||
use_cases_miniapp
|
use_cases_miniapp
|
||||||
|
password_hashing_miniapp
|
||||||
|
|
19
docs/examples/password_hashing_miniapp.rst
Normal file
19
docs/examples/password_hashing_miniapp.rst
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
.. disqus::
|
|
@ -1,5 +1,5 @@
|
||||||
Services mini application example
|
Services mini application example (v1 - multiple containers)
|
||||||
---------------------------------
|
------------------------------------------------------------
|
||||||
|
|
||||||
.. meta::
|
.. meta::
|
||||||
:description: "Services miniapp" is an example mini application that
|
:description: "Services miniapp" is an example mini application that
|
||||||
|
@ -7,7 +7,8 @@ Services mini application example
|
||||||
some standard and 3rd-party libraries for logging,
|
some standard and 3rd-party libraries for logging,
|
||||||
interaction with database and remote service via API.
|
interaction with database and remote service via API.
|
||||||
"Services miniapp" example demonstrates usage of
|
"Services miniapp" example demonstrates usage of
|
||||||
Dependency Injector for creating several IoC containers.
|
Dependency Injector for creating several inversion of control /
|
||||||
|
dependency injection containers.
|
||||||
|
|
||||||
"Services miniapp" is an example mini application that consists from several
|
"Services miniapp" is an example mini application that consists from several
|
||||||
services that have dependencies on some standard and 3rd-party libraries for
|
services that have dependencies on some standard and 3rd-party libraries for
|
||||||
|
@ -44,13 +45,13 @@ Example application structure:
|
||||||
|
|
||||||
Listing of ``example/services.py``:
|
Listing of ``example/services.py``:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/miniapps/services/example/services.py
|
.. literalinclude:: ../../examples/miniapps/services_v1/example/services.py
|
||||||
:language: python
|
:language: python
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
Listing of ``example/main.py``:
|
Listing of ``example/main.py``:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/miniapps/services/example/main.py
|
.. literalinclude:: ../../examples/miniapps/services_v1/example/main.py
|
||||||
:language: python
|
:language: python
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ IoC containers
|
||||||
|
|
||||||
Listing of ``containers.py``:
|
Listing of ``containers.py``:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/miniapps/services/containers.py
|
.. literalinclude:: ../../examples/miniapps/services_v1/containers.py
|
||||||
:language: python
|
:language: python
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ Run application
|
||||||
|
|
||||||
Listing of ``run.py``:
|
Listing of ``run.py``:
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/miniapps/services/run.py
|
.. literalinclude:: ../../examples/miniapps/services_v1/run.py
|
||||||
:language: python
|
:language: python
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
77
docs/examples/services_miniapp_v2.rst
Normal file
77
docs/examples/services_miniapp_v2.rst
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
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
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
Listing of ``example/main.py``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/miniapps/services_v2/example/main.py
|
||||||
|
:language: python
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
IoC container
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Listing of ``container.py``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/miniapps/services_v2/container.py
|
||||||
|
:language: python
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
Run application
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Listing of ``run.py``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/miniapps/services_v2/run.py
|
||||||
|
:language: python
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
|
||||||
|
.. disqus::
|
|
@ -7,6 +7,18 @@ 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.12.1
|
||||||
|
------
|
||||||
|
- Update main page example from "services_v1" to "services_v2".
|
||||||
|
- Fix few typos on main page.
|
||||||
|
- Add new example miniapp "password_hashing".
|
||||||
|
- Add new example miniapp "services_v2".
|
||||||
|
- Rename example miniapp "services" to "services_v1".
|
||||||
|
- Fix incompatibility issue between Python 3.3, pip 10.0.0 and virtualenv
|
||||||
|
16.0.0 (`details <https://github.com/awslabs/base64io-python/issues/4>`_)
|
||||||
|
that caused failures of Python 3.3 tests on Travis.
|
||||||
|
- Regenerate C sources using Cython 0.28.3.
|
||||||
|
|
||||||
3.12.0
|
3.12.0
|
||||||
------
|
------
|
||||||
- Regenerate C sources using Cython 0.28.2.
|
- Regenerate C sources using Cython 0.28.2.
|
||||||
|
|
12
examples/miniapps/password_hashing/README.rst
Normal file
12
examples/miniapps/password_hashing/README.rst
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
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
|
42
examples/miniapps/password_hashing/example.py
Normal file
42
examples/miniapps/password_hashing/example.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
"""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(object):
|
||||||
|
"""Users service."""
|
||||||
|
|
||||||
|
def __init__(self, password_hasher):
|
||||||
|
"""Initializer."""
|
||||||
|
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.encrypt,
|
||||||
|
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)
|
|
@ -1,27 +0,0 @@
|
||||||
"""Example main module."""
|
|
||||||
|
|
||||||
|
|
||||||
def main(uid, password, photo, users_service, auth_service, photos_service):
|
|
||||||
"""Authenticate user and upload photo.
|
|
||||||
|
|
||||||
:param uid: User identifier.
|
|
||||||
:type uid: int
|
|
||||||
|
|
||||||
:param password: User's password for verification.
|
|
||||||
:type password: str
|
|
||||||
|
|
||||||
:param photo_path: Path to photo for uploading.
|
|
||||||
:type photo_path: str
|
|
||||||
|
|
||||||
:param users_service: Users service.
|
|
||||||
:type users_service: example.services.UsersService
|
|
||||||
|
|
||||||
:param auth_service: Authentication service.
|
|
||||||
:type auth_service: example.services.AuthService
|
|
||||||
|
|
||||||
:param photo_service: Photo service.
|
|
||||||
:type photo_service: example.services.PhotoService
|
|
||||||
"""
|
|
||||||
user = users_service.get_user_by_id(uid)
|
|
||||||
auth_service.authenticate(user, password)
|
|
||||||
photos_service.upload_photo(user['uid'], photo)
|
|
|
@ -1,103 +0,0 @@
|
||||||
"""Example business services module."""
|
|
||||||
|
|
||||||
|
|
||||||
class BaseService(object):
|
|
||||||
"""Service base class."""
|
|
||||||
|
|
||||||
|
|
||||||
class UsersService(BaseService):
|
|
||||||
"""Users service."""
|
|
||||||
|
|
||||||
def __init__(self, logger, db):
|
|
||||||
"""Initializer.
|
|
||||||
|
|
||||||
:param logger: Logger instance.
|
|
||||||
:type logger: logging.Logger
|
|
||||||
|
|
||||||
:param db: Database connection.
|
|
||||||
:type db: sqlite3.Connection
|
|
||||||
"""
|
|
||||||
self.logger = logger
|
|
||||||
self.db = db
|
|
||||||
|
|
||||||
def get_user_by_id(self, uid):
|
|
||||||
"""Return user's data by identifier.
|
|
||||||
|
|
||||||
:param uid: User identifier.
|
|
||||||
:type uid: int
|
|
||||||
|
|
||||||
:rtype: dict
|
|
||||||
"""
|
|
||||||
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):
|
|
||||||
"""Initializer.
|
|
||||||
|
|
||||||
:param logger: Logger instance.
|
|
||||||
:type logger: logging.Logger
|
|
||||||
|
|
||||||
:param db: Database connection.
|
|
||||||
:type db: sqlite3.Connection
|
|
||||||
|
|
||||||
:param token_ttl: Token lifetime in seconds.
|
|
||||||
:type token_ttl: int
|
|
||||||
"""
|
|
||||||
self.logger = logger
|
|
||||||
self.db = db
|
|
||||||
self.token_ttl = token_ttl
|
|
||||||
|
|
||||||
def authenticate(self, user, password):
|
|
||||||
"""Authenticate user.
|
|
||||||
|
|
||||||
:param user: User's data.
|
|
||||||
:type user: dict
|
|
||||||
|
|
||||||
:param password: User's password for verification.
|
|
||||||
:type password: str
|
|
||||||
|
|
||||||
:raises: AssertionError when password is wrong
|
|
||||||
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
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):
|
|
||||||
"""Initializer.
|
|
||||||
|
|
||||||
:param logger: Logger instance.
|
|
||||||
:type logger: logging.Logger
|
|
||||||
|
|
||||||
:param db: Database connection.
|
|
||||||
:type db: sqlite3.Connection
|
|
||||||
|
|
||||||
:param s3: AWS S3 client.
|
|
||||||
:type s3: botocore.client.S3
|
|
||||||
"""
|
|
||||||
self.logger = logger
|
|
||||||
self.db = db
|
|
||||||
self.s3 = s3
|
|
||||||
|
|
||||||
def upload_photo(self, uid, photo_path):
|
|
||||||
"""Upload user photo.
|
|
||||||
|
|
||||||
:param uid: User identifier.
|
|
||||||
:type uid: int
|
|
||||||
|
|
||||||
:param photo_path: Path to photo for uploading.
|
|
||||||
:type photo_path: str
|
|
||||||
|
|
||||||
:rtpe: None
|
|
||||||
"""
|
|
||||||
self.logger.debug('Photo %s has been successfully uploaded by user %s',
|
|
||||||
photo_path, uid)
|
|
8
examples/miniapps/services_v1/example/main.py
Normal file
8
examples/miniapps/services_v1/example/main.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
"""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)
|
50
examples/miniapps/services_v1/example/services.py
Normal file
50
examples/miniapps/services_v1/example/services.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
"""Example business services module."""
|
||||||
|
|
||||||
|
|
||||||
|
class BaseService(object):
|
||||||
|
"""Service base class."""
|
||||||
|
|
||||||
|
|
||||||
|
class UsersService(BaseService):
|
||||||
|
"""Users service."""
|
||||||
|
|
||||||
|
def __init__(self, logger, db):
|
||||||
|
"""Initializer."""
|
||||||
|
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):
|
||||||
|
"""Initializer."""
|
||||||
|
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):
|
||||||
|
"""Initializer."""
|
||||||
|
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)
|
8
examples/miniapps/services_v2/README.rst
Normal file
8
examples/miniapps/services_v2/README.rst
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Dependency Injector IoC containers example
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
Instructions for running
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
python run.py 1 secret photo.jpg
|
57
examples/miniapps/services_v2/container.py
Normal file
57
examples/miniapps/services_v2/container.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
"""Example of dependency injection in Python."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
import boto3
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
from example import services, main
|
||||||
|
|
||||||
|
|
||||||
|
class IocContainer(containers.DeclarativeContainer):
|
||||||
|
"""Application IoC container."""
|
||||||
|
|
||||||
|
config = providers.Configuration('config')
|
||||||
|
logger = providers.Singleton(logging.Logger, name='example')
|
||||||
|
|
||||||
|
# Gateways
|
||||||
|
|
||||||
|
database_client = providers.Singleton(sqlite3.connect, config.database.dsn)
|
||||||
|
|
||||||
|
s3_client = providers.Singleton(
|
||||||
|
boto3.client, 's3',
|
||||||
|
aws_access_key_id=config.aws.access_key_id,
|
||||||
|
aws_secret_access_key=config.aws.secret_access_key
|
||||||
|
)
|
||||||
|
|
||||||
|
# Services
|
||||||
|
|
||||||
|
users_service = providers.Factory(
|
||||||
|
services.UsersService,
|
||||||
|
db=database_client,
|
||||||
|
logger=logger
|
||||||
|
)
|
||||||
|
|
||||||
|
auth_service = providers.Factory(
|
||||||
|
services.AuthService,
|
||||||
|
token_ttl=config.auth.token_ttl,
|
||||||
|
db=database_client,
|
||||||
|
logger=logger
|
||||||
|
)
|
||||||
|
|
||||||
|
photos_service = providers.Factory(
|
||||||
|
services.PhotosService,
|
||||||
|
db=database_client,
|
||||||
|
s3=s3_client,
|
||||||
|
logger=logger
|
||||||
|
)
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
|
||||||
|
main = providers.Callable(
|
||||||
|
main.main,
|
||||||
|
users_service=users_service,
|
||||||
|
auth_service=auth_service,
|
||||||
|
photos_service=photos_service
|
||||||
|
)
|
1
examples/miniapps/services_v2/example/__init__.py
Normal file
1
examples/miniapps/services_v2/example/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"""Example top-level package."""
|
8
examples/miniapps/services_v2/example/main.py
Normal file
8
examples/miniapps/services_v2/example/main.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
"""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)
|
50
examples/miniapps/services_v2/example/services.py
Normal file
50
examples/miniapps/services_v2/example/services.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
"""Example business services module."""
|
||||||
|
|
||||||
|
|
||||||
|
class BaseService(object):
|
||||||
|
"""Service base class."""
|
||||||
|
|
||||||
|
|
||||||
|
class UsersService(BaseService):
|
||||||
|
"""Users service."""
|
||||||
|
|
||||||
|
def __init__(self, logger, db):
|
||||||
|
"""Initializer."""
|
||||||
|
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):
|
||||||
|
"""Initializer."""
|
||||||
|
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):
|
||||||
|
"""Initializer."""
|
||||||
|
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)
|
28
examples/miniapps/services_v2/run.py
Normal file
28
examples/miniapps/services_v2/run.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
"""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:])
|
|
@ -1,4 +1,4 @@
|
||||||
cython==0.27.3
|
cython==0.28.3
|
||||||
tox
|
tox
|
||||||
unittest2
|
unittest2
|
||||||
coverage
|
coverage
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Dependency injector top-level package."""
|
"""Dependency injector top-level package."""
|
||||||
|
|
||||||
__version__ = '3.12.0'
|
__version__ = '3.12.1'
|
||||||
"""Version number that follows semantic versioning.
|
"""Version number that follows semantic versioning.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Generated by Cython 0.28.2 */
|
/* Generated by Cython 0.28.3 */
|
||||||
|
|
||||||
#define PY_SSIZE_T_CLEAN
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
#elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)
|
#elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)
|
||||||
#error Cython requires Python 2.6+ or Python 3.3+.
|
#error Cython requires Python 2.6+ or Python 3.3+.
|
||||||
#else
|
#else
|
||||||
#define CYTHON_ABI "0_28_2"
|
#define CYTHON_ABI "0_28_3"
|
||||||
#define CYTHON_FUTURE_DIVISION 0
|
#define CYTHON_FUTURE_DIVISION 0
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#ifndef offsetof
|
#ifndef offsetof
|
||||||
|
@ -453,6 +453,7 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
|
||||||
#define PyString_Type PyUnicode_Type
|
#define PyString_Type PyUnicode_Type
|
||||||
#define PyString_Check PyUnicode_Check
|
#define PyString_Check PyUnicode_Check
|
||||||
#define PyString_CheckExact PyUnicode_CheckExact
|
#define PyString_CheckExact PyUnicode_CheckExact
|
||||||
|
#define PyObject_Unicode PyObject_Str
|
||||||
#endif
|
#endif
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
#define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj)
|
#define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj)
|
||||||
|
@ -645,7 +646,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) {
|
||||||
#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode
|
#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode
|
||||||
#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj)
|
#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj)
|
||||||
#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None)
|
#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None)
|
||||||
#define __Pyx_PyBool_FromLong(b) ((b) ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False))
|
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b);
|
||||||
static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
|
static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
|
||||||
static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
|
static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
|
||||||
#define __Pyx_PySequence_Tuple(obj)\
|
#define __Pyx_PySequence_Tuple(obj)\
|
||||||
|
@ -753,7 +754,7 @@ static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; }
|
||||||
static PyObject *__pyx_m = NULL;
|
static PyObject *__pyx_m = NULL;
|
||||||
static PyObject *__pyx_d;
|
static PyObject *__pyx_d;
|
||||||
static PyObject *__pyx_b;
|
static PyObject *__pyx_b;
|
||||||
static PyObject *__pyx_cython_runtime;
|
static PyObject *__pyx_cython_runtime = NULL;
|
||||||
static PyObject *__pyx_empty_tuple;
|
static PyObject *__pyx_empty_tuple;
|
||||||
static PyObject *__pyx_empty_bytes;
|
static PyObject *__pyx_empty_bytes;
|
||||||
static PyObject *__pyx_empty_unicode;
|
static PyObject *__pyx_empty_unicode;
|
||||||
|
@ -11455,7 +11456,7 @@ static int __Pyx_modinit_function_import_code(void) {
|
||||||
#ifndef CYTHON_SMALL_CODE
|
#ifndef CYTHON_SMALL_CODE
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#define CYTHON_SMALL_CODE
|
#define CYTHON_SMALL_CODE
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__) && (!(defined(__cplusplus)) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4)))
|
||||||
#define CYTHON_SMALL_CODE __attribute__((optimize("Os")))
|
#define CYTHON_SMALL_CODE __attribute__((optimize("Os")))
|
||||||
#else
|
#else
|
||||||
#define CYTHON_SMALL_CODE
|
#define CYTHON_SMALL_CODE
|
||||||
|
@ -14131,6 +14132,9 @@ static int __Pyx_CLineForTraceback(CYTHON_UNUSED PyThreadState *tstate, int c_li
|
||||||
#if CYTHON_COMPILING_IN_CPYTHON
|
#if CYTHON_COMPILING_IN_CPYTHON
|
||||||
PyObject **cython_runtime_dict;
|
PyObject **cython_runtime_dict;
|
||||||
#endif
|
#endif
|
||||||
|
if (unlikely(!__pyx_cython_runtime)) {
|
||||||
|
return c_line;
|
||||||
|
}
|
||||||
__Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
|
__Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
|
||||||
#if CYTHON_COMPILING_IN_CPYTHON
|
#if CYTHON_COMPILING_IN_CPYTHON
|
||||||
cython_runtime_dict = _PyObject_GetDictPtr(__pyx_cython_runtime);
|
cython_runtime_dict = _PyObject_GetDictPtr(__pyx_cython_runtime);
|
||||||
|
@ -16325,6 +16329,9 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
|
||||||
Py_DECREF(x);
|
Py_DECREF(x);
|
||||||
return ival;
|
return ival;
|
||||||
}
|
}
|
||||||
|
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) {
|
||||||
|
return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False);
|
||||||
|
}
|
||||||
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
|
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
|
||||||
return PyInt_FromSize_t(ival);
|
return PyInt_FromSize_t(ival);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Generated by Cython 0.28.2 */
|
/* Generated by Cython 0.28.3 */
|
||||||
|
|
||||||
#define PY_SSIZE_T_CLEAN
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
#elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)
|
#elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000)
|
||||||
#error Cython requires Python 2.6+ or Python 3.3+.
|
#error Cython requires Python 2.6+ or Python 3.3+.
|
||||||
#else
|
#else
|
||||||
#define CYTHON_ABI "0_28_2"
|
#define CYTHON_ABI "0_28_3"
|
||||||
#define CYTHON_FUTURE_DIVISION 0
|
#define CYTHON_FUTURE_DIVISION 0
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#ifndef offsetof
|
#ifndef offsetof
|
||||||
|
@ -453,6 +453,7 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
|
||||||
#define PyString_Type PyUnicode_Type
|
#define PyString_Type PyUnicode_Type
|
||||||
#define PyString_Check PyUnicode_Check
|
#define PyString_Check PyUnicode_Check
|
||||||
#define PyString_CheckExact PyUnicode_CheckExact
|
#define PyString_CheckExact PyUnicode_CheckExact
|
||||||
|
#define PyObject_Unicode PyObject_Str
|
||||||
#endif
|
#endif
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
#define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj)
|
#define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj)
|
||||||
|
@ -645,7 +646,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) {
|
||||||
#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode
|
#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode
|
||||||
#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj)
|
#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj)
|
||||||
#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None)
|
#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None)
|
||||||
#define __Pyx_PyBool_FromLong(b) ((b) ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False))
|
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b);
|
||||||
static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
|
static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
|
||||||
static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
|
static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
|
||||||
#define __Pyx_PySequence_Tuple(obj)\
|
#define __Pyx_PySequence_Tuple(obj)\
|
||||||
|
@ -753,7 +754,7 @@ static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; }
|
||||||
static PyObject *__pyx_m = NULL;
|
static PyObject *__pyx_m = NULL;
|
||||||
static PyObject *__pyx_d;
|
static PyObject *__pyx_d;
|
||||||
static PyObject *__pyx_b;
|
static PyObject *__pyx_b;
|
||||||
static PyObject *__pyx_cython_runtime;
|
static PyObject *__pyx_cython_runtime = NULL;
|
||||||
static PyObject *__pyx_empty_tuple;
|
static PyObject *__pyx_empty_tuple;
|
||||||
static PyObject *__pyx_empty_bytes;
|
static PyObject *__pyx_empty_bytes;
|
||||||
static PyObject *__pyx_empty_unicode;
|
static PyObject *__pyx_empty_unicode;
|
||||||
|
@ -56515,7 +56516,7 @@ static int __Pyx_modinit_function_import_code(void) {
|
||||||
#ifndef CYTHON_SMALL_CODE
|
#ifndef CYTHON_SMALL_CODE
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#define CYTHON_SMALL_CODE
|
#define CYTHON_SMALL_CODE
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__) && (!(defined(__cplusplus)) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4)))
|
||||||
#define CYTHON_SMALL_CODE __attribute__((optimize("Os")))
|
#define CYTHON_SMALL_CODE __attribute__((optimize("Os")))
|
||||||
#else
|
#else
|
||||||
#define CYTHON_SMALL_CODE
|
#define CYTHON_SMALL_CODE
|
||||||
|
@ -59958,6 +59959,9 @@ static int __Pyx_CLineForTraceback(CYTHON_UNUSED PyThreadState *tstate, int c_li
|
||||||
#if CYTHON_COMPILING_IN_CPYTHON
|
#if CYTHON_COMPILING_IN_CPYTHON
|
||||||
PyObject **cython_runtime_dict;
|
PyObject **cython_runtime_dict;
|
||||||
#endif
|
#endif
|
||||||
|
if (unlikely(!__pyx_cython_runtime)) {
|
||||||
|
return c_line;
|
||||||
|
}
|
||||||
__Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
|
__Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
|
||||||
#if CYTHON_COMPILING_IN_CPYTHON
|
#if CYTHON_COMPILING_IN_CPYTHON
|
||||||
cython_runtime_dict = _PyObject_GetDictPtr(__pyx_cython_runtime);
|
cython_runtime_dict = _PyObject_GetDictPtr(__pyx_cython_runtime);
|
||||||
|
@ -60939,6 +60943,9 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
|
||||||
Py_DECREF(x);
|
Py_DECREF(x);
|
||||||
return ival;
|
return ival;
|
||||||
}
|
}
|
||||||
|
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) {
|
||||||
|
return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False);
|
||||||
|
}
|
||||||
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
|
static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
|
||||||
return PyInt_FromSize_t(ival);
|
return PyInt_FromSize_t(ival);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user