New implementation of scopes #186
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
from cpl.application.abc import ApplicationABC
|
from cpl.application.abc import ApplicationABC
|
||||||
from cpl.core.console.console import Console
|
from cpl.core.console.console import Console
|
||||||
from cpl.dependency import ServiceProvider
|
from cpl.dependency import ServiceProvider
|
||||||
from cpl.dependency.scope import Scope
|
|
||||||
from di.static_test import StaticTest
|
from di.static_test import StaticTest
|
||||||
from di.test_abc import TestABC
|
from di.test_abc import TestABC
|
||||||
from di.test_service import TestService
|
from di.test_service import TestService
|
||||||
@@ -17,26 +16,30 @@ class Application(ApplicationABC):
|
|||||||
ts: TestService = self._services.get_service(TestService)
|
ts: TestService = self._services.get_service(TestService)
|
||||||
ts.run()
|
ts.run()
|
||||||
|
|
||||||
def configure(self): ...
|
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
with self._services.create_scope() as scope:
|
with self._services.create_scope() as scope:
|
||||||
Console.write_line("Scope1")
|
Console.write_line("Scope1")
|
||||||
ts: TestService = scope.service_provider.get_service(TestService)
|
ts: TestService = scope.get_service(TestService)
|
||||||
ts.run()
|
ts.run()
|
||||||
dit: DITesterService = scope.service_provider.get_service(DITesterService)
|
dit: DITesterService = scope.get_service(DITesterService)
|
||||||
dit.run()
|
dit.run()
|
||||||
|
|
||||||
|
if ts.name != dit.name:
|
||||||
|
raise Exception("DI is broken!")
|
||||||
|
|
||||||
with self._services.create_scope() as scope:
|
with self._services.create_scope() as scope:
|
||||||
Console.write_line("Scope2")
|
Console.write_line("Scope2")
|
||||||
ts: TestService = scope.service_provider.get_service(TestService)
|
ts: TestService = scope.get_service(TestService)
|
||||||
ts.run()
|
ts.run()
|
||||||
dit: DITesterService = scope.service_provider.get_service(DITesterService)
|
dit: DITesterService = scope.get_service(DITesterService)
|
||||||
dit.run()
|
dit.run()
|
||||||
|
|
||||||
|
if ts.name != dit.name:
|
||||||
|
raise Exception("DI is broken!")
|
||||||
|
|
||||||
Console.write_line("Global")
|
Console.write_line("Global")
|
||||||
self._part_of_scoped()
|
self._part_of_scoped()
|
||||||
StaticTest.test()
|
StaticTest.test()
|
||||||
|
|
||||||
self._services.get_service(Tester)
|
self._services.get_service(Tester)
|
||||||
Console.write_line(self._services.get_services(list[TestABC]))
|
Console.write_line(self._services.get_services(TestABC))
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ class DITesterService:
|
|||||||
def __init__(self, ts: TestService):
|
def __init__(self, ts: TestService):
|
||||||
self._ts = ts
|
self._ts = ts
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._ts.name
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
Console.write_line("DIT: ")
|
Console.write_line("DIT: ")
|
||||||
self._ts.run()
|
self._ts.run()
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ class Startup(StartupABC):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
StartupABC.__init__(self)
|
StartupABC.__init__(self)
|
||||||
|
|
||||||
def configure_configuration(self): ...
|
@staticmethod
|
||||||
|
def configure_configuration(): ...
|
||||||
|
|
||||||
def configure_services(self, services: ServiceCollection) -> ServiceProvider:
|
@staticmethod
|
||||||
|
def configure_services(services: ServiceCollection) -> ServiceProvider:
|
||||||
services.add_scoped(TestService)
|
services.add_scoped(TestService)
|
||||||
services.add_scoped(DITesterService)
|
services.add_scoped(DITesterService)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from di.test_abc import TestABC
|
|||||||
|
|
||||||
class Test1Service(TestABC):
|
class Test1Service(TestABC):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
TestABC.__init__(self, String.random_string(string.ascii_lowercase, 8))
|
TestABC.__init__(self, String.random(8))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
Console.write_line(f"Im {self._name}")
|
Console.write_line(f"Im {self._name}")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from di.test_abc import TestABC
|
|||||||
|
|
||||||
class Test2Service(TestABC):
|
class Test2Service(TestABC):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
TestABC.__init__(self, String.random_string(string.ascii_lowercase, 8))
|
TestABC.__init__(self, String.random(8))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
Console.write_line(f"Im {self._name}")
|
Console.write_line(f"Im {self._name}")
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import string
|
|
||||||
|
|
||||||
from cpl.core.console.console import Console
|
from cpl.core.console.console import Console
|
||||||
from cpl.core.utils.string import String
|
from cpl.core.utils.string import String
|
||||||
|
|
||||||
@@ -8,5 +6,9 @@ class TestService:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._name = String.random(8)
|
self._name = String.random(8)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._name
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
Console.write_line(f"Im {self._name}")
|
Console.write_line(f"Im {self._name}")
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ from cpl.core.environment import Environment
|
|||||||
from cpl.core.log import LoggerABC
|
from cpl.core.log import LoggerABC
|
||||||
from cpl.core.pipes import IPAddressPipe
|
from cpl.core.pipes import IPAddressPipe
|
||||||
from cpl.mail import EMail, EMailClientABC
|
from cpl.mail import EMail, EMailClientABC
|
||||||
from cpl.query.extension.list import List
|
from cpl.query import List
|
||||||
|
from general.scoped_service import ScopedService
|
||||||
from test_service import TestService
|
from test_service import TestService
|
||||||
from test_settings import TestSettings
|
from test_settings import TestSettings
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ class Application(ApplicationABC):
|
|||||||
def main(self):
|
def main(self):
|
||||||
self._logger.debug(f"Host: {Environment.get_host_name()}")
|
self._logger.debug(f"Host: {Environment.get_host_name()}")
|
||||||
self._logger.debug(f"Environment: {Environment.get_environment()}")
|
self._logger.debug(f"Environment: {Environment.get_environment()}")
|
||||||
Console.write_line(List(int, range(0, 10)).select(lambda x: f"x={x}").to_list())
|
Console.write_line(List(range(0, 10)).select(lambda x: f"x={x}").to_list())
|
||||||
Console.spinner("Test", self._wait, 2, spinner_foreground_color="red")
|
Console.spinner("Test", self._wait, 2, spinner_foreground_color="red")
|
||||||
test: TestService = self._services.get_service(TestService)
|
test: TestService = self._services.get_service(TestService)
|
||||||
ip_pipe: IPAddressPipe = self._services.get_service(IPAddressPipe)
|
ip_pipe: IPAddressPipe = self._services.get_service(IPAddressPipe)
|
||||||
@@ -48,10 +49,21 @@ class Application(ApplicationABC):
|
|||||||
Console.write_line(f"DI working: {test == test2 and ip_pipe != ip_pipe2}")
|
Console.write_line(f"DI working: {test == test2 and ip_pipe != ip_pipe2}")
|
||||||
Console.write_line(self._services.get_service(LoggerABC))
|
Console.write_line(self._services.get_service(LoggerABC))
|
||||||
|
|
||||||
scope = self._services.create_scope()
|
root_scoped_service = self._services.get_service(ScopedService)
|
||||||
Console.write_line("scope", scope)
|
with self._services.create_scope() as scope:
|
||||||
with self._services.create_scope() as s:
|
s_srvc1 = scope.get_service(ScopedService)
|
||||||
Console.write_line("with scope", s)
|
s_srvc2 = scope.get_service(ScopedService)
|
||||||
|
|
||||||
|
Console.write_line(root_scoped_service)
|
||||||
|
Console.write_line(s_srvc1)
|
||||||
|
Console.write_line(s_srvc2)
|
||||||
|
if root_scoped_service == s_srvc1 or s_srvc1 != s_srvc2:
|
||||||
|
raise Exception("Root scoped service should not be equal to scoped service")
|
||||||
|
|
||||||
|
root_scoped_service2 = self._services.get_service(ScopedService)
|
||||||
|
Console.write_line(root_scoped_service2)
|
||||||
|
if root_scoped_service != root_scoped_service2:
|
||||||
|
raise Exception("Root scoped service should be equal to root scoped service 2")
|
||||||
|
|
||||||
test_settings = Configuration.get(TestSettings)
|
test_settings = Configuration.get(TestSettings)
|
||||||
Console.write_line(test_settings.value)
|
Console.write_line(test_settings.value)
|
||||||
|
|||||||
10
example/custom/general/src/general/scoped_service.py
Normal file
10
example/custom/general/src/general/scoped_service.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
|
class ScopedService:
|
||||||
|
def __init__(self):
|
||||||
|
self.value = "I am a scoped service"
|
||||||
|
Console.write_line(self.value, self)
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.value
|
||||||
@@ -4,6 +4,7 @@ from cpl.core.configuration import Configuration
|
|||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.core.pipes import IPAddressPipe
|
from cpl.core.pipes import IPAddressPipe
|
||||||
from cpl.dependency import ServiceCollection
|
from cpl.dependency import ServiceCollection
|
||||||
|
from general.scoped_service import ScopedService
|
||||||
from test_service import TestService
|
from test_service import TestService
|
||||||
|
|
||||||
|
|
||||||
@@ -21,3 +22,4 @@ class Startup(StartupABC):
|
|||||||
services.add_module(mail)
|
services.add_module(mail)
|
||||||
services.add_transient(IPAddressPipe)
|
services.add_transient(IPAddressPipe)
|
||||||
services.add_singleton(TestService)
|
services.add_singleton(TestService)
|
||||||
|
services.add_scoped(ScopedService)
|
||||||
|
|||||||
13
src/cpl-api/cpl/api/middleware/_scope_middleware.py
Normal file
13
src/cpl-api/cpl/api/middleware/_scope_middleware.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from cpl.api.abc import ASGIMiddleware
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
|
class ScopeMiddleware(ASGIMiddleware):
|
||||||
|
def __init__(self, app, provider: ServiceProvider):
|
||||||
|
ASGIMiddleware.__init__(self, app)
|
||||||
|
self._app = app
|
||||||
|
self._provider = provider
|
||||||
|
|
||||||
|
async def __call__(self, scope, receive, send):
|
||||||
|
with self._provider.create_scope():
|
||||||
|
await self._app(scope, receive, send)
|
||||||
@@ -114,12 +114,15 @@ class String:
|
|||||||
|
|
||||||
characters = []
|
characters = []
|
||||||
if letters:
|
if letters:
|
||||||
characters.append(string.ascii_letters)
|
characters.extend(string.ascii_letters)
|
||||||
|
|
||||||
if digits:
|
if digits:
|
||||||
characters.append(string.digits)
|
characters.extend(string.digits)
|
||||||
|
|
||||||
if special_characters:
|
if special_characters:
|
||||||
characters.append(string.punctuation)
|
characters.extend(string.punctuation)
|
||||||
|
|
||||||
return "".join(random.choice(characters) for _ in range(length)) if characters else ""
|
x = "".join(random.choice(list(characters)) for _ in range(length)) if characters else ""
|
||||||
|
if len(x) != length:
|
||||||
|
raise Exception("No characters selected to generate random string")
|
||||||
|
return x
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import copy
|
||||||
import typing
|
import typing
|
||||||
|
from contextlib import contextmanager
|
||||||
from inspect import signature, Parameter, Signature
|
from inspect import signature, Parameter, Signature
|
||||||
from typing import Optional, Type
|
from typing import Optional, Type
|
||||||
|
|
||||||
@@ -6,6 +8,7 @@ from cpl.core.configuration import Configuration
|
|||||||
from cpl.core.configuration.configuration_model_abc import ConfigurationModelABC
|
from cpl.core.configuration.configuration_model_abc import ConfigurationModelABC
|
||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.core.typing import T, R, Source
|
from cpl.core.typing import T, R, Source
|
||||||
|
from cpl.dependency import use_provider
|
||||||
from cpl.dependency.service_descriptor import ServiceDescriptor
|
from cpl.dependency.service_descriptor import ServiceDescriptor
|
||||||
from cpl.dependency.service_lifetime import ServiceLifetimeEnum
|
from cpl.dependency.service_lifetime import ServiceLifetimeEnum
|
||||||
|
|
||||||
@@ -132,6 +135,19 @@ class ServiceProvider:
|
|||||||
|
|
||||||
return service_type(*params, *args, **kwargs)
|
return service_type(*params, *args, **kwargs)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def create_scope(self):
|
||||||
|
scoped_descriptors = []
|
||||||
|
for d in self._service_descriptors:
|
||||||
|
if d.lifetime == ServiceLifetimeEnum.singleton:
|
||||||
|
scoped_descriptors.append(d)
|
||||||
|
else:
|
||||||
|
scoped_descriptors.append(copy.deepcopy(d))
|
||||||
|
|
||||||
|
scoped_provider = ServiceProvider(scoped_descriptors)
|
||||||
|
with use_provider(scoped_provider):
|
||||||
|
yield scoped_provider
|
||||||
|
|
||||||
def get_service(self, service_type: T, *args, **kwargs) -> Optional[R]:
|
def get_service(self, service_type: T, *args, **kwargs) -> Optional[R]:
|
||||||
result = self._find_service(service_type)
|
result = self._find_service(service_type)
|
||||||
|
|
||||||
@@ -143,7 +159,7 @@ class ServiceProvider:
|
|||||||
|
|
||||||
implementation = self._build_service(service_type, *args, **kwargs)
|
implementation = self._build_service(service_type, *args, **kwargs)
|
||||||
if (
|
if (
|
||||||
result.lifetime == ServiceLifetimeEnum.singleton
|
result.lifetime in (ServiceLifetimeEnum.singleton, ServiceLifetimeEnum.scoped)
|
||||||
):
|
):
|
||||||
result.implementation = implementation
|
result.implementation = implementation
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user