Added logic to support global service provider #148
This commit is contained in:
parent
c09f2f8e83
commit
d600852bec
@ -268,7 +268,7 @@ class Configuration(ConfigurationABC):
|
|||||||
configuration.from_dict(value)
|
configuration.from_dict(value)
|
||||||
self.add_configuration(sub, configuration)
|
self.add_configuration(sub, configuration)
|
||||||
|
|
||||||
def add_configuration(self, key_type: Union[str, type], value: any):
|
def add_configuration(self, key_type: T, value: any):
|
||||||
self._config[key_type] = value
|
self._config[key_type] = value
|
||||||
|
|
||||||
def create_console_argument(self, arg_type: ArgumentTypeEnum, token: str, name: str, aliases: list[str],
|
def create_console_argument(self, arg_type: ArgumentTypeEnum, token: str, name: str, aliases: list[str],
|
||||||
|
@ -76,7 +76,7 @@ class ConfigurationABC(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def add_configuration(self, key_type: Union[str, type], value: any):
|
def add_configuration(self, key_type: T, value: any):
|
||||||
r"""Add configuration object
|
r"""Add configuration object
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
@ -126,7 +126,7 @@ class ConfigurationABC(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_configuration(self, search_type: Union[str, Type[ConfigurationModelABC]]) -> Optional[T]:
|
def get_configuration(self, search_type: Type[T]) -> Optional[T]:
|
||||||
r"""Returns value from configuration by given type
|
r"""Returns value from configuration by given type
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
|
@ -11,6 +11,7 @@ from cpl_core.dependency_injection.service_provider_abc import ServiceProviderAB
|
|||||||
from cpl_core.logging.logger_abc import LoggerABC
|
from cpl_core.logging.logger_abc import LoggerABC
|
||||||
from cpl_core.logging.logger_service import Logger
|
from cpl_core.logging.logger_service import Logger
|
||||||
from cpl_core.pipes.pipe_abc import PipeABC
|
from cpl_core.pipes.pipe_abc import PipeABC
|
||||||
|
from cpl_core.type import T
|
||||||
|
|
||||||
|
|
||||||
class ServiceCollection(ServiceCollectionABC):
|
class ServiceCollection(ServiceCollectionABC):
|
||||||
@ -53,22 +54,26 @@ class ServiceCollection(ServiceCollectionABC):
|
|||||||
|
|
||||||
def add_logging(self):
|
def add_logging(self):
|
||||||
self.add_singleton(LoggerABC, Logger)
|
self.add_singleton(LoggerABC, Logger)
|
||||||
|
return self
|
||||||
|
|
||||||
def add_pipes(self):
|
def add_pipes(self):
|
||||||
for pipe in PipeABC.__subclasses__():
|
for pipe in PipeABC.__subclasses__():
|
||||||
self.add_transient(PipeABC, pipe)
|
self.add_transient(PipeABC, pipe)
|
||||||
|
return self
|
||||||
|
|
||||||
def add_singleton(self, service_type: Union[type, object], service: Union[type, object] = None):
|
def add_singleton(self, service_type: T, service: T = None):
|
||||||
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.singleton, service)
|
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.singleton, service)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_scoped(self, service_type: Type, service: Callable = None):
|
def add_scoped(self, service_type: T, service: Callable = None):
|
||||||
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.scoped, service)
|
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.scoped, service)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_transient(self, service_type: type, service: type = None):
|
def add_transient(self, service_type: T, service: T = None):
|
||||||
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.transient, service)
|
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.transient, service)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def build_service_provider(self) -> ServiceProviderABC:
|
def build_service_provider(self) -> ServiceProviderABC:
|
||||||
return ServiceProvider(self._service_descriptors, self._configuration, self._database_context)
|
sp = ServiceProvider(self._service_descriptors, self._configuration, self._database_context)
|
||||||
|
ServiceProviderABC.set_global_provider(sp)
|
||||||
|
return sp
|
||||||
|
@ -5,6 +5,7 @@ from typing import Type
|
|||||||
from cpl_core.database.database_settings import DatabaseSettings
|
from cpl_core.database.database_settings import DatabaseSettings
|
||||||
from cpl_core.database.context.database_context_abc import DatabaseContextABC
|
from cpl_core.database.context.database_context_abc import DatabaseContextABC
|
||||||
from cpl_core.dependency_injection.service_provider_abc import ServiceProviderABC
|
from cpl_core.dependency_injection.service_provider_abc import ServiceProviderABC
|
||||||
|
from cpl_core.type import T
|
||||||
|
|
||||||
|
|
||||||
class ServiceCollectionABC(ABC):
|
class ServiceCollectionABC(ABC):
|
||||||
@ -46,7 +47,7 @@ class ServiceCollectionABC(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def add_transient(self, service_type: Type, service: Callable = None) -> 'ServiceCollectionABC':
|
def add_transient(self, service_type: T, service: T = None) -> 'ServiceCollectionABC':
|
||||||
r"""Adds a service with transient lifetime
|
r"""Adds a service with transient lifetime
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
@ -63,7 +64,7 @@ class ServiceCollectionABC(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def add_scoped(self, service_type: Type, service: Callable = None) -> 'ServiceCollectionABC':
|
def add_scoped(self, service_type: T, service: T = None) -> 'ServiceCollectionABC':
|
||||||
r"""Adds a service with scoped lifetime
|
r"""Adds a service with scoped lifetime
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
@ -80,7 +81,7 @@ class ServiceCollectionABC(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def add_singleton(self, service_type: Type, service: Callable = None) -> 'ServiceCollectionABC':
|
def add_singleton(self, service_type: T, service: T = None) -> 'ServiceCollectionABC':
|
||||||
r"""Adds a service with singleton lifetime
|
r"""Adds a service with singleton lifetime
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import copy
|
import copy
|
||||||
from inspect import signature, Parameter
|
import functools
|
||||||
from typing import Optional
|
from inspect import signature, Parameter, Signature
|
||||||
|
from typing import Optional, Callable
|
||||||
|
|
||||||
from cpl_core.configuration.configuration_abc import ConfigurationABC
|
from cpl_core.configuration.configuration_abc import ConfigurationABC
|
||||||
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||||
@ -12,6 +13,7 @@ from cpl_core.dependency_injection.service_provider_abc import ServiceProviderAB
|
|||||||
from cpl_core.dependency_injection.service_descriptor import ServiceDescriptor
|
from cpl_core.dependency_injection.service_descriptor import ServiceDescriptor
|
||||||
from cpl_core.dependency_injection.service_lifetime_enum import ServiceLifetimeEnum
|
from cpl_core.dependency_injection.service_lifetime_enum import ServiceLifetimeEnum
|
||||||
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
|
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
|
||||||
|
from cpl_core.type import T
|
||||||
|
|
||||||
|
|
||||||
class ServiceProvider(ServiceProviderABC):
|
class ServiceProvider(ServiceProviderABC):
|
||||||
@ -54,17 +56,9 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
|
|
||||||
return implementation
|
return implementation
|
||||||
|
|
||||||
def build_service(self, service_type: type) -> object:
|
# raise Exception(f'Service {parameter.annotation} not found')
|
||||||
for descriptor in self._service_descriptors:
|
|
||||||
if descriptor.service_type == service_type or issubclass(descriptor.service_type, service_type):
|
|
||||||
if descriptor.implementation is not None:
|
|
||||||
service_type = type(descriptor.implementation)
|
|
||||||
else:
|
|
||||||
service_type = descriptor.service_type
|
|
||||||
|
|
||||||
break
|
def build_by_signature(self, sig: Signature) -> list[T]:
|
||||||
|
|
||||||
sig = signature(service_type.__init__)
|
|
||||||
params = []
|
params = []
|
||||||
for param in sig.parameters.items():
|
for param in sig.parameters.items():
|
||||||
parameter = param[1]
|
parameter = param[1]
|
||||||
@ -87,6 +81,21 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
else:
|
else:
|
||||||
params.append(self._get_service(parameter))
|
params.append(self._get_service(parameter))
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
def build_service(self, service_type: T) -> object:
|
||||||
|
for descriptor in self._service_descriptors:
|
||||||
|
if descriptor.service_type == service_type or issubclass(descriptor.service_type, service_type):
|
||||||
|
if descriptor.implementation is not None:
|
||||||
|
service_type = type(descriptor.implementation)
|
||||||
|
else:
|
||||||
|
service_type = descriptor.service_type
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
sig = signature(service_type.__init__)
|
||||||
|
params = self.build_by_signature(sig)
|
||||||
|
|
||||||
return service_type(*params)
|
return service_type(*params)
|
||||||
|
|
||||||
def set_scope(self, scope: ScopeABC):
|
def set_scope(self, scope: ScopeABC):
|
||||||
@ -96,7 +105,7 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
sb = ScopeBuilder(ServiceProvider(copy.deepcopy(self._service_descriptors), self._configuration, self._database_context))
|
sb = ScopeBuilder(ServiceProvider(copy.deepcopy(self._service_descriptors), self._configuration, self._database_context))
|
||||||
return sb.build()
|
return sb.build()
|
||||||
|
|
||||||
def get_service(self, service_type: type) -> Optional[object]:
|
def get_service(self, service_type: T) -> Optional[T]:
|
||||||
result = self._find_service(service_type)
|
result = self._find_service(service_type)
|
||||||
|
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -110,3 +119,18 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
result.implementation = implementation
|
result.implementation = implementation
|
||||||
|
|
||||||
return implementation
|
return implementation
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def inject(cls, f=None):
|
||||||
|
if f is None:
|
||||||
|
return functools.partial(cls.inject)
|
||||||
|
|
||||||
|
@functools.wraps(f)
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
if cls._provider is None:
|
||||||
|
raise Exception(f'{cls.__name__} not build!')
|
||||||
|
|
||||||
|
injection = cls._provider.build_by_signature(signature(f))
|
||||||
|
return f(*injection, *args, **kwargs)
|
||||||
|
|
||||||
|
return inner
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
|
from inspect import Signature
|
||||||
from typing import Type, Optional
|
from typing import Type, Optional
|
||||||
|
|
||||||
|
|
||||||
from cpl_core.dependency_injection.scope_abc import ScopeABC
|
from cpl_core.dependency_injection.scope_abc import ScopeABC
|
||||||
|
from cpl_core.type import T
|
||||||
|
|
||||||
|
|
||||||
class ServiceProviderABC(ABC):
|
class ServiceProviderABC(ABC):
|
||||||
r"""ABC for the class :class:`cpl_core.dependency_injection.service_provider.ServiceProvider`"""
|
r"""ABC for the class :class:`cpl_core.dependency_injection.service_provider.ServiceProvider`"""
|
||||||
|
|
||||||
@abstractmethod
|
_provider: Optional['ServiceProviderABC'] = None
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def build_service(self, service_type: Type) -> object:
|
def __init__(self): pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_global_provider(cls, provider: 'ServiceProviderABC'):
|
||||||
|
cls._provider = provider
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def build_by_signature(self, sig: Signature) -> list[T]: pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def build_service(self, service_type: T) -> object:
|
||||||
r"""Creates instance of given type
|
r"""Creates instance of given type
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
@ -48,7 +59,7 @@ class ServiceProviderABC(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_service(self, instance_type: Type) -> Optional[object]:
|
def get_service(self, instance_type: T) -> Optional[T]:
|
||||||
r"""Returns instance of given type
|
r"""Returns instance of given type
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
@ -61,3 +72,8 @@ class ServiceProviderABC(ABC):
|
|||||||
Object of type Optional[Callable[:class:`object`]]
|
Object of type Optional[Callable[:class:`object`]]
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# @classmethod
|
||||||
|
# @abstractmethod
|
||||||
|
# def inject(cls):
|
||||||
|
# pass
|
||||||
|
@ -3,8 +3,9 @@ from cpl_core.configuration import ConfigurationABC
|
|||||||
from cpl_core.console.console import Console
|
from cpl_core.console.console import Console
|
||||||
from cpl_core.dependency_injection import ServiceProviderABC
|
from cpl_core.dependency_injection import ServiceProviderABC
|
||||||
from cpl_core.dependency_injection.scope import Scope
|
from cpl_core.dependency_injection.scope import Scope
|
||||||
from test_service_service import TestService
|
from di.static_test import StaticTest
|
||||||
from di_tester_service import DITesterService
|
from di.test_service_service import TestService
|
||||||
|
from di.di_tester_service import DITesterService
|
||||||
|
|
||||||
|
|
||||||
class Application(ApplicationABC):
|
class Application(ApplicationABC):
|
||||||
@ -40,3 +41,6 @@ class Application(ApplicationABC):
|
|||||||
|
|
||||||
Console.write_line('Global')
|
Console.write_line('Global')
|
||||||
self._part_of_scoped()
|
self._part_of_scoped()
|
||||||
|
StaticTest.test()
|
||||||
|
with self._services.create_scope() as scope:
|
||||||
|
StaticTest.test()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from cpl_core.console.console import Console
|
from cpl_core.console.console import Console
|
||||||
from test_service_service import TestService
|
from di.test_service_service import TestService
|
||||||
|
|
||||||
|
|
||||||
class DITesterService:
|
class DITesterService:
|
||||||
|
@ -2,8 +2,8 @@ from cpl_core.application import StartupABC
|
|||||||
from cpl_core.configuration import ConfigurationABC
|
from cpl_core.configuration import ConfigurationABC
|
||||||
from cpl_core.dependency_injection import ServiceProviderABC, ServiceCollectionABC
|
from cpl_core.dependency_injection import ServiceProviderABC, ServiceCollectionABC
|
||||||
from cpl_core.environment import ApplicationEnvironment
|
from cpl_core.environment import ApplicationEnvironment
|
||||||
from test_service_service import TestService
|
from di.test_service_service import TestService
|
||||||
from di_tester_service import DITesterService
|
from di.di_tester_service import DITesterService
|
||||||
|
|
||||||
|
|
||||||
class Startup(StartupABC):
|
class Startup(StartupABC):
|
||||||
|
11
tests/custom/di/src/di/static_test.py
Normal file
11
tests/custom/di/src/di/static_test.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from cpl_core.configuration import ConfigurationABC
|
||||||
|
from cpl_core.dependency_injection import ServiceProvider, ServiceProviderABC
|
||||||
|
from di.test_service_service import TestService
|
||||||
|
|
||||||
|
|
||||||
|
class StaticTest:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@ServiceProvider.inject
|
||||||
|
def test(services: ServiceProviderABC, config: ConfigurationABC, t1: TestService):
|
||||||
|
t1.run()
|
Loading…
Reference in New Issue
Block a user