mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-11-04 09:57:37 +03:00 
			
		
		
		
	Refactor multiple containers example
This commit is contained in:
		
							parent
							
								
									0f97fa4db8
								
							
						
					
					
						commit
						04f22e6152
					
				
							
								
								
									
										29
									
								
								examples/miniapps/services-multiple-containers/README.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								examples/miniapps/services-multiple-containers/README.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
Services mini application example (multiple containers)
 | 
			
		||||
=======================================================
 | 
			
		||||
 | 
			
		||||
Create virtual env:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   python3 -m venv venv
 | 
			
		||||
   . venv/bin/activate
 | 
			
		||||
 | 
			
		||||
Install requirements:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   pip install -r requirements.txt
 | 
			
		||||
 | 
			
		||||
Run:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   python -m example user@example.com secret photo.jpg
 | 
			
		||||
 | 
			
		||||
You should see:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   [2020-09-04 16:06:00,750] [DEBUG] [example.services.UserService]: User user@example.com has been found in database
 | 
			
		||||
   [2020-09-04 16:06:00,750] [DEBUG] [example.services.AuthService]: User user@example.com has been successfully authenticated
 | 
			
		||||
   [2020-09-04 16:06:00,750] [DEBUG] [example.services.PhotoService]: Photo photo.jpg has been successfully uploaded by user user@example.com
 | 
			
		||||
							
								
								
									
										30
									
								
								examples/miniapps/services-multiple-containers/config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								examples/miniapps/services-multiple-containers/config.yml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
core:
 | 
			
		||||
 | 
			
		||||
  logging:
 | 
			
		||||
    version: 1
 | 
			
		||||
    formatters:
 | 
			
		||||
      formatter:
 | 
			
		||||
        format: "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s"
 | 
			
		||||
    handlers:
 | 
			
		||||
      console:
 | 
			
		||||
        class: "logging.StreamHandler"
 | 
			
		||||
        level: "DEBUG"
 | 
			
		||||
        formatter: "formatter"
 | 
			
		||||
        stream: "ext://sys.stderr"
 | 
			
		||||
    root:
 | 
			
		||||
      level: "DEBUG"
 | 
			
		||||
      handlers: ["console"]
 | 
			
		||||
 | 
			
		||||
gateways:
 | 
			
		||||
 | 
			
		||||
  database:
 | 
			
		||||
    dsn: ":memory:"
 | 
			
		||||
 | 
			
		||||
  aws:
 | 
			
		||||
    access_key_id: "KEY"
 | 
			
		||||
    secret_access_key: "SECRET"
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
 | 
			
		||||
  auth:
 | 
			
		||||
    token_ttl: 3600
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
"""Main module."""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from .containers import Application
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(email: str, password: str, photo: str) -> None:
 | 
			
		||||
    application = Application()
 | 
			
		||||
 | 
			
		||||
    application.config.from_yaml('config.yml')
 | 
			
		||||
    application.core.configure_logging()
 | 
			
		||||
 | 
			
		||||
    user_service = application.services.user()
 | 
			
		||||
    auth_service = application.services.auth()
 | 
			
		||||
    photo_service = application.services.photo()
 | 
			
		||||
 | 
			
		||||
    user = user_service.get_user(email)
 | 
			
		||||
    auth_service.authenticate(user, password)
 | 
			
		||||
    photo_service.upload_photo(user, photo)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    main(*sys.argv[1:])
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
"""Containers module."""
 | 
			
		||||
 | 
			
		||||
import logging.config
 | 
			
		||||
import sqlite3
 | 
			
		||||
 | 
			
		||||
import boto3
 | 
			
		||||
from dependency_injector import containers, providers
 | 
			
		||||
 | 
			
		||||
