From 431dee80d88b60e3a36d0fc044c212bce0a1a1df Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Fri, 22 Jun 2018 09:52:58 +0300 Subject: [PATCH] Add ioc_container example --- docs/main/changelog.rst | 4 + examples/miniapps/ioc_container/README.rst | 8 ++ examples/miniapps/ioc_container/container.py | 55 ++++++++++ .../ioc_container/example/__init__.py | 1 + .../miniapps/ioc_container/example/main.py | 27 +++++ .../ioc_container/example/services.py | 103 ++++++++++++++++++ examples/miniapps/ioc_container/run.py | 21 ++++ 7 files changed, 219 insertions(+) create mode 100644 examples/miniapps/ioc_container/README.rst create mode 100644 examples/miniapps/ioc_container/container.py create mode 100644 examples/miniapps/ioc_container/example/__init__.py create mode 100644 examples/miniapps/ioc_container/example/main.py create mode 100644 examples/miniapps/ioc_container/example/services.py create mode 100644 examples/miniapps/ioc_container/run.py diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 24f1e103..6e7287b0 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,10 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +Development version +------------------- +- Add new example miniapp "ioc_container". + 3.12.0 ------ - Regenerate C sources using Cython 0.28.2. diff --git a/examples/miniapps/ioc_container/README.rst b/examples/miniapps/ioc_container/README.rst new file mode 100644 index 00000000..87373537 --- /dev/null +++ b/examples/miniapps/ioc_container/README.rst @@ -0,0 +1,8 @@ +Dependency Injector IoC containers example +========================================== + +Instructions for running + +.. code-block:: bash + + python run.py 1 secret photo.jpg diff --git a/examples/miniapps/ioc_container/container.py b/examples/miniapps/ioc_container/container.py new file mode 100644 index 00000000..39e3be89 --- /dev/null +++ b/examples/miniapps/ioc_container/container.py @@ -0,0 +1,55 @@ +"""Example of dependency injection in Python.""" + +import logging +import sqlite3 + +import boto3 + +import example.main +import example.services + +import dependency_injector.containers as containers +import dependency_injector.providers as providers + + +class 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( + example.services.UsersService, + db=database_client, + logger=logger) + + auth_service = providers.Factory( + example.services.AuthService, + token_ttl=config.auth.token_ttl, + db=database_client, + logger=logger) + + photos_service = providers.Factory( + example.services.PhotosService, + db=database_client, + s3=s3_client, + logger=logger) + + # Misc + + main = providers.Callable( + example.main.main, + users_service=users_service, + auth_service=auth_service, + photos_service=photos_service) diff --git a/examples/miniapps/ioc_container/example/__init__.py b/examples/miniapps/ioc_container/example/__init__.py new file mode 100644 index 00000000..bfa99aa2 --- /dev/null +++ b/examples/miniapps/ioc_container/example/__init__.py @@ -0,0 +1 @@ +"""Example top-level package.""" diff --git a/examples/miniapps/ioc_container/example/main.py b/examples/miniapps/ioc_container/example/main.py new file mode 100644 index 00000000..f1386c1a --- /dev/null +++ b/examples/miniapps/ioc_container/example/main.py @@ -0,0 +1,27 @@ +"""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) diff --git a/examples/miniapps/ioc_container/example/services.py b/examples/miniapps/ioc_container/example/services.py new file mode 100644 index 00000000..77e1ae99 --- /dev/null +++ b/examples/miniapps/ioc_container/example/services.py @@ -0,0 +1,103 @@ +"""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) diff --git a/examples/miniapps/ioc_container/run.py b/examples/miniapps/ioc_container/run.py new file mode 100644 index 00000000..50caff94 --- /dev/null +++ b/examples/miniapps/ioc_container/run.py @@ -0,0 +1,21 @@ +"""Run example of dependency injection in Python.""" + +import sys +import logging + +from container import IocContainer + + +if __name__ == '__main__': + # Configure platform: + 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(uid=sys.argv[1], + password=sys.argv[2], + photo=sys.argv[3])