[WIP] Added first changes for the DI
This commit is contained in:
parent
36dfedf918
commit
c650e87443
@ -6,6 +6,7 @@ from cpl.application.application_runtime import ApplicationRuntime
|
|||||||
from cpl.application.startup_abc import StartupABC
|
from cpl.application.startup_abc import StartupABC
|
||||||
from cpl.configuration import Configuration
|
from cpl.configuration import Configuration
|
||||||
from cpl.dependency_injection import ServiceProvider
|
from cpl.dependency_injection import ServiceProvider
|
||||||
|
from cpl.dependency_injection.service_collection import ServiceCollection
|
||||||
|
|
||||||
|
|
||||||
class ApplicationBuilder(ApplicationBuilderABC):
|
class ApplicationBuilder(ApplicationBuilderABC):
|
||||||
@ -20,7 +21,7 @@ class ApplicationBuilder(ApplicationBuilderABC):
|
|||||||
|
|
||||||
self._configuration = Configuration()
|
self._configuration = Configuration()
|
||||||
self._runtime = ApplicationRuntime()
|
self._runtime = ApplicationRuntime()
|
||||||
self._services = ServiceProvider(self._configuration, self._runtime)
|
self._services = ServiceCollection(self._configuration, self._runtime)
|
||||||
|
|
||||||
def use_startup(self, startup: Type[StartupABC]):
|
def use_startup(self, startup: Type[StartupABC]):
|
||||||
"""
|
"""
|
||||||
@ -39,4 +40,4 @@ class ApplicationBuilder(ApplicationBuilderABC):
|
|||||||
self._startup.configure_configuration()
|
self._startup.configure_configuration()
|
||||||
self._startup.configure_services()
|
self._startup.configure_services()
|
||||||
|
|
||||||
return self._app(self._configuration, self._runtime, self._services)
|
return self._app(self._configuration, self._runtime, self._services.build_service_provider())
|
||||||
|
@ -21,7 +21,7 @@ from collections import namedtuple
|
|||||||
|
|
||||||
# imports:
|
# imports:
|
||||||
from .service_abc import ServiceABC
|
from .service_abc import ServiceABC
|
||||||
from .service_provider import ServiceProvider
|
from .service_provider_old import ServiceProvider
|
||||||
from .service_provider_abc import ServiceProviderABC
|
from .service_provider_abc import ServiceProviderABC
|
||||||
|
|
||||||
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
|
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
|
||||||
|
67
src/cpl/dependency_injection/service_collection.py
Normal file
67
src/cpl/dependency_injection/service_collection.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
from typing import Union, Type, Callable, Optional
|
||||||
|
|
||||||
|
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
|
||||||
|
from cpl.configuration.configuration_abc import ConfigurationABC
|
||||||
|
from cpl.database.context import DatabaseContextABC
|
||||||
|
from cpl.dependency_injection.service_factory import ServiceFactory
|
||||||
|
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
|
||||||
|
from cpl.dependency_injection.service_collection_abc import ServiceCollectionABC
|
||||||
|
from cpl.dependency_injection.service_descriptor import ServiceDescriptor
|
||||||
|
from cpl.dependency_injection.service_lifetime_enum import ServiceLifetimeEnum
|
||||||
|
from cpl.dependency_injection.service_provider import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceCollection(ServiceCollectionABC):
|
||||||
|
|
||||||
|
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC):
|
||||||
|
ServiceCollectionABC.__init__(self)
|
||||||
|
self._configuration: ConfigurationABC = config
|
||||||
|
self._runtime: ApplicationRuntimeABC = runtime
|
||||||
|
|
||||||
|
self._database_context: Optional[DatabaseContextABC] = None
|
||||||
|
self._service_descriptors: list[ServiceDescriptor] = []
|
||||||
|
|
||||||
|
def _add_descriptor(self, service: Union[type, object], lifetime: ServiceLifetimeEnum):
|
||||||
|
found = False
|
||||||
|
for descriptor in self._service_descriptors:
|
||||||
|
if not isinstance(service, type):
|
||||||
|
service = type(service)
|
||||||
|
|
||||||
|
if descriptor.service_type == service:
|
||||||
|
found = True
|
||||||
|
|
||||||
|
if found:
|
||||||
|
service_type = service
|
||||||
|
if not isinstance(service, type):
|
||||||
|
service_type = type(service)
|
||||||
|
|
||||||
|
raise Exception(f'Service of type {service_type} already exists')
|
||||||
|
|
||||||
|
self._service_descriptors.append(ServiceDescriptor(service, lifetime))
|
||||||
|
|
||||||
|
def add_db_context(self, db_context: Type[DatabaseContextABC]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_singleton(self, service_type: Union[type, object], service: Union[type, object] = None):
|
||||||
|
if service is not None:
|
||||||
|
if isinstance(service, type):
|
||||||
|
service = service()
|
||||||
|
|
||||||
|
self._add_descriptor(service, ServiceLifetimeEnum.singleton)
|
||||||
|
else:
|
||||||
|
if isinstance(service_type, type):
|
||||||
|
service_type = service_type()
|
||||||
|
|
||||||
|
self._add_descriptor(service_type, ServiceLifetimeEnum.singleton)
|
||||||
|
|
||||||
|
def add_scoped(self, service_type: Type, service: Callable = None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_transient(self, service_type: Union[type], service: Union[type] = None):
|
||||||
|
if service is not None:
|
||||||
|
self._add_descriptor(service, ServiceLifetimeEnum.transient)
|
||||||
|
else:
|
||||||
|
self._add_descriptor(service_type, ServiceLifetimeEnum.transient)
|
||||||
|
|
||||||
|
def build_service_provider(self) -> ServiceProviderABC:
|
||||||
|
return ServiceProvider(ServiceFactory(self._service_descriptors, self._configuration, self._runtime))
|
62
src/cpl/dependency_injection/service_collection_abc.py
Normal file
62
src/cpl/dependency_injection/service_collection_abc.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
from abc import abstractmethod, ABC
|
||||||
|
from collections import Callable
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
|
from cpl.database.context.database_context_abc import DatabaseContextABC
|
||||||
|
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceCollectionABC(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
ABC for service providing
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add_db_context(self, db_context: Type[DatabaseContextABC]):
|
||||||
|
"""
|
||||||
|
Adds database context
|
||||||
|
:param db_context:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add_transient(self, service_type: Type, service: Callable = None):
|
||||||
|
"""
|
||||||
|
Adds a service with transient lifetime
|
||||||
|
:param service_type:
|
||||||
|
:param service:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add_scoped(self, service_type: Type, service: Callable = None):
|
||||||
|
"""
|
||||||
|
Adds a service with scoped lifetime
|
||||||
|
:param service_type:
|
||||||
|
:param service:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add_singleton(self, service_type: Type, service: Callable = None):
|
||||||
|
"""
|
||||||
|
Adds a service with singleton lifetime
|
||||||
|
:param service_type:
|
||||||
|
:param service:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def build_service_provider(self) -> ServiceProviderABC:
|
||||||
|
"""
|
||||||
|
Creates instance of the service provider
|
||||||
|
"""
|
||||||
|
pass
|
33
src/cpl/dependency_injection/service_descriptor.py
Normal file
33
src/cpl/dependency_injection/service_descriptor.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from typing import Union, Optional
|
||||||
|
|
||||||
|
from cpl.dependency_injection.service_lifetime_enum import ServiceLifetimeEnum
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceDescriptor:
|
||||||
|
|
||||||
|
def __init__(self, implementation: Union[type, Optional[object]], lifetime: ServiceLifetimeEnum):
|
||||||
|
|
||||||
|
self._service_type = implementation
|
||||||
|
self._implementation = implementation
|
||||||
|
self._lifetime = lifetime
|
||||||
|
|
||||||
|
if not isinstance(implementation, type):
|
||||||
|
self._service_type = type(implementation)
|
||||||
|
else:
|
||||||
|
self._implementation = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def service_type(self) -> type:
|
||||||
|
return self._service_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def implementation(self) -> Union[type, Optional[object]]:
|
||||||
|
return self._implementation
|
||||||
|
|
||||||
|
@implementation.setter
|
||||||
|
def implementation(self, implementation: Union[type, Optional[object]]):
|
||||||
|
self._implementation = implementation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lifetime(self) -> ServiceLifetimeEnum:
|
||||||
|
return self._lifetime
|
27
src/cpl/dependency_injection/service_factory.py
Normal file
27
src/cpl/dependency_injection/service_factory.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
|
||||||
|
from cpl.configuration.configuration_abc import ConfigurationABC
|
||||||
|
from cpl.dependency_injection.service_descriptor import ServiceDescriptor
|
||||||
|
from cpl.dependency_injection.service_factory_abc import ServiceFactoryABC
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceFactory(ServiceFactoryABC):
|
||||||
|
|
||||||
|
def __init__(self, service_descriptors: list[ServiceDescriptor], config: ConfigurationABC,
|
||||||
|
runtime: ApplicationRuntimeABC):
|
||||||
|
ServiceFactoryABC.__init__(self)
|
||||||
|
|
||||||
|
self._service_descriptors: list[ServiceDescriptor] = service_descriptors
|
||||||
|
self._configuration: ConfigurationABC = config
|
||||||
|
self._runtime: ApplicationRuntimeABC = runtime
|
||||||
|
|
||||||
|
@property
|
||||||
|
def service_descriptors(self) -> list[ServiceDescriptor]:
|
||||||
|
return self._service_descriptors
|
||||||
|
|
||||||
|
@property
|
||||||
|
def configuration(self) -> ConfigurationABC:
|
||||||
|
return self._configuration
|
||||||
|
|
||||||
|
@property
|
||||||
|
def runtime(self) -> ApplicationRuntimeABC:
|
||||||
|
return self._runtime
|
23
src/cpl/dependency_injection/service_factory_abc.py
Normal file
23
src/cpl/dependency_injection/service_factory_abc.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
|
||||||
|
from cpl.configuration.configuration_abc import ConfigurationABC
|
||||||
|
from cpl.dependency_injection.service_descriptor import ServiceDescriptor
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceFactoryABC(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __init__(self): pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def service_descriptors(self) -> list[ServiceDescriptor]: pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def configuration(self) -> ConfigurationABC: pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def runtime(self) -> ApplicationRuntimeABC: pass
|
8
src/cpl/dependency_injection/service_lifetime_enum.py
Normal file
8
src/cpl/dependency_injection/service_lifetime_enum.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceLifetimeEnum(Enum):
|
||||||
|
|
||||||
|
singleton = 0
|
||||||
|
scoped = 1 # not supported yet
|
||||||
|
transient = 2
|
@ -1,122 +1,37 @@
|
|||||||
from collections import Callable
|
from collections import Callable
|
||||||
from inspect import signature, Parameter
|
from typing import Optional
|
||||||
from typing import Type, Optional, Union
|
|
||||||
|
|
||||||
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
|
from cpl.dependency_injection import ServiceProviderABC
|
||||||
from cpl.configuration.configuration_abc import ConfigurationABC
|
from cpl.dependency_injection.service_descriptor import ServiceDescriptor
|
||||||
from cpl.configuration.configuration_model_abc import ConfigurationModelABC
|
from cpl.dependency_injection.service_factory_abc import ServiceFactoryABC
|
||||||
from cpl.database.context.database_context_abc import DatabaseContextABC
|
from cpl.dependency_injection.service_lifetime_enum import ServiceLifetimeEnum
|
||||||
from cpl.dependency_injection.service_abc import ServiceABC
|
|
||||||
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
|
|
||||||
from cpl.environment.environment_abc import ApplicationEnvironmentABC
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceProvider(ServiceProviderABC):
|
class ServiceProvider(ServiceProviderABC):
|
||||||
|
|
||||||
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC):
|
def __init__(self, service_factory: ServiceFactoryABC):
|
||||||
"""
|
|
||||||
Service for service providing
|
|
||||||
:param runtime:
|
|
||||||
"""
|
|
||||||
ServiceProviderABC.__init__(self)
|
ServiceProviderABC.__init__(self)
|
||||||
self._configuration: ConfigurationABC = config
|
|
||||||
self._runtime: ApplicationRuntimeABC = runtime
|
|
||||||
self._database_context: Optional[DatabaseContextABC] = None
|
|
||||||
|
|
||||||
self._transient_services: dict[Type[ServiceABC], Callable[ServiceABC]] = {}
|
self._service_factory = service_factory
|
||||||
self._scoped_services: dict[Type[ServiceABC], Callable[ServiceABC]] = {}
|
|
||||||
self._singleton_services: dict[Type[ServiceABC], Callable[ServiceABC], ServiceABC] = {}
|
|
||||||
|
|
||||||
def _create_instance(self, service: Union[Callable[ServiceABC], ServiceABC]) -> Callable[ServiceABC]:
|
def _find_service(self, service_type: type) -> [ServiceDescriptor]:
|
||||||
"""
|
for descriptor in self._service_factory.service_descriptors:
|
||||||
Creates an instance of given type
|
if descriptor.service_type == service_type or issubclass(descriptor.service_type, service_type):
|
||||||
:param service:
|
return descriptor
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
sig = signature(service.__init__)
|
|
||||||
params = []
|
|
||||||
for param in sig.parameters.items():
|
|
||||||
parameter = param[1]
|
|
||||||
if parameter.name != 'self' and parameter.annotation != Parameter.empty:
|
|
||||||
if issubclass(parameter.annotation, ApplicationRuntimeABC):
|
|
||||||
params.append(self._runtime)
|
|
||||||
|
|
||||||
elif issubclass(parameter.annotation, ApplicationEnvironmentABC):
|
return None
|
||||||
params.append(self._configuration.environment)
|
|
||||||
|
|
||||||
elif issubclass(parameter.annotation, DatabaseContextABC):
|
def get_service(self, service_type: type) -> Optional[Callable[object]]:
|
||||||
params.append(self._database_context)
|
result = self._find_service(service_type)
|
||||||
|
|
||||||
elif issubclass(parameter.annotation, ConfigurationModelABC):
|
if result is None:
|
||||||
params.append(self._configuration.get_configuration(parameter.annotation))
|
return None
|
||||||
|
|
||||||
elif issubclass(parameter.annotation, ConfigurationABC):
|
if result.implementation is not None:
|
||||||
params.append(self._configuration)
|
return result.implementation
|
||||||
|
|
||||||
elif issubclass(parameter.annotation, ServiceProviderABC):
|
implementation = result.service_type()
|
||||||
params.append(self)
|
if result.lifetime == ServiceLifetimeEnum.singleton:
|
||||||
|
result.implementation = implementation
|
||||||
|
|
||||||
else:
|
return implementation
|
||||||
params.append(self.get_service(parameter.annotation))
|
|
||||||
|
|
||||||
return service(*params)
|
|
||||||
|
|
||||||
def add_db_context(self, db_context: Type[DatabaseContextABC]):
|
|
||||||
self._database_context = self._create_instance(db_context)
|
|
||||||
|
|
||||||
def get_db_context(self) -> Callable[DatabaseContextABC]:
|
|
||||||
return self._database_context
|
|
||||||
|
|
||||||
def add_transient(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None):
|
|
||||||
if service is None:
|
|
||||||
self._transient_services[service_type] = service_type
|
|
||||||
else:
|
|
||||||
self._transient_services[service_type] = service
|
|
||||||
|
|
||||||
def add_scoped(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None):
|
|
||||||
if service is None:
|
|
||||||
self._scoped_services[service_type] = service_type
|
|
||||||
else:
|
|
||||||
self._scoped_services[service_type] = service
|
|
||||||
|
|
||||||
def add_singleton(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None):
|
|
||||||
for known_service in self._singleton_services:
|
|
||||||
if type(known_service) == service_type:
|
|
||||||
raise Exception(f'Service with type {service_type} already exists')
|
|
||||||
|
|
||||||
if service is None:
|
|
||||||
self._singleton_services[service_type] = self._create_instance(service_type)
|
|
||||||
else:
|
|
||||||
self._singleton_services[service_type] = self._create_instance(service)
|
|
||||||
|
|
||||||
def get_service(self, instance_type: Type) -> Callable[ServiceABC]:
|
|
||||||
if issubclass(instance_type, ServiceProviderABC):
|
|
||||||
return self
|
|
||||||
|
|
||||||
for service in self._transient_services:
|
|
||||||
if service == instance_type and isinstance(self._transient_services[service], type(instance_type)):
|
|
||||||
return self._create_instance(self._transient_services[service])
|
|
||||||
|
|
||||||
for service in self._scoped_services:
|
|
||||||
if service == instance_type and isinstance(self._scoped_services[service], type(instance_type)):
|
|
||||||
return self._create_instance(self._scoped_services[service])
|
|
||||||
|
|
||||||
for service in self._singleton_services:
|
|
||||||
if service == instance_type and isinstance(self._singleton_services[service], instance_type):
|
|
||||||
return self._singleton_services[service]
|
|
||||||
|
|
||||||
def remove_service(self, instance_type: Type[ServiceABC]):
|
|
||||||
for service in self._transient_services:
|
|
||||||
if service == instance_type and isinstance(self._transient_services[service], type(instance_type)):
|
|
||||||
del self._transient_services[service]
|
|
||||||
return
|
|
||||||
|
|
||||||
for service in self._scoped_services:
|
|
||||||
if service == instance_type and isinstance(self._scoped_services[service], type(instance_type)):
|
|
||||||
del self._scoped_services[service]
|
|
||||||
return
|
|
||||||
|
|
||||||
for service in self._singleton_services:
|
|
||||||
if service == instance_type and isinstance(self._singleton_services[service], instance_type):
|
|
||||||
del self._singleton_services[service]
|
|
||||||
return
|
|
||||||
|
@ -2,7 +2,6 @@ from abc import abstractmethod, ABC
|
|||||||
from collections import Callable
|
from collections import Callable
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from cpl.database.context.database_context_abc import DatabaseContextABC
|
|
||||||
from cpl.dependency_injection.service_abc import ServiceABC
|
from cpl.dependency_injection.service_abc import ServiceABC
|
||||||
|
|
||||||
|
|
||||||
@ -15,53 +14,6 @@ class ServiceProviderABC(ABC):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def add_db_context(self, db_context: Type[DatabaseContextABC]):
|
|
||||||
"""
|
|
||||||
Adds database context
|
|
||||||
:param db_context:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def get_db_context(self) -> Callable[DatabaseContextABC]:
|
|
||||||
""""
|
|
||||||
Returns database context
|
|
||||||
:return Callable[DatabaseContextABC]:
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def add_transient(self, service_type: Type, service: Callable = None):
|
|
||||||
"""
|
|
||||||
Adds a service with transient lifetime
|
|
||||||
:param service_type:
|
|
||||||
:param service:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def add_scoped(self, service_type: Type, service: Callable = None):
|
|
||||||
"""
|
|
||||||
Adds a service with scoped lifetime
|
|
||||||
:param service_type:
|
|
||||||
:param service:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def add_singleton(self, service_type: Type, service: Callable = None):
|
|
||||||
"""
|
|
||||||
Adds a service with singleton lifetime
|
|
||||||
:param service_type:
|
|
||||||
:param service:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_service(self, instance_type: Type) -> Callable[ServiceABC]:
|
def get_service(self, instance_type: Type) -> Callable[ServiceABC]:
|
||||||
"""
|
"""
|
||||||
@ -70,12 +22,3 @@ class ServiceProviderABC(ABC):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def remove_service(self, instance_type: type):
|
|
||||||
"""
|
|
||||||
Removes service
|
|
||||||
:param instance_type:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
122
src/cpl/dependency_injection/service_provider_old.py
Normal file
122
src/cpl/dependency_injection/service_provider_old.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
from collections import Callable
|
||||||
|
from inspect import signature, Parameter
|
||||||
|
from typing import Type, Optional, Union
|
||||||
|
|
||||||
|
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
|
||||||
|
from cpl.configuration.configuration_abc import ConfigurationABC
|
||||||
|
from cpl.configuration.configuration_model_abc import ConfigurationModelABC
|
||||||
|
from cpl.database.context.database_context_abc import DatabaseContextABC
|
||||||
|
from cpl.dependency_injection.service_abc import ServiceABC
|
||||||
|
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
|
||||||
|
from cpl.environment.environment_abc import ApplicationEnvironmentABC
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceProvider(ServiceProviderABC):
|
||||||
|
|
||||||
|
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC):
|
||||||
|
"""
|
||||||
|
Service for service providing
|
||||||
|
:param runtime:
|
||||||
|
"""
|
||||||
|
ServiceProviderABC.__init__(self)
|
||||||
|
self._configuration: ConfigurationABC = config
|
||||||
|
self._runtime: ApplicationRuntimeABC = runtime
|
||||||
|
self._database_context: Optional[DatabaseContextABC] = None
|
||||||
|
|
||||||
|
self._transient_services: dict[Type[ServiceABC], Callable[ServiceABC]] = {}
|
||||||
|
self._scoped_services: dict[Type[ServiceABC], Callable[ServiceABC]] = {}
|
||||||
|
self._singleton_services: dict[Type[ServiceABC], Callable[ServiceABC], ServiceABC] = {}
|
||||||
|
|
||||||
|
def _create_instance(self, service: Union[Callable[ServiceABC], ServiceABC]) -> Callable[ServiceABC]:
|
||||||
|
"""
|
||||||
|
Creates an instance of given type
|
||||||
|
:param service:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
sig = signature(service.__init__)
|
||||||
|
params = []
|
||||||
|
for param in sig.parameters.items():
|
||||||
|
parameter = param[1]
|
||||||
|
if parameter.name != 'self' and parameter.annotation != Parameter.empty:
|
||||||
|
if issubclass(parameter.annotation, ApplicationRuntimeABC):
|
||||||
|
params.append(self._runtime)
|
||||||
|
|
||||||
|
elif issubclass(parameter.annotation, ApplicationEnvironmentABC):
|
||||||
|
params.append(self._configuration.environment)
|
||||||
|
|
||||||
|
elif issubclass(parameter.annotation, DatabaseContextABC):
|
||||||
|
params.append(self._database_context)
|
||||||
|
|
||||||
|
elif issubclass(parameter.annotation, ConfigurationModelABC):
|
||||||
|
params.append(self._configuration.get_configuration(parameter.annotation))
|
||||||
|
|
||||||
|
elif issubclass(parameter.annotation, ConfigurationABC):
|
||||||
|
params.append(self._configuration)
|
||||||
|
|
||||||
|
elif issubclass(parameter.annotation, ServiceProviderABC):
|
||||||
|
params.append(self)
|
||||||
|
|
||||||
|
else:
|
||||||
|
params.append(self.get_service(parameter.annotation))
|
||||||
|
|
||||||
|
return service(*params)
|
||||||
|
|
||||||
|
def add_db_context(self, db_context: Type[DatabaseContextABC]):
|
||||||
|
self._database_context = self._create_instance(db_context)
|
||||||
|
|
||||||
|
def get_db_context(self) -> Callable[DatabaseContextABC]:
|
||||||
|
return self._database_context
|
||||||
|
|
||||||
|
def add_transient(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None):
|
||||||
|
if service is None:
|
||||||
|
self._transient_services[service_type] = service_type
|
||||||
|
else:
|
||||||
|
self._transient_services[service_type] = service
|
||||||
|
|
||||||
|
def add_scoped(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None):
|
||||||
|
if service is None:
|
||||||
|
self._scoped_services[service_type] = service_type
|
||||||
|
else:
|
||||||
|
self._scoped_services[service_type] = service
|
||||||
|
|
||||||
|
def add_singleton(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None):
|
||||||
|
for known_service in self._singleton_services:
|
||||||
|
if type(known_service) == service_type:
|
||||||
|
raise Exception(f'Service with type {service_type} already exists')
|
||||||
|
|
||||||
|
if service is None:
|
||||||
|
self._singleton_services[service_type] = self._create_instance(service_type)
|
||||||
|
else:
|
||||||
|
self._singleton_services[service_type] = self._create_instance(service)
|
||||||
|
|
||||||
|
def get_service(self, instance_type: Type) -> Callable[ServiceABC]:
|
||||||
|
if issubclass(instance_type, ServiceProviderABC):
|
||||||
|
return self
|
||||||
|
|
||||||
|
for service in self._transient_services:
|
||||||
|
if service == instance_type and isinstance(self._transient_services[service], type(instance_type)):
|
||||||
|
return self._create_instance(self._transient_services[service])
|
||||||
|
|
||||||
|
for service in self._scoped_services:
|
||||||
|
if service == instance_type and isinstance(self._scoped_services[service], type(instance_type)):
|
||||||
|
return self._create_instance(self._scoped_services[service])
|
||||||
|
|
||||||
|
for service in self._singleton_services:
|
||||||
|
if service == instance_type and isinstance(self._singleton_services[service], instance_type):
|
||||||
|
return self._singleton_services[service]
|
||||||
|
|
||||||
|
def remove_service(self, instance_type: Type[ServiceABC]):
|
||||||
|
for service in self._transient_services:
|
||||||
|
if service == instance_type and isinstance(self._transient_services[service], type(instance_type)):
|
||||||
|
del self._transient_services[service]
|
||||||
|
return
|
||||||
|
|
||||||
|
for service in self._scoped_services:
|
||||||
|
if service == instance_type and isinstance(self._scoped_services[service], type(instance_type)):
|
||||||
|
del self._scoped_services[service]
|
||||||
|
return
|
||||||
|
|
||||||
|
for service in self._singleton_services:
|
||||||
|
if service == instance_type and isinstance(self._singleton_services[service], instance_type):
|
||||||
|
del self._singleton_services[service]
|
||||||
|
return
|
@ -3,6 +3,7 @@ from cpl.application.startup_abc import StartupABC
|
|||||||
from cpl.configuration.configuration_abc import ConfigurationABC
|
from cpl.configuration.configuration_abc import ConfigurationABC
|
||||||
from cpl.database.context.database_context import DatabaseContext
|
from cpl.database.context.database_context import DatabaseContext
|
||||||
from cpl.database.database_settings import DatabaseSettings
|
from cpl.database.database_settings import DatabaseSettings
|
||||||
|
from cpl.dependency_injection.service_collection_abc import ServiceCollectionABC
|
||||||
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
|
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
|
||||||
from cpl.logging.logger_service import Logger
|
from cpl.logging.logger_service import Logger
|
||||||
from cpl.logging.logger_abc import LoggerABC
|
from cpl.logging.logger_abc import LoggerABC
|
||||||
@ -13,12 +14,13 @@ from cpl.utils.credential_manager import CredentialManager
|
|||||||
|
|
||||||
class Startup(StartupABC):
|
class Startup(StartupABC):
|
||||||
|
|
||||||
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC, services: ServiceProviderABC):
|
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC, services: ServiceCollectionABC):
|
||||||
StartupABC.__init__(self)
|
StartupABC.__init__(self)
|
||||||
|
|
||||||
self._configuration = config
|
self._configuration = config
|
||||||
self._application_runtime = runtime
|
self._application_runtime = runtime
|
||||||
self._services = services
|
self._services = services
|
||||||
|
print(self._services)
|
||||||
|
|
||||||
def configure_configuration(self) -> ConfigurationABC:
|
def configure_configuration(self) -> ConfigurationABC:
|
||||||
self._configuration.add_environment_variables('PYTHON_')
|
self._configuration.add_environment_variables('PYTHON_')
|
||||||
@ -32,12 +34,11 @@ class Startup(StartupABC):
|
|||||||
|
|
||||||
def configure_services(self) -> ServiceProviderABC:
|
def configure_services(self) -> ServiceProviderABC:
|
||||||
# Create and connect to database
|
# Create and connect to database
|
||||||
db_settings: DatabaseSettings = self._configuration.get_configuration(DatabaseSettings)
|
# db_settings: DatabaseSettings = self._configuration.get_configuration(DatabaseSettings)
|
||||||
self._services.add_db_context(DatabaseContext)
|
# self._services.add_db_context(DatabaseContext)
|
||||||
db: DatabaseContext = self._services.get_db_context()
|
# db.connect(CredentialManager.build_string(db_settings.connection_string, db_settings.credentials))
|
||||||
db.connect(CredentialManager.build_string(db_settings.connection_string, db_settings.credentials))
|
|
||||||
|
|
||||||
self._services.add_singleton(LoggerABC, Logger)
|
self._services.add_singleton(LoggerABC, Logger)
|
||||||
self._services.add_singleton(EMailClientABC, EMailClient)
|
self._services.add_singleton(EMailClientABC, EMailClient)
|
||||||
|
|
||||||
return self._services
|
return self._services.build_service_provider()
|
||||||
|
Loading…
Reference in New Issue
Block a user