from . import services
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Core(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    config = providers.Configuration()
 | 
			
		||||
 | 
			
		||||
    configure_logging = providers.Callable(
 | 
			
		||||
        logging.config.dictConfig,
 | 
			
		||||
        config=config.logging,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Gateways(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    config = providers.Configuration()
 | 
			
		||||
 | 
			
		||||
    database_client = providers.Singleton(
 | 
			
		||||
        sqlite3.connect,
 | 
			
		||||
        config.database.dsn,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    s3_client = providers.Singleton(
 | 
			
		||||
        boto3.client,
 | 
			
		||||
        service_name='s3',
 | 
			
		||||
        aws_access_key_id=config.aws.access_key_id,
 | 
			
		||||
        aws_secret_access_key=config.aws.secret_access_key,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Services(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    config = providers.Configuration()
 | 
			
		||||
    gateways = providers.DependenciesContainer()
 | 
			
		||||
 | 
			
		||||
    user = providers.Factory(
 | 
			
		||||
        services.UserService,
 | 
			
		||||
        db=gateways.database_client,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    auth = providers.Factory(
 | 
			
		||||
        services.AuthService,
 | 
			
		||||
        db=gateways.database_client,
 | 
			
		||||
        token_ttl=config.auth.token_ttl.as_int(),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    photo = providers.Factory(
 | 
			
		||||
        services.PhotoService,
 | 
			
		||||
        db=gateways.database_client,
 | 
			
		||||
        s3=gateways.s3_client,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Application(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    config = providers.Configuration()
 | 
			
		||||
 | 
			
		||||
    core = providers.Container(
 | 
			
		||||
        Core,
 | 
			
		||||
        config=config.core,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    gateways = providers.Container(
 | 
			
		||||
        Gateways,
 | 
			
		||||
        config=config.gateways,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    services = providers.Container(
 | 
			
		||||
        Services,
 | 
			
		||||
        config=config.services,
 | 
			
		||||
        gateways=gateways,
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
"""Services module."""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import sqlite3
 | 
			
		||||
from typing import Dict
 | 
			
		||||
 | 
			
		||||
from mypy_boto3_s3 import S3Client
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseService:
 | 
			
		||||
 | 
			
		||||
    def __init__(self) -> None:
 | 
			
		||||
        self.logger = logging.getLogger(
 | 
			
		||||
            f'{__name__}.{self.__class__.__name__}',
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserService(BaseService):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, db: sqlite3.Connection) -> None:
 | 
			
		||||
        self.db = db
 | 
			
		||||
        super().__init__()
 | 
			
		||||
 | 
			
		||||
    def get_user(self, email: str) -> Dict[str, str]:
 | 
			
		||||
        self.logger.debug('User %s has been found in database', email)
 | 
			
		||||
        return {'email': email, 'password_hash': '...'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AuthService(BaseService):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, db: sqlite3.Connection, token_ttl: int) -> None:
 | 
			
		||||
        self.db = db
 | 
			
		||||
        self.token_ttl = token_ttl
 | 
			
		||||
        super().__init__()
 | 
			
		||||
 | 
			
		||||
    def authenticate(self, user: Dict[str, str], password: str) -> None:
 | 
			
		||||
        assert password is not None
 | 
			
		||||
        self.logger.debug(
 | 
			
		||||
            'User %s has been successfully authenticated',
 | 
			
		||||
            user['email'],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PhotoService(BaseService):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, db: sqlite3.Connection, s3: S3Client) -> None:
 | 
			
		||||
        self.db = db
 | 
			
		||||
        self.s3 = s3
 | 
			
		||||
        super().__init__()
 | 
			
		||||
 | 
			
		||||
    def upload_photo(self, user: Dict[str, str], photo_path: str) -> None:
 | 
			
		||||
        self.logger.debug(
 | 
			
		||||
            'Photo %s has been successfully uploaded by user %s',
 | 
			
		||||
            photo_path,
 | 
			
		||||
            user['email'],
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
dependency-injector[yaml]
 | 
			
		||||
boto3
 | 
			
		||||
boto3-stubs[s3]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +0,0 @@
 | 
			
		|||
Dependency Injector IoC containers example
 | 
			
		||||
==========================================
 | 
			
		||||
 | 
			
		||||
Instructions for running
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    python run.py 1 secret photo.jpg
 | 
			
		||||
| 
						 | 
				
			
			@ -1,58 +0,0 @@
 | 
			
		|||
"""Example of dependency injection in Python."""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import sqlite3
 | 
			
		||||
 | 
			
		||||
import boto3
 | 
			
		||||
 | 
			
		||||
import example.main
 | 
			
		||||
import example.services
 | 
			
		||||
 | 
			
		||||
import dependency_injector.containers as containers
 | 
			
		||||
import dependency_injector.providers as providers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Core(containers.DeclarativeContainer):
 | 
			
		||||
    """IoC container of core component providers."""
 | 
			
		||||
 | 
			
		||||
    config = providers.Configuration('config')
 | 
			
		||||
 | 
			
		||||
    logger = providers.Singleton(logging.Logger, name='example')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Gateways(containers.DeclarativeContainer):
 | 
			
		||||
    """IoC container of gateway (API clients to remote services) providers."""
 | 
			
		||||
 | 
			
		||||
    database = providers.Singleton(sqlite3.connect, Core.config.database.dsn)
 | 
			
		||||
 | 
			
		||||
    s3 = providers.Singleton(
 | 
			
		||||
        boto3.client, 's3',
 | 
			
		||||
        aws_access_key_id=Core.config.aws.access_key_id,
 | 
			
		||||
        aws_secret_access_key=Core.config.aws.secret_access_key)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Services(containers.DeclarativeContainer):
 | 
			
		||||
    """IoC container of business service providers."""
 | 
			
		||||
 | 
			
		||||
    users = providers.Factory(example.services.UsersService,
 | 
			
		||||
                              db=Gateways.database,
 | 
			
		||||
                              logger=Core.logger)
 | 
			
		||||
 | 
			
		||||
    auth = providers.Factory(example.services.AuthService,
 | 
			
		||||
                             db=Gateways.database,
 | 
			
		||||
                             logger=Core.logger,
 | 
			
		||||
                             token_ttl=Core.config.auth.token_ttl)
 | 
			
		||||
 | 
			
		||||
    photos = providers.Factory(example.services.PhotosService,
 | 
			
		||||
                               db=Gateways.database,
 | 
			
		||||
                               s3=Gateways.s3,
 | 
			
		||||
                               logger=Core.logger)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Application(containers.DeclarativeContainer):
 | 
			
		||||
    """IoC container of application component providers."""
 | 
			
		||||
 | 
			
		||||
    main = providers.Callable(example.main.main,
 | 
			
		||||
                              users_service=Services.users,
 | 
			
		||||
                              auth_service=Services.auth,
 | 
			
		||||
                              photos_service=Services.photos)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +0,0 @@
 | 
			
		|||
"""Example main module."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(uid, password, photo, users_service, auth_service, photos_service):
 | 
			
		||||
    """Authenticate user and upload photo."""
 | 
			
		||||
    user = users_service.get_user_by_id(uid)
 | 
			
		||||
    auth_service.authenticate(user, password)
 | 
			
		||||
    photos_service.upload_photo(user['uid'], photo)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,50 +0,0 @@
 | 
			
		|||
"""Example business services module."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseService:
 | 
			
		||||
    """Service base class."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UsersService(BaseService):
 | 
			
		||||
    """Users service."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, logger, db):
 | 
			
		||||
        """Initialize instance."""
 | 
			
		||||
        self.logger = logger
 | 
			
		||||
        self.db = db
 | 
			
		||||
 | 
			
		||||
    def get_user_by_id(self, uid):
 | 
			
		||||
        """Return user's data by identifier."""
 | 
			
		||||
        self.logger.debug('User %s has been found in database', uid)
 | 
			
		||||
        return dict(uid=uid, password_hash='secret_hash')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AuthService(BaseService):
 | 
			
		||||
    """Authentication service."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, logger, db, token_ttl):
 | 
			
		||||
        """Initialize instance."""
 | 
			
		||||
        self.logger = logger
 | 
			
		||||
        self.db = db
 | 
			
		||||
        self.token_ttl = token_ttl
 | 
			
		||||
 | 
			
		||||
    def authenticate(self, user, password):
 | 
			
		||||
        """Authenticate user."""
 | 
			
		||||
        assert user['password_hash'] == '_'.join((password, 'hash'))
 | 
			
		||||
        self.logger.debug('User %s has been successfully authenticated',
 | 
			
		||||
                          user['uid'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PhotosService(BaseService):
 | 
			
		||||
    """Photos service."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, logger, db, s3):
 | 
			
		||||
        """Initialize instance."""
 | 
			
		||||
        self.logger = logger
 | 
			
		||||
        self.db = db
 | 
			
		||||
        self.s3 = s3
 | 
			
		||||
 | 
			
		||||
    def upload_photo(self, uid, photo_path):
 | 
			
		||||
        """Upload user photo."""
 | 
			
		||||
        self.logger.debug('Photo %s has been successfully uploaded by user %s',
 | 
			
		||||
                          photo_path, uid)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,20 +0,0 @@
 | 
			
		|||
"""Run example application."""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from containers import Core, Application
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    # Configure platform:
 | 
			
		||||
    Core.config.override({'database': {'dsn': ':memory:'},
 | 
			
		||||
                          'aws': {'access_key_id': 'KEY',
 | 
			
		||||
                                  'secret_access_key': 'SECRET'},
 | 
			
		||||
                          'auth': {'token_ttl': 3600}})
 | 
			
		||||
    Core.logger().addHandler(logging.StreamHandler(sys.stdout))
 | 
			
		||||
 | 
			
		||||
    # Run application:
 | 
			
		||||
    Application.main(uid=sys.argv[1],
 | 
			
		||||
                     password=sys.argv[2],
 | 
			
		||||
                     photo=sys.argv[3])
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user