Compare commits
9 Commits
2025.09.24
...
2025.09.24
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bb264b8c1 | |||
| 75417966eb | |||
| 15d3c59f02 | |||
| 6a3fdb3ebd | |||
| b49f663ae0 | |||
| 287f5e3149 | |||
| 4c8cd988cc | |||
| cdb4a0fb34 | |||
| cf8edafd39 |
@@ -6,8 +6,10 @@ from cpl.application import ApplicationBuilder
|
|||||||
from cpl.auth.permission.permissions import Permissions
|
from cpl.auth.permission.permissions import Permissions
|
||||||
from cpl.auth.schema import AuthUser, Role
|
from cpl.auth.schema import AuthUser, Role
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
|
from cpl.core.console import Console
|
||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.core.utils.cache import Cache
|
from cpl.core.utils.cache import Cache
|
||||||
|
from scoped_service import ScopedService
|
||||||
from service import PingService
|
from service import PingService
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +25,8 @@ def main():
|
|||||||
builder.services.add_transient(PingService)
|
builder.services.add_transient(PingService)
|
||||||
builder.services.add_module(api)
|
builder.services.add_module(api)
|
||||||
|
|
||||||
|
builder.services.add_scoped(ScopedService)
|
||||||
|
|
||||||
builder.services.add_cache(AuthUser)
|
builder.services.add_cache(AuthUser)
|
||||||
builder.services.add_cache(Role)
|
builder.services.add_cache(Role)
|
||||||
|
|
||||||
@@ -40,6 +44,32 @@ def main():
|
|||||||
user_cache = provider.get_service(Cache[AuthUser])
|
user_cache = provider.get_service(Cache[AuthUser])
|
||||||
role_cache = provider.get_service(Cache[Role])
|
role_cache = provider.get_service(Cache[Role])
|
||||||
|
|
||||||
|
if role_cache == user_cache:
|
||||||
|
raise Exception("Cache service is not working")
|
||||||
|
|
||||||
|
s1 = provider.get_service(ScopedService)
|
||||||
|
s2 = provider.get_service(ScopedService)
|
||||||
|
|
||||||
|
if s1.name == s2.name:
|
||||||
|
raise Exception("Scoped service is not working")
|
||||||
|
|
||||||
|
with provider.create_scope() as scope:
|
||||||
|
s3 = scope.get_service(ScopedService)
|
||||||
|
s4 = scope.get_service(ScopedService)
|
||||||
|
|
||||||
|
if s3.name != s4.name:
|
||||||
|
raise Exception("Scoped service is not working")
|
||||||
|
|
||||||
|
if s1.name == s3.name:
|
||||||
|
raise Exception("Scoped service is not working")
|
||||||
|
|
||||||
|
Console.write_line(
|
||||||
|
s1.name,
|
||||||
|
s2.name,
|
||||||
|
s3.name,
|
||||||
|
s4.name,
|
||||||
|
)
|
||||||
|
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,17 @@ from starlette.responses import JSONResponse
|
|||||||
|
|
||||||
from cpl.api import APILogger
|
from cpl.api import APILogger
|
||||||
from cpl.api.router import Router
|
from cpl.api.router import Router
|
||||||
|
from cpl.core.console import Console
|
||||||
|
from cpl.dependency import ServiceProvider
|
||||||
|
from scoped_service import ScopedService
|
||||||
|
|
||||||
|
|
||||||
@Router.authenticate()
|
@Router.authenticate()
|
||||||
# @Router.authorize(permissions=[Permissions.administrator])
|
# @Router.authorize(permissions=[Permissions.administrator])
|
||||||
# @Router.authorize(policies=["test"])
|
# @Router.authorize(policies=["test"])
|
||||||
@Router.get(f"/ping")
|
@Router.get(f"/ping")
|
||||||
async def ping(r: Request, ping: PingService, logger: APILogger):
|
async def ping(r: Request, ping: PingService, logger: APILogger, provider: ServiceProvider, scoped: ScopedService):
|
||||||
logger.info(f"Ping: {ping}")
|
logger.info(f"Ping: {ping}")
|
||||||
|
|
||||||
|
Console.write_line(scoped.name)
|
||||||
return JSONResponse(ping.ping(r))
|
return JSONResponse(ping.ping(r))
|
||||||
|
|||||||
14
example/custom/api/src/scoped_service.py
Normal file
14
example/custom/api/src/scoped_service.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from cpl.core.console.console import Console
|
||||||
|
from cpl.core.utils.string import String
|
||||||
|
|
||||||
|
|
||||||
|
class ScopedService:
|
||||||
|
def __init__(self):
|
||||||
|
self._name = String.random(8)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
Console.write_line(f"Im {self._name}")
|
||||||
@@ -3,7 +3,7 @@ from cpl.auth.keycloak import KeycloakAdmin
|
|||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.core.log import LoggerABC
|
from cpl.core.log import LoggerABC
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
from model.city import City
|
from model.city import City
|
||||||
from model.city_dao import CityDao
|
from model.city_dao import CityDao
|
||||||
from model.user import User
|
from model.user import User
|
||||||
@@ -11,7 +11,7 @@ from model.user_dao import UserDao
|
|||||||
|
|
||||||
|
|
||||||
class Application(ApplicationABC):
|
class Application(ApplicationABC):
|
||||||
def __init__(self, services: ServiceProviderABC):
|
def __init__(self, services: ServiceProvider):
|
||||||
ApplicationABC.__init__(self, services)
|
ApplicationABC.__init__(self, services)
|
||||||
|
|
||||||
self._logger = services.get_service(LoggerABC)
|
self._logger = services.get_service(LoggerABC)
|
||||||
|
|||||||
@@ -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 ServiceProviderABC
|
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
|
||||||
@@ -10,33 +9,37 @@ from di.tester import Tester
|
|||||||
|
|
||||||
|
|
||||||
class Application(ApplicationABC):
|
class Application(ApplicationABC):
|
||||||
def __init__(self, services: ServiceProviderABC):
|
def __init__(self, services: ServiceProvider):
|
||||||
ApplicationABC.__init__(self, services)
|
ApplicationABC.__init__(self, services)
|
||||||
|
|
||||||
def _part_of_scoped(self):
|
def _part_of_scoped(self):
|
||||||
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()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from cpl.application.abc import StartupABC
|
from cpl.application.abc import StartupABC
|
||||||
from cpl.dependency import ServiceProviderABC, ServiceCollection
|
from cpl.dependency import ServiceProvider, ServiceCollection
|
||||||
from di.di_tester_service import DITesterService
|
from di.di_tester_service import DITesterService
|
||||||
from di.test1_service import Test1Service
|
from di.test1_service import Test1Service
|
||||||
from di.test2_service import Test2Service
|
from di.test2_service import Test2Service
|
||||||
@@ -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) -> ServiceProviderABC:
|
@staticmethod
|
||||||
|
def configure_services(services: ServiceCollection) -> ServiceProvider:
|
||||||
services.add_scoped(TestService)
|
services.add_scoped(TestService)
|
||||||
services.add_scoped(DITesterService)
|
services.add_scoped(DITesterService)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
from cpl.dependency import ServiceProvider, ServiceProviderABC
|
from cpl.dependency import ServiceProvider, ServiceProvider
|
||||||
|
from cpl.dependency.inject import inject
|
||||||
from di.test_service import TestService
|
from di.test_service import TestService
|
||||||
|
|
||||||
|
|
||||||
class StaticTest:
|
class StaticTest:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ServiceProvider.inject
|
@inject
|
||||||
def test(services: ServiceProviderABC, t1: TestService):
|
def test(services: ServiceProvider, t1: TestService):
|
||||||
t1.run()
|
t1.run()
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import time
|
import time
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from cpl.application.abc import ApplicationABC
|
from cpl.application.abc import ApplicationABC
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
from cpl.dependency import ServiceProviderABC
|
|
||||||
from cpl.core.environment import Environment
|
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.dependency import ServiceProvider
|
||||||
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 scoped_service import ScopedService
|
||||||
from test_service import TestService
|
from test_service import TestService
|
||||||
from test_settings import TestSettings
|
from test_settings import TestSettings
|
||||||
|
|
||||||
|
|
||||||
class Application(ApplicationABC):
|
class Application(ApplicationABC):
|
||||||
|
|
||||||
def __init__(self, services: ServiceProviderABC):
|
def __init__(self, services: ServiceProvider):
|
||||||
ApplicationABC.__init__(self, services)
|
ApplicationABC.__init__(self, services)
|
||||||
self._logger = self._services.get_service(LoggerABC)
|
self._logger = self._services.get_service(LoggerABC)
|
||||||
self._mailer = self._services.get_service(EMailClientABC)
|
self._mailer = self._services.get_service(EMailClientABC)
|
||||||
@@ -38,7 +38,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 +48,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)
|
||||||
@@ -62,3 +73,9 @@ class Application(ApplicationABC):
|
|||||||
test_settings1 = Configuration.get(TestSettings)
|
test_settings1 = Configuration.get(TestSettings)
|
||||||
Console.write_line(test_settings1.value)
|
Console.write_line(test_settings1.value)
|
||||||
# self.test_send_mail()
|
# self.test_send_mail()
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
while x < 5:
|
||||||
|
Console.write_line("Running...")
|
||||||
|
x += 1
|
||||||
|
time.sleep(5)
|
||||||
20
example/custom/general/src/hosted_service.py
Normal file
20
example/custom/general/src/hosted_service.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
|
||||||
|
from cpl.core.console import Console
|
||||||
|
from cpl.dependency.hosted.hosted_service import HostedService
|
||||||
|
|
||||||
|
|
||||||
|
class Hosted(HostedService):
|
||||||
|
def __init__(self):
|
||||||
|
self._stopped = False
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
Console.write_line("Hosted Service Started")
|
||||||
|
while not self._stopped:
|
||||||
|
Console.write_line("Hosted Service Running")
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
|
async def stop(self):
|
||||||
|
Console.write_line("Hosted Service Stopped")
|
||||||
|
self._stopped = True
|
||||||
10
example/custom/general/src/scoped_service.py
Normal file
10
example/custom/general/src/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,8 @@ 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 example.custom.general.src.hosted_service import Hosted
|
||||||
|
from scoped_service import ScopedService
|
||||||
from test_service import TestService
|
from test_service import TestService
|
||||||
|
|
||||||
|
|
||||||
@@ -21,3 +23,5 @@ 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)
|
||||||
|
services.add_hosted_service(Hosted)
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from cpl.application.abc import ApplicationExtensionABC
|
from cpl.application.abc import ApplicationExtensionABC
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class TestExtension(ApplicationExtensionABC):
|
class TestExtension(ApplicationExtensionABC):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run(services: ServiceProviderABC):
|
def run(services: ServiceProvider):
|
||||||
Console.write_line("Hello World from App Extension")
|
Console.write_line("Hello World from App Extension")
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from cpl.core.console.console import Console
|
from cpl.core.console.console import Console
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
from cpl.core.pipes.ip_address_pipe import IPAddressPipe
|
from cpl.core.pipes.ip_address_pipe import IPAddressPipe
|
||||||
|
|
||||||
|
|
||||||
class TestService:
|
class TestService:
|
||||||
def __init__(self, provider: ServiceProviderABC):
|
def __init__(self, provider: ServiceProvider):
|
||||||
self._provider = provider
|
self._provider = provider
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
from cpl.core.utils.benchmark import Benchmark
|
from cpl.core.utils.benchmark import Benchmark
|
||||||
from cpl.query.collection import Collection
|
|
||||||
from cpl.query.enumerable import Enumerable
|
from cpl.query.enumerable import Enumerable
|
||||||
from cpl.query.list import List
|
|
||||||
from cpl.query.immutable_list import ImmutableList
|
from cpl.query.immutable_list import ImmutableList
|
||||||
|
from cpl.query.list import List
|
||||||
from cpl.query.set import Set
|
from cpl.query.set import Set
|
||||||
|
|
||||||
|
|
||||||
@@ -24,11 +23,23 @@ def _default():
|
|||||||
s.add(1)
|
s.add(1)
|
||||||
Console.write_line(s)
|
Console.write_line(s)
|
||||||
|
|
||||||
|
data = Enumerable(
|
||||||
|
[
|
||||||
|
{"name": "Alice", "age": 30},
|
||||||
|
{"name": "Dave", "age": 35},
|
||||||
|
{"name": "Charlie", "age": 25},
|
||||||
|
{"name": "Bob", "age": 25},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
Console.write_line(data.order_by(lambda x: x["age"]).to_list())
|
||||||
|
Console.write_line(data.order_by(lambda x: x["age"]).then_by(lambda x: x["name"]).to_list())
|
||||||
|
Console.write_line(data.order_by(lambda x: x["name"]).then_by(lambda x: x["age"]).to_list())
|
||||||
|
|
||||||
|
|
||||||
def t_benchmark(data: list):
|
def t_benchmark(data: list):
|
||||||
Benchmark.all("Enumerable", lambda: Enumerable(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list())
|
Benchmark.all("Enumerable", lambda: Enumerable(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list())
|
||||||
Benchmark.all("Set", lambda: Set(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list())
|
Benchmark.all("Set", lambda: Set(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list())
|
||||||
Benchmark.all("Collection", lambda: Collection(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list())
|
|
||||||
Benchmark.all("List", lambda: List(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list())
|
Benchmark.all("List", lambda: List(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list())
|
||||||
Benchmark.all(
|
Benchmark.all(
|
||||||
"ImmutableList", lambda: ImmutableList(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list()
|
"ImmutableList", lambda: ImmutableList(data).where(lambda x: x % 2 == 0).select(lambda x: x * 2).to_list()
|
||||||
@@ -39,7 +50,7 @@ def t_benchmark(data: list):
|
|||||||
def main():
|
def main():
|
||||||
N = 10_000_000
|
N = 10_000_000
|
||||||
data = list(range(N))
|
data = list(range(N))
|
||||||
t_benchmark(data)
|
#t_benchmark(data)
|
||||||
|
|
||||||
Console.write_line()
|
Console.write_line()
|
||||||
_default()
|
_default()
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
from cpl.application import ApplicationABC
|
from cpl.application import ApplicationABC
|
||||||
from cpl.core.configuration import ConfigurationABC
|
from cpl.core.configuration import ConfigurationABC
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
from cpl.translation.translate_pipe import TranslatePipe
|
from cpl.translation.translate_pipe import TranslatePipe
|
||||||
from cpl.translation.translation_service_abc import TranslationServiceABC
|
from cpl.translation.translation_service_abc import TranslationServiceABC
|
||||||
from cpl.translation.translation_settings import TranslationSettings
|
from cpl.translation.translation_settings import TranslationSettings
|
||||||
|
|
||||||
|
|
||||||
class Application(ApplicationABC):
|
class Application(ApplicationABC):
|
||||||
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
|
def __init__(self, config: ConfigurationABC, services: ServiceProvider):
|
||||||
ApplicationABC.__init__(self, config, services)
|
ApplicationABC.__init__(self, config, services)
|
||||||
|
|
||||||
self._translate: TranslatePipe = services.get_service(TranslatePipe)
|
self._translate: TranslatePipe = services.get_service(TranslatePipe)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from cpl.application import StartupABC
|
from cpl.application import StartupABC
|
||||||
from cpl.core.configuration import ConfigurationABC
|
from cpl.core.configuration import ConfigurationABC
|
||||||
from cpl.dependency import ServiceProviderABC, ServiceCollection
|
from cpl.dependency import ServiceProvider, ServiceCollection
|
||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
|
|
||||||
|
|
||||||
@@ -12,6 +12,6 @@ class Startup(StartupABC):
|
|||||||
configuration.add_json_file("appsettings.json")
|
configuration.add_json_file("appsettings.json")
|
||||||
return configuration
|
return configuration
|
||||||
|
|
||||||
def configure_services(self, services: ServiceCollection, environment: Environment) -> ServiceProviderABC:
|
def configure_services(self, services: ServiceCollection, environment: Environment) -> ServiceProvider:
|
||||||
services.add_translation()
|
services.add_translation()
|
||||||
return services.build()
|
return services.build()
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ from cpl.api.settings import ApiSettings
|
|||||||
from cpl.api.typing import HTTPMethods, PartialMiddleware, PolicyResolver
|
from cpl.api.typing import HTTPMethods, PartialMiddleware, PolicyResolver
|
||||||
from cpl.application.abc.application_abc import ApplicationABC
|
from cpl.application.abc.application_abc import ApplicationABC
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
from cpl.dependency.inject import inject
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
PolicyInput = Union[dict[str, PolicyResolver], Policy]
|
PolicyInput = Union[dict[str, PolicyResolver], Policy]
|
||||||
|
|
||||||
|
|
||||||
class WebApp(ApplicationABC):
|
class WebApp(ApplicationABC):
|
||||||
def __init__(self, services: ServiceProviderABC):
|
def __init__(self, services: ServiceProvider):
|
||||||
super().__init__(services, [auth, api])
|
super().__init__(services, [auth, api])
|
||||||
self._app: Starlette | None = None
|
self._app: Starlette | None = None
|
||||||
|
|
||||||
@@ -44,15 +44,15 @@ class WebApp(ApplicationABC):
|
|||||||
self._policies = services.get_service(PolicyRegistry)
|
self._policies = services.get_service(PolicyRegistry)
|
||||||
self._routes = services.get_service(RouteRegistry)
|
self._routes = services.get_service(RouteRegistry)
|
||||||
|
|
||||||
self._middleware: list[Middleware] = [
|
self._middleware: list[Middleware] = []
|
||||||
Middleware(RequestMiddleware),
|
|
||||||
Middleware(LoggingMiddleware),
|
|
||||||
]
|
|
||||||
self._exception_handlers: Mapping[Any, ExceptionHandler] = {
|
self._exception_handlers: Mapping[Any, ExceptionHandler] = {
|
||||||
Exception: self._handle_exception,
|
Exception: self._handle_exception,
|
||||||
APIError: self._handle_exception,
|
APIError: self._handle_exception,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.with_middleware(RequestMiddleware)
|
||||||
|
self.with_middleware(LoggingMiddleware)
|
||||||
|
|
||||||
async def _handle_exception(self, request: Request, exc: Exception):
|
async def _handle_exception(self, request: Request, exc: Exception):
|
||||||
if isinstance(exc, APIError):
|
if isinstance(exc, APIError):
|
||||||
self._logger.error(exc)
|
self._logger.error(exc)
|
||||||
@@ -168,9 +168,9 @@ class WebApp(ApplicationABC):
|
|||||||
self._check_for_app()
|
self._check_for_app()
|
||||||
|
|
||||||
if isinstance(middleware, Middleware):
|
if isinstance(middleware, Middleware):
|
||||||
self._middleware.append(middleware)
|
self._middleware.append(inject(middleware))
|
||||||
elif callable(middleware):
|
elif callable(middleware):
|
||||||
self._middleware.append(Middleware(middleware))
|
self._middleware.append(Middleware(inject(middleware)))
|
||||||
else:
|
else:
|
||||||
raise ValueError("middleware must be of type starlette.middleware.Middleware or a callable")
|
raise ValueError("middleware must be of type starlette.middleware.Middleware or a callable")
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ class WebApp(ApplicationABC):
|
|||||||
self._validate_policies()
|
self._validate_policies()
|
||||||
|
|
||||||
if self._app is None:
|
if self._app is None:
|
||||||
routes = [route.to_starlette(self._services.inject) for route in self._routes.all()]
|
routes = [route.to_starlette(inject) for route in self._routes.all()]
|
||||||
|
|
||||||
app = Starlette(
|
app = Starlette(
|
||||||
routes=routes,
|
routes=routes,
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ from cpl.api.router import Router
|
|||||||
from cpl.auth.keycloak import KeycloakClient
|
from cpl.auth.keycloak import KeycloakClient
|
||||||
from cpl.auth.schema import AuthUserDao, AuthUser
|
from cpl.auth.schema import AuthUserDao, AuthUser
|
||||||
from cpl.core.ctx import set_user
|
from cpl.core.ctx import set_user
|
||||||
from cpl.dependency import ServiceProviderABC
|
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationMiddleware(ASGIMiddleware):
|
class AuthenticationMiddleware(ASGIMiddleware):
|
||||||
|
|
||||||
@ServiceProviderABC.inject
|
|
||||||
def __init__(self, app, logger: APILogger, keycloak: KeycloakClient, user_dao: AuthUserDao):
|
def __init__(self, app, logger: APILogger, keycloak: KeycloakClient, user_dao: AuthUserDao):
|
||||||
ASGIMiddleware.__init__(self, app)
|
ASGIMiddleware.__init__(self, app)
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ from cpl.api.registry.policy import PolicyRegistry
|
|||||||
from cpl.api.router import Router
|
from cpl.api.router import Router
|
||||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||||
from cpl.core.ctx.user_context import get_user
|
from cpl.core.ctx.user_context import get_user
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationMiddleware(ASGIMiddleware):
|
class AuthorizationMiddleware(ASGIMiddleware):
|
||||||
|
|
||||||
@ServiceProviderABC.inject
|
|
||||||
def __init__(self, app, logger: APILogger, policies: PolicyRegistry, user_dao: AuthUserDao):
|
def __init__(self, app, logger: APILogger, policies: PolicyRegistry, user_dao: AuthUserDao):
|
||||||
ASGIMiddleware.__init__(self, app)
|
ASGIMiddleware.__init__(self, app)
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,10 @@ from starlette.types import Receive, Scope, Send
|
|||||||
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
|
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
|
||||||
from cpl.api.logger import APILogger
|
from cpl.api.logger import APILogger
|
||||||
from cpl.api.middleware.request import get_request
|
from cpl.api.middleware.request import get_request
|
||||||
from cpl.dependency import ServiceProviderABC
|
|
||||||
|
|
||||||
|
|
||||||
class LoggingMiddleware(ASGIMiddleware):
|
class LoggingMiddleware(ASGIMiddleware):
|
||||||
|
|
||||||
@ServiceProviderABC.inject
|
|
||||||
def __init__(self, app, logger: APILogger):
|
def __init__(self, app, logger: APILogger):
|
||||||
ASGIMiddleware.__init__(self, app)
|
ASGIMiddleware.__init__(self, app)
|
||||||
|
|
||||||
|
|||||||
@@ -9,17 +9,18 @@ from starlette.types import Scope, Receive, Send
|
|||||||
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
|
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
|
||||||
from cpl.api.logger import APILogger
|
from cpl.api.logger import APILogger
|
||||||
from cpl.api.typing import TRequest
|
from cpl.api.typing import TRequest
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency.inject import inject
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
_request_context: ContextVar[Union[TRequest, None]] = ContextVar("request", default=None)
|
_request_context: ContextVar[Union[TRequest, None]] = ContextVar("request", default=None)
|
||||||
|
|
||||||
|
|
||||||
class RequestMiddleware(ASGIMiddleware):
|
class RequestMiddleware(ASGIMiddleware):
|
||||||
|
|
||||||
@ServiceProviderABC.inject
|
def __init__(self, app, provider: ServiceProvider, logger: APILogger):
|
||||||
def __init__(self, app, logger: APILogger):
|
|
||||||
ASGIMiddleware.__init__(self, app)
|
ASGIMiddleware.__init__(self, app)
|
||||||
|
|
||||||
|
self._provider = provider
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
|
|
||||||
self._ctx_token = None
|
self._ctx_token = None
|
||||||
@@ -29,7 +30,8 @@ class RequestMiddleware(ASGIMiddleware):
|
|||||||
await self.set_request_data(request)
|
await self.set_request_data(request)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self._app(scope, receive, send)
|
with self._provider.create_scope():
|
||||||
|
inject(await self._app(scope, receive, send))
|
||||||
finally:
|
finally:
|
||||||
await self.clean_request_data()
|
await self.clean_request_data()
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from enum import Enum
|
|||||||
from cpl.api.model.validation_match import ValidationMatch
|
from cpl.api.model.validation_match import ValidationMatch
|
||||||
from cpl.api.registry.route import RouteRegistry
|
from cpl.api.registry.route import RouteRegistry
|
||||||
from cpl.api.typing import HTTPMethods
|
from cpl.api.typing import HTTPMethods
|
||||||
|
from cpl.dependency import get_provider
|
||||||
|
|
||||||
|
|
||||||
class Router:
|
class Router:
|
||||||
@@ -95,9 +96,7 @@ class Router:
|
|||||||
from cpl.api.model.api_route import ApiRoute
|
from cpl.api.model.api_route import ApiRoute
|
||||||
|
|
||||||
if not registry:
|
if not registry:
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
routes = get_provider().get_service(RouteRegistry)
|
||||||
|
|
||||||
routes = ServiceProviderABC.get_global_service(RouteRegistry)
|
|
||||||
else:
|
else:
|
||||||
routes = registry
|
routes = registry
|
||||||
|
|
||||||
@@ -144,9 +143,8 @@ class Router:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from cpl.api.model.api_route import ApiRoute
|
from cpl.api.model.api_route import ApiRoute
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
|
||||||
|
|
||||||
routes = ServiceProviderABC.get_global_service(RouteRegistry)
|
routes = get_provider().get_service(RouteRegistry)
|
||||||
|
|
||||||
def inner(fn):
|
def inner(fn):
|
||||||
path = getattr(fn, "_route_path", None)
|
path = getattr(fn, "_route_path", None)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from cpl.application.host import Host
|
|||||||
from cpl.core.log.log_level import LogLevel
|
from cpl.core.log.log_level import LogLevel
|
||||||
from cpl.core.log.log_settings import LogSettings
|
from cpl.core.log.log_settings import LogSettings
|
||||||
from cpl.core.log.logger_abc import LoggerABC
|
from cpl.core.log.logger_abc import LoggerABC
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
def __not_implemented__(package: str, func: Callable):
|
def __not_implemented__(package: str, func: Callable):
|
||||||
@@ -16,12 +16,12 @@ class ApplicationABC(ABC):
|
|||||||
r"""ABC for the Application class
|
r"""ABC for the Application class
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
services: :class:`cpl.dependency.service_provider_abc.ServiceProviderABC`
|
services: :class:`cpl.dependency.service_provider.ServiceProvider`
|
||||||
Contains instances of prepared objects
|
Contains instances of prepared objects
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __init__(self, services: ServiceProviderABC, required_modules: list[str | object] = None):
|
def __init__(self, services: ServiceProvider, required_modules: list[str | object] = None):
|
||||||
self._services = services
|
self._services = services
|
||||||
self._required_modules = (
|
self._required_modules = (
|
||||||
[x.__name__ if not isinstance(x, str) else x for x in required_modules] if required_modules else []
|
[x.__name__ if not isinstance(x, str) else x for x in required_modules] if required_modules else []
|
||||||
@@ -84,7 +84,7 @@ class ApplicationABC(ABC):
|
|||||||
Called by custom Application.main
|
Called by custom Application.main
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
Host.run(self.main)
|
Host.run_app(self.main)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class ApplicationExtensionABC(ABC):
|
class ApplicationExtensionABC(ABC):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def run(services: ServiceProviderABC): ...
|
def run(services: ServiceProvider): ...
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from cpl.application.abc.startup_abc import StartupABC
|
|||||||
from cpl.application.abc.startup_extension_abc import StartupExtensionABC
|
from cpl.application.abc.startup_extension_abc import StartupExtensionABC
|
||||||
from cpl.application.host import Host
|
from cpl.application.host import Host
|
||||||
from cpl.core.errors import dependency_error
|
from cpl.core.errors import dependency_error
|
||||||
|
from cpl.dependency.context import get_provider, use_root_provider
|
||||||
from cpl.dependency.service_collection import ServiceCollection
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
|
|
||||||
TApp = TypeVar("TApp", bound=ApplicationABC)
|
TApp = TypeVar("TApp", bound=ApplicationABC)
|
||||||
@@ -21,6 +22,7 @@ class ApplicationBuilder(Generic[TApp]):
|
|||||||
self._app = app if app is not None else ApplicationABC
|
self._app = app if app is not None else ApplicationABC
|
||||||
|
|
||||||
self._services = ServiceCollection()
|
self._services = ServiceCollection()
|
||||||
|
use_root_provider(self._services.build())
|
||||||
|
|
||||||
self._startup: Optional[StartupABC] = None
|
self._startup: Optional[StartupABC] = None
|
||||||
self._app_extensions: list[Type[ApplicationExtensionABC]] = []
|
self._app_extensions: list[Type[ApplicationExtensionABC]] = []
|
||||||
@@ -34,7 +36,12 @@ class ApplicationBuilder(Generic[TApp]):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def service_provider(self):
|
def service_provider(self):
|
||||||
return self._services.build()
|
provider = get_provider()
|
||||||
|
if provider is None:
|
||||||
|
provider = self._services.build()
|
||||||
|
use_root_provider(provider)
|
||||||
|
|
||||||
|
return provider
|
||||||
|
|
||||||
def validate_app_required_modules(self, app: ApplicationABC):
|
def validate_app_required_modules(self, app: ApplicationABC):
|
||||||
for module in app.required_modules:
|
for module in app.required_modules:
|
||||||
|
|||||||
@@ -1,17 +1,83 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
|
from cpl.dependency import get_provider
|
||||||
|
from cpl.dependency.hosted.startup_task import StartupTask
|
||||||
|
|
||||||
|
|
||||||
class Host:
|
class Host:
|
||||||
_loop = asyncio.get_event_loop()
|
_loop: asyncio.AbstractEventLoop | None = None
|
||||||
|
_tasks: dict = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_loop(cls):
|
def get_loop(cls) -> asyncio.AbstractEventLoop:
|
||||||
|
if cls._loop is None:
|
||||||
|
cls._loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(cls._loop)
|
||||||
return cls._loop
|
return cls._loop
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run_start_tasks(cls):
|
||||||
|
provider = get_provider()
|
||||||
|
tasks = provider.get_services(StartupTask)
|
||||||
|
loop = cls.get_loop()
|
||||||
|
|
||||||
|
for task in tasks:
|
||||||
|
if asyncio.iscoroutinefunction(task.run):
|
||||||
|
loop.run_until_complete(task.run())
|
||||||
|
else:
|
||||||
|
task.run()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run_hosted_services(cls):
|
||||||
|
provider = get_provider()
|
||||||
|
services = provider.get_hosted_services()
|
||||||
|
loop = cls.get_loop()
|
||||||
|
|
||||||
|
for service in services:
|
||||||
|
if asyncio.iscoroutinefunction(service.start):
|
||||||
|
cls._tasks[service] = loop.create_task(service.start())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def _stop_all(cls):
|
||||||
|
for service in cls._tasks.keys():
|
||||||
|
if asyncio.iscoroutinefunction(service.stop):
|
||||||
|
await service.stop()
|
||||||
|
|
||||||
|
for task in cls._tasks.values():
|
||||||
|
task.cancel()
|
||||||
|
|
||||||
|
cls._tasks.clear()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run_app(cls, func: Callable, *args, **kwargs):
|
||||||
|
loop = cls.get_loop()
|
||||||
|
|
||||||
|
cls.run_start_tasks()
|
||||||
|
cls.run_hosted_services()
|
||||||
|
|
||||||
|
async def runner():
|
||||||
|
try:
|
||||||
|
if asyncio.iscoroutinefunction(func):
|
||||||
|
app_task = asyncio.create_task(func(*args, **kwargs))
|
||||||
|
else:
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
app_task = loop.run_in_executor(None, func, *args, **kwargs)
|
||||||
|
|
||||||
|
await asyncio.wait(
|
||||||
|
[app_task, *cls._tasks.values()],
|
||||||
|
return_when=asyncio.FIRST_COMPLETED,
|
||||||
|
)
|
||||||
|
except (KeyboardInterrupt, asyncio.CancelledError):
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
await cls._stop_all()
|
||||||
|
|
||||||
|
loop.run_until_complete(runner())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls, func: Callable, *args, **kwargs):
|
def run(cls, func: Callable, *args, **kwargs):
|
||||||
if asyncio.iscoroutinefunction(func):
|
if asyncio.iscoroutinefunction(func):
|
||||||
return cls._loop.run_until_complete(func(*args, **kwargs))
|
return cls._loop.run_until_complete(func(*args, **kwargs))
|
||||||
|
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from cpl.core.utils.get_value import get_value
|
from cpl.core.utils.get_value import get_value
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class KeycloakUser:
|
class KeycloakUser:
|
||||||
@@ -32,5 +32,5 @@ class KeycloakUser:
|
|||||||
def id(self) -> str:
|
def id(self) -> str:
|
||||||
from cpl.auth import KeycloakAdmin
|
from cpl.auth import KeycloakAdmin
|
||||||
|
|
||||||
keycloak_admin: KeycloakAdmin = ServiceProviderABC.get_global_service(KeycloakAdmin)
|
keycloak_admin: KeycloakAdmin = get_provider().get_service(KeycloakAdmin)
|
||||||
return keycloak_admin.get_user_id(self._username)
|
return keycloak_admin.get_user_id(self._username)
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ from cpl.core.log.logger import Logger
|
|||||||
from cpl.core.typing import Id, SerialId
|
from cpl.core.typing import Id, SerialId
|
||||||
from cpl.core.utils.credential_manager import CredentialManager
|
from cpl.core.utils.credential_manager import CredentialManager
|
||||||
from cpl.database.abc.db_model_abc import DbModelABC
|
from cpl.database.abc.db_model_abc import DbModelABC
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
from cpl.dependency import get_provider
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
_logger = Logger(__name__)
|
_logger = Logger(__name__)
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ class ApiKey(DbModelABC):
|
|||||||
async def permissions(self):
|
async def permissions(self):
|
||||||
from cpl.auth.schema._permission.api_key_permission_dao import ApiKeyPermissionDao
|
from cpl.auth.schema._permission.api_key_permission_dao import ApiKeyPermissionDao
|
||||||
|
|
||||||
apiKeyPermissionDao = ServiceProviderABC.get_global_provider().get_service(ApiKeyPermissionDao)
|
apiKeyPermissionDao = get_provider().get_service(ApiKeyPermissionDao)
|
||||||
|
|
||||||
return [await x.permission for x in await apiKeyPermissionDao.find_by_api_key_id(self.id)]
|
return [await x.permission for x in await apiKeyPermissionDao.find_by_api_key_id(self.id)]
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from cpl.auth.permission.permissions import Permissions
|
|||||||
from cpl.core.typing import SerialId
|
from cpl.core.typing import SerialId
|
||||||
from cpl.database.abc import DbModelABC
|
from cpl.database.abc import DbModelABC
|
||||||
from cpl.database.logger import DBLogger
|
from cpl.database.logger import DBLogger
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class AuthUser(DbModelABC):
|
class AuthUser(DbModelABC):
|
||||||
@@ -36,12 +36,12 @@ class AuthUser(DbModelABC):
|
|||||||
return "ANONYMOUS"
|
return "ANONYMOUS"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
keycloak = ServiceProviderABC.get_global_service(KeycloakAdmin)
|
keycloak = get_provider().get_service(KeycloakAdmin)
|
||||||
return keycloak.get_user(self._keycloak_id).get("username")
|
return keycloak.get_user(self._keycloak_id).get("username")
|
||||||
except KeycloakGetError as e:
|
except KeycloakGetError as e:
|
||||||
return "UNKNOWN"
|
return "UNKNOWN"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger = ServiceProviderABC.get_global_service(DBLogger)
|
logger = get_provider().get_service(DBLogger)
|
||||||
logger.error(f"Failed to get user {self._keycloak_id} from Keycloak", e)
|
logger.error(f"Failed to get user {self._keycloak_id} from Keycloak", e)
|
||||||
return "UNKNOWN"
|
return "UNKNOWN"
|
||||||
|
|
||||||
@@ -51,12 +51,12 @@ class AuthUser(DbModelABC):
|
|||||||
return "ANONYMOUS"
|
return "ANONYMOUS"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
keycloak = ServiceProviderABC.get_global_service(KeycloakAdmin)
|
keycloak = get_provider().get_service(KeycloakAdmin)
|
||||||
return keycloak.get_user(self._keycloak_id).get("email")
|
return keycloak.get_user(self._keycloak_id).get("email")
|
||||||
except KeycloakGetError as e:
|
except KeycloakGetError as e:
|
||||||
return "UNKNOWN"
|
return "UNKNOWN"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger = ServiceProviderABC.get_global_service(DBLogger)
|
logger = get_provider().get_service(DBLogger)
|
||||||
logger.error(f"Failed to get user {self._keycloak_id} from Keycloak", e)
|
logger.error(f"Failed to get user {self._keycloak_id} from Keycloak", e)
|
||||||
return "UNKNOWN"
|
return "UNKNOWN"
|
||||||
|
|
||||||
@@ -64,26 +64,26 @@ class AuthUser(DbModelABC):
|
|||||||
async def roles(self):
|
async def roles(self):
|
||||||
from cpl.auth.schema._permission.role_user_dao import RoleUserDao
|
from cpl.auth.schema._permission.role_user_dao import RoleUserDao
|
||||||
|
|
||||||
role_user_dao: RoleUserDao = ServiceProviderABC.get_global_service(RoleUserDao)
|
role_user_dao: RoleUserDao = get_provider().get_service(RoleUserDao)
|
||||||
return [await x.role for x in await role_user_dao.get_by_user_id(self.id)]
|
return [await x.role for x in await role_user_dao.get_by_user_id(self.id)]
|
||||||
|
|
||||||
@async_property
|
@async_property
|
||||||
async def permissions(self):
|
async def permissions(self):
|
||||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||||
|
|
||||||
auth_user_dao: AuthUserDao = ServiceProviderABC.get_global_service(AuthUserDao)
|
auth_user_dao: AuthUserDao = get_provider().get_service(AuthUserDao)
|
||||||
return await auth_user_dao.get_permissions(self.id)
|
return await auth_user_dao.get_permissions(self.id)
|
||||||
|
|
||||||
async def has_permission(self, permission: Permissions) -> bool:
|
async def has_permission(self, permission: Permissions) -> bool:
|
||||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||||
|
|
||||||
auth_user_dao: AuthUserDao = ServiceProviderABC.get_global_service(AuthUserDao)
|
auth_user_dao: AuthUserDao = get_provider().get_service(AuthUserDao)
|
||||||
return await auth_user_dao.has_permission(self.id, permission)
|
return await auth_user_dao.has_permission(self.id, permission)
|
||||||
|
|
||||||
async def anonymize(self):
|
async def anonymize(self):
|
||||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||||
|
|
||||||
auth_user_dao: AuthUserDao = ServiceProviderABC.get_global_service(AuthUserDao)
|
auth_user_dao: AuthUserDao = get_provider().get_service(AuthUserDao)
|
||||||
|
|
||||||
self._keycloak_id = str(uuid.UUID(int=0))
|
self._keycloak_id = str(uuid.UUID(int=0))
|
||||||
await auth_user_dao.update(self)
|
await auth_user_dao.update(self)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from cpl.auth.schema._administration.auth_user import AuthUser
|
|||||||
from cpl.database import TableManager
|
from cpl.database import TableManager
|
||||||
from cpl.database.abc import DbModelDaoABC
|
from cpl.database.abc import DbModelDaoABC
|
||||||
from cpl.database.external_data_temp_table_builder import ExternalDataTempTableBuilder
|
from cpl.database.external_data_temp_table_builder import ExternalDataTempTableBuilder
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class AuthUserDao(DbModelDaoABC[AuthUser]):
|
class AuthUserDao(DbModelDaoABC[AuthUser]):
|
||||||
@@ -36,7 +36,7 @@ class AuthUserDao(DbModelDaoABC[AuthUser]):
|
|||||||
async def has_permission(self, user_id: int, permission: Union[Permissions, str]) -> bool:
|
async def has_permission(self, user_id: int, permission: Union[Permissions, str]) -> bool:
|
||||||
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
||||||
|
|
||||||
permission_dao: PermissionDao = ServiceProviderABC.get_global_service(PermissionDao)
|
permission_dao: PermissionDao = get_provider().get_service(PermissionDao)
|
||||||
p = await permission_dao.get_by_name(permission if isinstance(permission, str) else permission.value)
|
p = await permission_dao.get_by_name(permission if isinstance(permission, str) else permission.value)
|
||||||
result = await self._db.select_map(
|
result = await self._db.select_map(
|
||||||
f"""
|
f"""
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from async_property import async_property
|
|||||||
|
|
||||||
from cpl.core.typing import SerialId
|
from cpl.core.typing import SerialId
|
||||||
from cpl.database.abc import DbJoinModelABC
|
from cpl.database.abc import DbJoinModelABC
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class ApiKeyPermission(DbJoinModelABC):
|
class ApiKeyPermission(DbJoinModelABC):
|
||||||
@@ -31,7 +31,7 @@ class ApiKeyPermission(DbJoinModelABC):
|
|||||||
async def api_key(self):
|
async def api_key(self):
|
||||||
from cpl.auth.schema._administration.api_key_dao import ApiKeyDao
|
from cpl.auth.schema._administration.api_key_dao import ApiKeyDao
|
||||||
|
|
||||||
api_key_dao: ApiKeyDao = ServiceProviderABC.get_global_service(ApiKeyDao)
|
api_key_dao: ApiKeyDao = get_provider().get_service(ApiKeyDao)
|
||||||
return await api_key_dao.get_by_id(self._api_key_id)
|
return await api_key_dao.get_by_id(self._api_key_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -42,5 +42,5 @@ class ApiKeyPermission(DbJoinModelABC):
|
|||||||
async def permission(self):
|
async def permission(self):
|
||||||
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
||||||
|
|
||||||
permission_dao: PermissionDao = ServiceProviderABC.get_global_service(PermissionDao)
|
permission_dao: PermissionDao = get_provider().get_service(PermissionDao)
|
||||||
return await permission_dao.get_by_id(self._permission_id)
|
return await permission_dao.get_by_id(self._permission_id)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from async_property import async_property
|
|||||||
from cpl.auth.permission.permissions import Permissions
|
from cpl.auth.permission.permissions import Permissions
|
||||||
from cpl.core.typing import SerialId
|
from cpl.core.typing import SerialId
|
||||||
from cpl.database.abc import DbModelABC
|
from cpl.database.abc import DbModelABC
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class Role(DbModelABC):
|
class Role(DbModelABC):
|
||||||
@@ -44,22 +44,22 @@ class Role(DbModelABC):
|
|||||||
async def permissions(self):
|
async def permissions(self):
|
||||||
from cpl.auth.schema._permission.role_permission_dao import RolePermissionDao
|
from cpl.auth.schema._permission.role_permission_dao import RolePermissionDao
|
||||||
|
|
||||||
role_permission_dao: RolePermissionDao = ServiceProviderABC.get_global_service(RolePermissionDao)
|
role_permission_dao: RolePermissionDao = get_provider().get_service(RolePermissionDao)
|
||||||
return [await x.permission for x in await role_permission_dao.get_by_role_id(self.id)]
|
return [await x.permission for x in await role_permission_dao.get_by_role_id(self.id)]
|
||||||
|
|
||||||
@async_property
|
@async_property
|
||||||
async def users(self):
|
async def users(self):
|
||||||
from cpl.auth.schema._permission.role_user_dao import RoleUserDao
|
from cpl.auth.schema._permission.role_user_dao import RoleUserDao
|
||||||
|
|
||||||
role_user_dao: RoleUserDao = ServiceProviderABC.get_global_service(RoleUserDao)
|
role_user_dao: RoleUserDao = get_provider().get_service(RoleUserDao)
|
||||||
return [await x.user for x in await role_user_dao.get_by_role_id(self.id)]
|
return [await x.user for x in await role_user_dao.get_by_role_id(self.id)]
|
||||||
|
|
||||||
async def has_permission(self, permission: Permissions) -> bool:
|
async def has_permission(self, permission: Permissions) -> bool:
|
||||||
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
||||||
from cpl.auth.schema._permission.role_permission_dao import RolePermissionDao
|
from cpl.auth.schema._permission.role_permission_dao import RolePermissionDao
|
||||||
|
|
||||||
permission_dao: PermissionDao = ServiceProviderABC.get_global_service(PermissionDao)
|
permission_dao: PermissionDao = get_provider().get_service(PermissionDao)
|
||||||
role_permission_dao: RolePermissionDao = ServiceProviderABC.get_global_service(RolePermissionDao)
|
role_permission_dao: RolePermissionDao = get_provider().get_service(RolePermissionDao)
|
||||||
|
|
||||||
p = await permission_dao.get_by_name(permission.value)
|
p = await permission_dao.get_by_name(permission.value)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from async_property import async_property
|
|||||||
|
|
||||||
from cpl.core.typing import SerialId
|
from cpl.core.typing import SerialId
|
||||||
from cpl.database.abc import DbModelABC
|
from cpl.database.abc import DbModelABC
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class RolePermission(DbModelABC):
|
class RolePermission(DbModelABC):
|
||||||
@@ -31,7 +31,7 @@ class RolePermission(DbModelABC):
|
|||||||
async def role(self):
|
async def role(self):
|
||||||
from cpl.auth.schema._permission.role_dao import RoleDao
|
from cpl.auth.schema._permission.role_dao import RoleDao
|
||||||
|
|
||||||
role_dao: RoleDao = ServiceProviderABC.get_global_service(RoleDao)
|
role_dao: RoleDao = get_provider().get_service(RoleDao)
|
||||||
return await role_dao.get_by_id(self._role_id)
|
return await role_dao.get_by_id(self._role_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -42,5 +42,5 @@ class RolePermission(DbModelABC):
|
|||||||
async def permission(self):
|
async def permission(self):
|
||||||
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
||||||
|
|
||||||
permission_dao: PermissionDao = ServiceProviderABC.get_global_service(PermissionDao)
|
permission_dao: PermissionDao = get_provider().get_service(PermissionDao)
|
||||||
return await permission_dao.get_by_id(self._permission_id)
|
return await permission_dao.get_by_id(self._permission_id)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from async_property import async_property
|
|||||||
|
|
||||||
from cpl.core.typing import SerialId
|
from cpl.core.typing import SerialId
|
||||||
from cpl.database.abc import DbJoinModelABC
|
from cpl.database.abc import DbJoinModelABC
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class RoleUser(DbJoinModelABC):
|
class RoleUser(DbJoinModelABC):
|
||||||
@@ -31,7 +31,7 @@ class RoleUser(DbJoinModelABC):
|
|||||||
async def user(self):
|
async def user(self):
|
||||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||||
|
|
||||||
auth_user_dao: AuthUserDao = ServiceProviderABC.get_global_service(AuthUserDao)
|
auth_user_dao: AuthUserDao = get_provider().get_service(AuthUserDao)
|
||||||
return await auth_user_dao.get_by_id(self._user_id)
|
return await auth_user_dao.get_by_id(self._user_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -42,5 +42,5 @@ class RoleUser(DbJoinModelABC):
|
|||||||
async def role(self):
|
async def role(self):
|
||||||
from cpl.auth.schema._permission.role_dao import RoleDao
|
from cpl.auth.schema._permission.role_dao import RoleDao
|
||||||
|
|
||||||
role_dao: RoleDao = ServiceProviderABC.get_global_service(RoleDao)
|
role_dao: RoleDao = get_provider().get_service(RoleDao)
|
||||||
return await role_dao.get_by_id(self._role_id)
|
return await role_dao.get_by_id(self._role_id)
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ from contextvars import ContextVar
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from cpl.auth.schema._administration.auth_user import AuthUser
|
from cpl.auth.schema._administration.auth_user import AuthUser
|
||||||
|
from cpl.dependency import get_provider
|
||||||
|
|
||||||
_user_context: ContextVar[Optional[AuthUser]] = ContextVar("user", default=None)
|
_user_context: ContextVar[Optional[AuthUser]] = ContextVar("user", default=None)
|
||||||
|
|
||||||
|
|
||||||
def set_user(user: Optional[AuthUser]):
|
def set_user(user: Optional[AuthUser]):
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
|
||||||
from cpl.core.log.logger_abc import LoggerABC
|
from cpl.core.log.logger_abc import LoggerABC
|
||||||
|
|
||||||
logger = ServiceProviderABC.get_global_service(LoggerABC)
|
logger = get_provider().get_service(LoggerABC)
|
||||||
logger.trace("Setting user context", user.id)
|
logger.trace("Setting user context", user.id)
|
||||||
_user_context.set(user)
|
_user_context.set(user)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from starlette.requests import Request
|
|||||||
from cpl.core.log.log_level import LogLevel
|
from cpl.core.log.log_level import LogLevel
|
||||||
from cpl.core.log.logger import Logger
|
from cpl.core.log.logger import Logger
|
||||||
from cpl.core.typing import Source, Messages
|
from cpl.core.typing import Source, Messages
|
||||||
|
from cpl.dependency import get_provider
|
||||||
|
|
||||||
|
|
||||||
class StructuredLogger(Logger):
|
class StructuredLogger(Logger):
|
||||||
@@ -99,10 +100,9 @@ class StructuredLogger(Logger):
|
|||||||
if user is None:
|
if user is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
|
||||||
from cpl.auth.keycloak.keycloak_admin import KeycloakAdmin
|
from cpl.auth.keycloak.keycloak_admin import KeycloakAdmin
|
||||||
|
|
||||||
keycloak = ServiceProviderABC.get_global_service(KeycloakAdmin)
|
keycloak = get_provider().get_service(KeycloakAdmin)
|
||||||
kc_user = keycloak.get_user(user.keycloak_id)
|
kc_user = keycloak.get_user(user.keycloak_id)
|
||||||
message["user"] = {
|
message["user"] = {
|
||||||
"id": str(user.id),
|
"id": str(user.id),
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ from typing import Type
|
|||||||
|
|
||||||
from cpl.core.log import LoggerABC, LogLevel
|
from cpl.core.log import LoggerABC, LogLevel
|
||||||
from cpl.core.typing import Messages
|
from cpl.core.typing import Messages
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
from cpl.dependency.inject import inject
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class WrappedLogger(LoggerABC):
|
class WrappedLogger(LoggerABC):
|
||||||
@@ -17,13 +18,13 @@ class WrappedLogger(LoggerABC):
|
|||||||
|
|
||||||
self._set_logger()
|
self._set_logger()
|
||||||
|
|
||||||
@ServiceProviderABC.inject
|
@inject
|
||||||
def _set_logger(self, services: ServiceProviderABC):
|
def _set_logger(self, services: ServiceProvider):
|
||||||
from cpl.core.log import Logger
|
from cpl.core.log import Logger
|
||||||
|
|
||||||
t_logger: Type[Logger] = services.get_service_type(LoggerABC)
|
t_logger: Type[Logger] = services.get_service_type(LoggerABC)
|
||||||
if t_logger is None:
|
if t_logger is None:
|
||||||
raise Exception("No LoggerABC service registered in ServiceProviderABC")
|
raise Exception("No LoggerABC service registered in ServiceProvider")
|
||||||
|
|
||||||
self._logger = t_logger(self._source, self._file_prefix)
|
self._logger = t_logger(self._source, self._file_prefix)
|
||||||
|
|
||||||
@@ -42,8 +43,8 @@ class WrappedLogger(LoggerABC):
|
|||||||
from cpl.dependency import ServiceCollection
|
from cpl.dependency import ServiceCollection
|
||||||
|
|
||||||
ignore_classes = [
|
ignore_classes = [
|
||||||
ServiceProviderABC,
|
ServiceProvider,
|
||||||
ServiceProviderABC.__subclasses__(),
|
ServiceProvider.__subclasses__(),
|
||||||
ServiceCollection,
|
ServiceCollection,
|
||||||
WrappedLogger,
|
WrappedLogger,
|
||||||
WrappedLogger.__subclasses__(),
|
WrappedLogger.__subclasses__(),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from .table_manager import TableManager
|
|||||||
|
|
||||||
|
|
||||||
def _with_migrations(self: _ApplicationABC, *paths: str | list[str]) -> _ApplicationABC:
|
def _with_migrations(self: _ApplicationABC, *paths: str | list[str]) -> _ApplicationABC:
|
||||||
from cpl.application.host import Host
|
|
||||||
from cpl.database.service.migration_service import MigrationService
|
from cpl.database.service.migration_service import MigrationService
|
||||||
|
|
||||||
migration_service = self._services.get_service(MigrationService)
|
migration_service = self._services.get_service(MigrationService)
|
||||||
@@ -21,8 +20,6 @@ def _with_migrations(self: _ApplicationABC, *paths: str | list[str]) -> _Applica
|
|||||||
for path in paths:
|
for path in paths:
|
||||||
migration_service.with_directory(path)
|
migration_service.with_directory(path)
|
||||||
|
|
||||||
Host.run(migration_service.migrate)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,21 +9,20 @@ from cpl.core.utils.get_value import get_value
|
|||||||
from cpl.core.utils.string import String
|
from cpl.core.utils.string import String
|
||||||
from cpl.database.abc.db_context_abc import DBContextABC
|
from cpl.database.abc.db_context_abc import DBContextABC
|
||||||
from cpl.database.const import DATETIME_FORMAT
|
from cpl.database.const import DATETIME_FORMAT
|
||||||
from cpl.database.logger import DBLogger
|
|
||||||
from cpl.database.external_data_temp_table_builder import ExternalDataTempTableBuilder
|
from cpl.database.external_data_temp_table_builder import ExternalDataTempTableBuilder
|
||||||
|
from cpl.database.logger import DBLogger
|
||||||
|
from cpl.database.model.server_type import ServerType, ServerTypes
|
||||||
from cpl.database.postgres.sql_select_builder import SQLSelectBuilder
|
from cpl.database.postgres.sql_select_builder import SQLSelectBuilder
|
||||||
from cpl.database.typing import T_DBM, Attribute, AttributeFilters, AttributeSorts
|
from cpl.database.typing import T_DBM, Attribute, AttributeFilters, AttributeSorts
|
||||||
|
from cpl.dependency import get_provider
|
||||||
|
|
||||||
|
|
||||||
class DataAccessObjectABC(ABC, Generic[T_DBM]):
|
class DataAccessObjectABC(ABC, Generic[T_DBM]):
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __init__(self, model_type: Type[T_DBM], table_name: str):
|
def __init__(self, model_type: Type[T_DBM], table_name: str):
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
self._db = get_provider().get_service(DBContextABC)
|
||||||
|
self._logger = get_provider().get_service(DBLogger)
|
||||||
self._db = ServiceProviderABC.get_global_service(DBContextABC)
|
|
||||||
|
|
||||||
self._logger = ServiceProviderABC.get_global_service(DBLogger)
|
|
||||||
self._model_type = model_type
|
self._model_type = model_type
|
||||||
self._table_name = table_name
|
self._table_name = table_name
|
||||||
|
|
||||||
@@ -353,13 +352,13 @@ class DataAccessObjectABC(ABC, Generic[T_DBM]):
|
|||||||
values = f"{await self._get_editor_id(obj) if not skip_editor else ''}{f', {values}' if not skip_editor and len(values) > 0 else f'{values}'}"
|
values = f"{await self._get_editor_id(obj) if not skip_editor else ''}{f', {values}' if not skip_editor and len(values) > 0 else f'{values}'}"
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
INSERT INTO {self._table_name} (
|
INSERT INTO {self._table_name} (
|
||||||
{fields}
|
{fields}
|
||||||
) VALUES (
|
) VALUES (
|
||||||
{values}
|
{values}
|
||||||
)
|
)
|
||||||
RETURNING {self.__primary_key};
|
{"RETURNING {self.__primary_key};" if ServerType.server_type == ServerTypes.POSTGRES else ";SELECT LAST_INSERT_ID();"}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def create(self, obj: T_DBM, skip_editor=False) -> int:
|
async def create(self, obj: T_DBM, skip_editor=False) -> int:
|
||||||
self._logger.debug(f"create {type(obj).__name__} {obj.__dict__}")
|
self._logger.debug(f"create {type(obj).__name__} {obj.__dict__}")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from mysql.connector.aio import MySQLConnectionPool
|
|||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.database.logger import DBLogger
|
from cpl.database.logger import DBLogger
|
||||||
from cpl.database.model import DatabaseSettings
|
from cpl.database.model import DatabaseSettings
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency.context import get_provider
|
||||||
|
|
||||||
|
|
||||||
class MySQLPool:
|
class MySQLPool:
|
||||||
@@ -18,7 +18,11 @@ class MySQLPool:
|
|||||||
"user": database_settings.user,
|
"user": database_settings.user,
|
||||||
"password": database_settings.password,
|
"password": database_settings.password,
|
||||||
"database": database_settings.database,
|
"database": database_settings.database,
|
||||||
"ssl_disabled": True,
|
"charset": database_settings.charset,
|
||||||
|
"use_unicode": database_settings.use_unicode,
|
||||||
|
"buffered": database_settings.buffered,
|
||||||
|
"auth_plugin": database_settings.auth_plugin,
|
||||||
|
"ssl_disabled": False,
|
||||||
}
|
}
|
||||||
self._pool: Optional[MySQLConnectionPool] = None
|
self._pool: Optional[MySQLConnectionPool] = None
|
||||||
|
|
||||||
@@ -35,7 +39,7 @@ class MySQLPool:
|
|||||||
await cursor.execute("SELECT 1")
|
await cursor.execute("SELECT 1")
|
||||||
await cursor.fetchall()
|
await cursor.fetchall()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger = ServiceProviderABC.get_global_service(DBLogger)
|
logger = get_provider().get_service(DBLogger)
|
||||||
logger.fatal(f"Error connecting to the database: {e}")
|
logger.fatal(f"Error connecting to the database: {e}")
|
||||||
finally:
|
finally:
|
||||||
await con.close()
|
await con.close()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from psycopg_pool import AsyncConnectionPool, PoolTimeout
|
|||||||
from cpl.core.environment import Environment
|
from cpl.core.environment import Environment
|
||||||
from cpl.database.logger import DBLogger
|
from cpl.database.logger import DBLogger
|
||||||
from cpl.database.model import DatabaseSettings
|
from cpl.database.model import DatabaseSettings
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class PostgresPool:
|
class PostgresPool:
|
||||||
@@ -37,7 +37,7 @@ class PostgresPool:
|
|||||||
await pool.check_connection(con)
|
await pool.check_connection(con)
|
||||||
except PoolTimeout as e:
|
except PoolTimeout as e:
|
||||||
await pool.close()
|
await pool.close()
|
||||||
logger = ServiceProviderABC.get_global_service(DBLogger)
|
logger = get_provider().get_service(DBLogger)
|
||||||
logger.fatal(f"Failed to connect to the database", e)
|
logger.fatal(f"Failed to connect to the database", e)
|
||||||
self._pool = pool
|
self._pool = pool
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,16 @@ from cpl.database.model import Migration
|
|||||||
from cpl.database.model.server_type import ServerType, ServerTypes
|
from cpl.database.model.server_type import ServerType, ServerTypes
|
||||||
from cpl.database.schema.executed_migration import ExecutedMigration
|
from cpl.database.schema.executed_migration import ExecutedMigration
|
||||||
from cpl.database.schema.executed_migration_dao import ExecutedMigrationDao
|
from cpl.database.schema.executed_migration_dao import ExecutedMigrationDao
|
||||||
|
from cpl.dependency.hosted.startup_task import StartupTask
|
||||||
|
|
||||||
|
|
||||||
class MigrationService:
|
class MigrationService(StartupTask):
|
||||||
|
|
||||||
def __init__(self, logger: DBLogger, db: DBContextABC, executedMigrationDao: ExecutedMigrationDao):
|
def __init__(self, logger: DBLogger, db: DBContextABC, executed_migration_dao: ExecutedMigrationDao):
|
||||||
|
StartupTask.__init__(self)
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
self._db = db
|
self._db = db
|
||||||
self._executedMigrationDao = executedMigrationDao
|
self._executed_migration_dao = executed_migration_dao
|
||||||
|
|
||||||
self._script_directories: list[str] = []
|
self._script_directories: list[str] = []
|
||||||
|
|
||||||
@@ -23,12 +25,15 @@ class MigrationService:
|
|||||||
elif ServerType.server_type == ServerTypes.MYSQL:
|
elif ServerType.server_type == ServerTypes.MYSQL:
|
||||||
self.with_directory(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../scripts/mysql"))
|
self.with_directory(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../scripts/mysql"))
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
await self._execute(self._load_scripts())
|
||||||
|
|
||||||
def with_directory(self, directory: str) -> "MigrationService":
|
def with_directory(self, directory: str) -> "MigrationService":
|
||||||
self._script_directories.append(directory)
|
self._script_directories.append(directory)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
async def _get_migration_history(self) -> list[ExecutedMigration]:
|
async def _get_migration_history(self) -> list[ExecutedMigration]:
|
||||||
results = await self._db.select(f"SELECT * FROM {self._executedMigrationDao.table_name}")
|
results = await self._db.select(f"SELECT * FROM {self._executed_migration_dao.table_name}")
|
||||||
applied_migrations = []
|
applied_migrations = []
|
||||||
for result in results:
|
for result in results:
|
||||||
applied_migrations.append(ExecutedMigration(result[0]))
|
applied_migrations.append(ExecutedMigration(result[0]))
|
||||||
@@ -91,7 +96,7 @@ class MigrationService:
|
|||||||
try:
|
try:
|
||||||
# check if table exists
|
# check if table exists
|
||||||
if len(result) > 0:
|
if len(result) > 0:
|
||||||
migration_from_db = await self._executedMigrationDao.find_by_id(migration.name)
|
migration_from_db = await self._executed_migration_dao.find_by_id(migration.name)
|
||||||
if migration_from_db is not None:
|
if migration_from_db is not None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -99,12 +104,9 @@ class MigrationService:
|
|||||||
|
|
||||||
await self._db.execute(migration.script, multi=True)
|
await self._db.execute(migration.script, multi=True)
|
||||||
|
|
||||||
await self._executedMigrationDao.create(ExecutedMigration(migration.name), skip_editor=True)
|
await self._executed_migration_dao.create(ExecutedMigration(migration.name), skip_editor=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._logger.fatal(
|
self._logger.fatal(
|
||||||
f"Migration failed: {migration.name}\n{active_statement}",
|
f"Migration failed: {migration.name}\n{active_statement}",
|
||||||
e,
|
e,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def migrate(self):
|
|
||||||
await self._execute(self._load_scripts())
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from cpl.database.abc.data_seeder_abc import DataSeederABC
|
from cpl.database.abc.data_seeder_abc import DataSeederABC
|
||||||
from cpl.database.logger import DBLogger
|
from cpl.database.logger import DBLogger
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class SeederService:
|
class SeederService:
|
||||||
|
|
||||||
def __init__(self, provider: ServiceProviderABC):
|
def __init__(self, provider: ServiceProvider):
|
||||||
self._provider = provider
|
self._provider = provider
|
||||||
self._logger = provider.get_service(DBLogger)
|
self._logger = provider.get_service(DBLogger)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from .scope import Scope
|
from .context import get_provider, use_provider
|
||||||
from .scope_abc import ScopeABC
|
from .inject import inject
|
||||||
from .service_collection import ServiceCollection
|
from .service_collection import ServiceCollection
|
||||||
from .service_descriptor import ServiceDescriptor
|
from .service_descriptor import ServiceDescriptor
|
||||||
from .service_lifetime_enum import ServiceLifetimeEnum
|
from .service_lifetime import ServiceLifetimeEnum
|
||||||
|
from .service_provider import ServiceProvider
|
||||||
from .service_provider import ServiceProvider
|
from .service_provider import ServiceProvider
|
||||||
from .service_provider_abc import ServiceProviderABC
|
|
||||||
|
|||||||
22
src/cpl-dependency/cpl/dependency/context.py
Normal file
22
src/cpl-dependency/cpl/dependency/context.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import contextvars
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
|
||||||
|
_current_provider = contextvars.ContextVar("current_provider")
|
||||||
|
|
||||||
|
|
||||||
|
def use_root_provider(provider: "ServiceProvider"):
|
||||||
|
_current_provider.set(provider)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def use_provider(provider: "ServiceProvider"):
|
||||||
|
token = _current_provider.set(provider)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
_current_provider.reset(token)
|
||||||
|
|
||||||
|
|
||||||
|
def get_provider() -> "ServiceProvider":
|
||||||
|
return _current_provider.get()
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class HostedService(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
async def start(self): ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def stop(self): ...
|
||||||
6
src/cpl-dependency/cpl/dependency/hosted/startup_task.py
Normal file
6
src/cpl-dependency/cpl/dependency/hosted/startup_task.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class StartupTask(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
async def run(self): ...
|
||||||
42
src/cpl-dependency/cpl/dependency/inject.py
Normal file
42
src/cpl-dependency/cpl/dependency/inject.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import functools
|
||||||
|
from asyncio import iscoroutinefunction
|
||||||
|
from inspect import signature
|
||||||
|
|
||||||
|
from cpl.dependency.context import get_provider
|
||||||
|
|
||||||
|
|
||||||
|
def inject(f=None):
|
||||||
|
if f is None:
|
||||||
|
return functools.partial(inject)
|
||||||
|
|
||||||
|
if iscoroutinefunction(f):
|
||||||
|
|
||||||
|
@functools.wraps(f)
|
||||||
|
async def async_inner(*args, **kwargs):
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
|
provider: ServiceProvider | None = get_provider()
|
||||||
|
if provider is None:
|
||||||
|
raise ValueError(
|
||||||
|
"No provider in current context. Use 'with use_provider(provider):' to set the provider in the current context."
|
||||||
|
)
|
||||||
|
|
||||||
|
injection = [x for x in provider._build_by_signature(signature(f)) if x is not None]
|
||||||
|
return await f(*args, *injection, **kwargs)
|
||||||
|
|
||||||
|
return async_inner
|
||||||
|
|
||||||
|
@functools.wraps(f)
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
|
|
||||||
|
provider: ServiceProvider | None = get_provider()
|
||||||
|
if provider is None:
|
||||||
|
raise ValueError(
|
||||||
|
"No provider in current context. Use 'with use_provider(provider):' to set the provider in the current context."
|
||||||
|
)
|
||||||
|
|
||||||
|
injection = [x for x in provider._build_by_signature(signature(f)) if x is not None]
|
||||||
|
return f(*args, *injection, **kwargs)
|
||||||
|
|
||||||
|
return inner
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
from cpl.dependency.scope_abc import ScopeABC
|
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
|
||||||
|
|
||||||
|
|
||||||
class Scope(ScopeABC):
|
|
||||||
def __init__(self, service_provider: ServiceProviderABC):
|
|
||||||
self._service_provider = service_provider
|
|
||||||
self._service_provider.set_scope(self)
|
|
||||||
ScopeABC.__init__(self)
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, *args):
|
|
||||||
self.dispose()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def service_provider(self) -> ServiceProviderABC:
|
|
||||||
return self._service_provider
|
|
||||||
|
|
||||||
def dispose(self):
|
|
||||||
self._service_provider = None
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
|
|
||||||
class ScopeABC(ABC):
|
|
||||||
r"""ABC for the class :class:`cpl.dependency.scope.Scope`"""
|
|
||||||
|
|
||||||
def __init__(self): ...
|
|
||||||
|
|
||||||
@property
|
|
||||||
@abstractmethod
|
|
||||||
def service_provider(self):
|
|
||||||
r"""Returns to service provider of scope
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Object of type :class:`cpl.dependency.service_provider_abc.ServiceProviderABC`
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def dispose(self):
|
|
||||||
r"""Sets service_provider to None"""
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
from cpl.dependency.scope import Scope
|
|
||||||
from cpl.dependency.scope_abc import ScopeABC
|
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
|
||||||
|
|
||||||
|
|
||||||
class ScopeBuilder:
|
|
||||||
r"""Class to build :class:`cpl.dependency.scope.Scope`"""
|
|
||||||
|
|
||||||
def __init__(self, service_provider: ServiceProviderABC) -> None:
|
|
||||||
self._service_provider = service_provider
|
|
||||||
|
|
||||||
def build(self) -> ScopeABC:
|
|
||||||
r"""Returns scope
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Object of type :class:`cpl.dependency.scope.Scope`
|
|
||||||
"""
|
|
||||||
return Scope(self._service_provider)
|
|
||||||
@@ -3,10 +3,10 @@ from typing import Union, Type, Callable, Self
|
|||||||
from cpl.core.log.logger_abc import LoggerABC
|
from cpl.core.log.logger_abc import LoggerABC
|
||||||
from cpl.core.typing import T, Service
|
from cpl.core.typing import T, Service
|
||||||
from cpl.core.utils.cache import Cache
|
from cpl.core.utils.cache import Cache
|
||||||
|
from cpl.dependency.hosted.startup_task import StartupTask
|
||||||
from cpl.dependency.service_descriptor import ServiceDescriptor
|
from cpl.dependency.service_descriptor import ServiceDescriptor
|
||||||
from cpl.dependency.service_lifetime_enum import ServiceLifetimeEnum
|
from cpl.dependency.service_lifetime import ServiceLifetimeEnum
|
||||||
from cpl.dependency.service_provider import ServiceProvider
|
from cpl.dependency.service_provider import ServiceProvider
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceCollection:
|
class ServiceCollection:
|
||||||
@@ -62,9 +62,16 @@ class ServiceCollection:
|
|||||||
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(self) -> ServiceProviderABC:
|
def add_startup_task(self, task: Type[StartupTask]) -> Self:
|
||||||
|
self.add_singleton(StartupTask, task)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_hosted_service(self, service_type: T, service: Service = None) -> Self:
|
||||||
|
self._add_descriptor_by_lifetime(service_type, ServiceLifetimeEnum.hosted, service)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def build(self) -> ServiceProvider:
|
||||||
sp = ServiceProvider(self._service_descriptors)
|
sp = ServiceProvider(self._service_descriptors)
|
||||||
ServiceProviderABC.set_global_provider(sp)
|
|
||||||
return sp
|
return sp
|
||||||
|
|
||||||
def add_module(self, module: str | object) -> Self:
|
def add_module(self, module: str | object) -> Self:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Union, Optional
|
from typing import Union, Optional
|
||||||
|
|
||||||
from cpl.dependency.service_lifetime_enum import ServiceLifetimeEnum
|
from cpl.dependency.service_lifetime import ServiceLifetimeEnum
|
||||||
|
|
||||||
|
|
||||||
class ServiceDescriptor:
|
class ServiceDescriptor:
|
||||||
|
|||||||
8
src/cpl-dependency/cpl/dependency/service_lifetime.py
Normal file
8
src/cpl-dependency/cpl/dependency/service_lifetime.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from enum import Enum, auto
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceLifetimeEnum(Enum):
|
||||||
|
singleton = auto()
|
||||||
|
scoped = auto()
|
||||||
|
transient = auto()
|
||||||
|
hosted = auto()
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceLifetimeEnum(Enum):
|
|
||||||
singleton = 0
|
|
||||||
scoped = 1
|
|
||||||
transient = 2
|
|
||||||
@@ -1,40 +1,22 @@
|
|||||||
import copy
|
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
|
||||||
|
|
||||||
from cpl.core.configuration import Configuration
|
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, Source
|
||||||
from cpl.dependency.scope_abc import ScopeABC
|
from cpl.dependency import use_provider
|
||||||
from cpl.dependency.scope_builder import ScopeBuilder
|
|
||||||
from cpl.dependency.service_descriptor import ServiceDescriptor
|
from cpl.dependency.service_descriptor import ServiceDescriptor
|
||||||
from cpl.dependency.service_lifetime_enum import ServiceLifetimeEnum
|
from cpl.dependency.service_lifetime import ServiceLifetimeEnum
|
||||||
from cpl.dependency.service_provider_abc import ServiceProviderABC
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceProvider(ServiceProviderABC):
|
class ServiceProvider:
|
||||||
r"""Provider for the services
|
def __init__(self, service_descriptors: list[ServiceDescriptor], is_scope: bool = False):
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
service_descriptors: list[:class:`cpl.dependency.service_descriptor.ServiceDescriptor`]
|
|
||||||
Descriptor of the service
|
|
||||||
config: :class:`cpl.core.configuration.configuration_abc.ConfigurationABC`
|
|
||||||
CPL Configuration
|
|
||||||
db_context: Optional[:class:`cpl.database.context.database_context_abc.DatabaseContextABC`]
|
|
||||||
Database representation
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
service_descriptors: list[ServiceDescriptor],
|
|
||||||
):
|
|
||||||
ServiceProviderABC.__init__(self)
|
|
||||||
|
|
||||||
self._service_descriptors: list[ServiceDescriptor] = service_descriptors
|
self._service_descriptors: list[ServiceDescriptor] = service_descriptors
|
||||||
self._scope: Optional[ScopeABC] = None
|
self._is_scope = is_scope
|
||||||
|
|
||||||
def _find_service(self, service_type: type) -> Optional[ServiceDescriptor]:
|
def _find_service(self, service_type: type) -> Optional[ServiceDescriptor]:
|
||||||
origin_type = typing.get_origin(service_type) or service_type
|
origin_type = typing.get_origin(service_type) or service_type
|
||||||
@@ -67,14 +49,12 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
return descriptor.implementation
|
return descriptor.implementation
|
||||||
|
|
||||||
implementation = self._build_service(descriptor.service_type, origin_service_type=origin_service_type)
|
implementation = self._build_service(descriptor.service_type, origin_service_type=origin_service_type)
|
||||||
if descriptor.lifetime == ServiceLifetimeEnum.singleton:
|
if descriptor.lifetime in (ServiceLifetimeEnum.singleton, ServiceLifetimeEnum.scoped):
|
||||||
descriptor.implementation = implementation
|
descriptor.implementation = implementation
|
||||||
|
|
||||||
return implementation
|
return implementation
|
||||||
|
|
||||||
# raise Exception(f'Service {parameter.annotation} not found')
|
def _get_services(self, t: type, *args, service_type: type = None, **kwargs) -> list[Optional[object]]:
|
||||||
|
|
||||||
def _get_services(self, t: type, service_type: type = None, **kwargs) -> list[Optional[object]]:
|
|
||||||
implementations = []
|
implementations = []
|
||||||
for descriptor in self._service_descriptors:
|
for descriptor in self._service_descriptors:
|
||||||
if descriptor.service_type == t or issubclass(descriptor.service_type, t):
|
if descriptor.service_type == t or issubclass(descriptor.service_type, t):
|
||||||
@@ -83,16 +63,16 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
implementation = self._build_service(
|
implementation = self._build_service(
|
||||||
descriptor.service_type, origin_service_type=service_type, **kwargs
|
descriptor.service_type, *args, origin_service_type=service_type, **kwargs
|
||||||
)
|
)
|
||||||
if descriptor.lifetime == ServiceLifetimeEnum.singleton:
|
if descriptor.lifetime in (ServiceLifetimeEnum.singleton, ServiceLifetimeEnum.scoped):
|
||||||
descriptor.implementation = implementation
|
descriptor.implementation = implementation
|
||||||
|
|
||||||
implementations.append(implementation)
|
implementations.append(implementation)
|
||||||
|
|
||||||
return implementations
|
return implementations
|
||||||
|
|
||||||
def _build_by_signature(self, sig: Signature, origin_service_type: type = None) -> list[R]:
|
def _build_by_signature(self, sig: Signature, origin_service_type: type = None) -> list[T]:
|
||||||
params = []
|
params = []
|
||||||
for param in sig.parameters.items():
|
for param in sig.parameters.items():
|
||||||
parameter = param[1]
|
parameter = param[1]
|
||||||
@@ -103,7 +83,7 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
elif parameter.annotation == Source:
|
elif parameter.annotation == Source:
|
||||||
params.append(origin_service_type.__name__)
|
params.append(origin_service_type.__name__)
|
||||||
|
|
||||||
elif issubclass(parameter.annotation, ServiceProviderABC):
|
elif issubclass(parameter.annotation, ServiceProvider):
|
||||||
params.append(self)
|
params.append(self)
|
||||||
|
|
||||||
elif issubclass(parameter.annotation, Environment):
|
elif issubclass(parameter.annotation, Environment):
|
||||||
@@ -131,32 +111,31 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
service_type = type(descriptor.implementation)
|
service_type = type(descriptor.implementation)
|
||||||
else:
|
else:
|
||||||
service_type = descriptor.service_type
|
service_type = descriptor.service_type
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
sig = signature(service_type.__init__)
|
sig = signature(service_type.__init__)
|
||||||
params = self._build_by_signature(sig, origin_service_type)
|
params = self._build_by_signature(sig, origin_service_type)
|
||||||
|
|
||||||
return service_type(*params, *args, **kwargs)
|
return service_type(*params, *args, **kwargs)
|
||||||
|
|
||||||
def set_scope(self, scope: ScopeABC):
|
@contextmanager
|
||||||
self._scope = scope
|
def create_scope(self):
|
||||||
|
scoped_descriptors = []
|
||||||
def create_scope(self) -> ScopeABC:
|
for d in self._service_descriptors:
|
||||||
descriptors = []
|
if d.lifetime == ServiceLifetimeEnum.singleton:
|
||||||
|
scoped_descriptors.append(d)
|
||||||
for descriptor in self._service_descriptors:
|
|
||||||
if descriptor.lifetime == ServiceLifetimeEnum.singleton:
|
|
||||||
descriptors.append(descriptor)
|
|
||||||
else:
|
else:
|
||||||
descriptors.append(copy.deepcopy(descriptor))
|
scoped_descriptors.append(copy.deepcopy(d))
|
||||||
|
|
||||||
sb = ScopeBuilder(ServiceProvider(descriptors))
|
scoped_provider = ServiceProvider(scoped_descriptors, is_scope=True)
|
||||||
return sb.build()
|
with use_provider(scoped_provider):
|
||||||
|
yield scoped_provider
|
||||||
|
|
||||||
def get_service(self, service_type: T, *args, **kwargs) -> Optional[R]:
|
def get_hosted_services(self) -> list[Optional[T]]:
|
||||||
|
hosted_services = [self.get_service(d.service_type) for d in self._service_descriptors if d.lifetime == ServiceLifetimeEnum.hosted]
|
||||||
|
return hosted_services
|
||||||
|
|
||||||
|
def get_service(self, service_type: Type[T], *args, **kwargs) -> Optional[T]:
|
||||||
result = self._find_service(service_type)
|
result = self._find_service(service_type)
|
||||||
|
|
||||||
if result is None:
|
if result is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -164,11 +143,10 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
return result.implementation
|
return result.implementation
|
||||||
|
|
||||||
implementation = self._build_service(service_type, *args, **kwargs)
|
implementation = self._build_service(service_type, *args, **kwargs)
|
||||||
if (
|
|
||||||
result.lifetime == ServiceLifetimeEnum.singleton
|
if result.lifetime == ServiceLifetimeEnum.singleton:
|
||||||
or result.lifetime == ServiceLifetimeEnum.scoped
|
result.implementation = implementation
|
||||||
and self._scope is not None
|
elif result.lifetime == ServiceLifetimeEnum.scoped and self._is_scope:
|
||||||
):
|
|
||||||
result.implementation = implementation
|
result.implementation = implementation
|
||||||
|
|
||||||
return implementation
|
return implementation
|
||||||
@@ -179,14 +157,11 @@ class ServiceProvider(ServiceProviderABC):
|
|||||||
return descriptor.service_type
|
return descriptor.service_type
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_services(self, service_type: T, *args, **kwargs) -> list[Optional[R]]:
|
def get_services(self, service_type: Type[T], *args, **kwargs) -> list[Optional[T]]:
|
||||||
implementations = []
|
implementations = []
|
||||||
|
|
||||||
if typing.get_origin(service_type) == list:
|
if typing.get_origin(service_type) == list:
|
||||||
raise Exception(f"Invalid type {service_type}! Expected single type not list of type")
|
raise Exception(f"Invalid type {service_type}! Expected single type not list of type")
|
||||||
|
implementations.extend(self._get_services(service_type, None, *args, **kwargs))
|
||||||
implementations.extend(self._get_services(service_type))
|
|
||||||
|
|
||||||
return implementations
|
return implementations
|
||||||
|
|
||||||
def get_service_types(self, service_type: Type[T]) -> list[Type[T]]:
|
def get_service_types(self, service_type: Type[T]) -> list[Type[T]]:
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
import functools
|
|
||||||
from abc import abstractmethod, ABC
|
|
||||||
from inspect import Signature, signature, iscoroutinefunction
|
|
||||||
from typing import Optional, Type
|
|
||||||
|
|
||||||
from cpl.core.typing import T, R
|
|
||||||
from cpl.dependency.scope_abc import ScopeABC
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceProviderABC(ABC):
|
|
||||||
r"""ABC for the class :class:`cpl.dependency.service_provider.ServiceProvider`"""
|
|
||||||
|
|
||||||
_provider: Optional["ServiceProviderABC"] = None
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def __init__(self): ...
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set_global_provider(cls, provider: "ServiceProviderABC"):
|
|
||||||
cls._provider = provider
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_global_provider(cls) -> Optional["ServiceProviderABC"]:
|
|
||||||
return cls._provider
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_global_service(cls, instance_type: Type[T], *args, **kwargs) -> Optional[T]:
|
|
||||||
if cls._provider is None:
|
|
||||||
return None
|
|
||||||
return cls._provider.get_service(instance_type, *args, **kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_global_services(cls, instance_type: Type[T], *args, **kwargs) -> list[Optional[T]]:
|
|
||||||
if cls._provider is None:
|
|
||||||
return []
|
|
||||||
return cls._provider.get_services(instance_type, *args, **kwargs)
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def _build_by_signature(self, sig: Signature, origin_service_type: type = None) -> list[T]: ...
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def _build_service(self, service_type: type, *args, **kwargs) -> object:
|
|
||||||
r"""Creates instance of given type
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
instance_type: :class:`type`
|
|
||||||
The type of the searched instance
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Object of the given type
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def set_scope(self, scope: ScopeABC):
|
|
||||||
r"""Sets the scope of service provider
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
Object of type :class:`cpl.dependency.scope_abc.ScopeABC`
|
|
||||||
Service scope
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def create_scope(self) -> ScopeABC:
|
|
||||||
r"""Creates a service scope
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Object of type :class:`cpl.dependency.scope_abc.ScopeABC`
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def get_service(self, instance_type: Type[T], *args, **kwargs) -> Optional[T]:
|
|
||||||
r"""Returns instance of given type
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
instance_type: :class:`cpl.core.type.T`
|
|
||||||
The type of the searched instance
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Object of type Optional[:class:`cpl.core.type.T`]
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def get_service_type(self, instance_type: Type[T]) -> Optional[Type[T]]:
|
|
||||||
r"""Returns the registered service type for loggers
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
instance_type: :class:`cpl.core.type.T`
|
|
||||||
The type of the searched instance
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Object of type Optional[:class:`type`]
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def get_services(self, service_type: Type[T], *args, **kwargs) -> list[Optional[T]]:
|
|
||||||
r"""Returns instance of given type
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
service_type: :class:`cpl.core.type.T`
|
|
||||||
The type of the searched instance
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Object of type list[Optional[:class:`cpl.core.type.T`]
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def get_service_types(self, service_type: Type[T]) -> list[Type[T]]:
|
|
||||||
r"""Returns all registered service types
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
service_type: :class:`cpl.core.type.T`
|
|
||||||
The type of the searched instance
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Object of type list[:class:`type`]
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def inject(cls, f=None):
|
|
||||||
r"""Decorator to allow injection into static and class methods
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
f: Callable
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
function
|
|
||||||
"""
|
|
||||||
if f is None:
|
|
||||||
return functools.partial(cls.inject)
|
|
||||||
|
|
||||||
if iscoroutinefunction(f):
|
|
||||||
|
|
||||||
@functools.wraps(f)
|
|
||||||
async def async_inner(*args, **kwargs):
|
|
||||||
if cls._provider is None:
|
|
||||||
raise Exception(f"{cls.__name__} not build!")
|
|
||||||
|
|
||||||
injection = [x for x in cls._provider._build_by_signature(signature(f)) if x is not None]
|
|
||||||
return await f(*args, *injection, **kwargs)
|
|
||||||
|
|
||||||
return async_inner
|
|
||||||
|
|
||||||
@functools.wraps(f)
|
|
||||||
def inner(*args, **kwargs):
|
|
||||||
if cls._provider is None:
|
|
||||||
raise Exception(f"{cls.__name__} not build!")
|
|
||||||
|
|
||||||
injection = [x for x in cls._provider._build_by_signature(signature(f)) if x is not None]
|
|
||||||
return f(*args, *injection, **kwargs)
|
|
||||||
|
|
||||||
return inner
|
|
||||||
@@ -1 +1,7 @@
|
|||||||
|
from .array import Array
|
||||||
|
from .enumerable import Enumerable
|
||||||
|
from .immutable_list import ImmutableList
|
||||||
|
from .immutable_set import ImmutableSet
|
||||||
|
from .list import List
|
||||||
|
from .ordered_enumerable import OrderedEnumerable
|
||||||
|
from .set import Set
|
||||||
|
|||||||
44
src/cpl-query/cpl/query/array.py
Normal file
44
src/cpl-query/cpl/query/array.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from typing import Generic, Iterable, Optional
|
||||||
|
|
||||||
|
from cpl.core.typing import T
|
||||||
|
from cpl.query.list import List
|
||||||
|
from cpl.query.enumerable import Enumerable
|
||||||
|
|
||||||
|
|
||||||
|
class Array(Generic[T], List[T]):
|
||||||
|
def __init__(self, length: int, source: Optional[Iterable[T]] = None):
|
||||||
|
List.__init__(self, source)
|
||||||
|
self._length = length
|
||||||
|
|
||||||
|
@property
|
||||||
|
def length(self) -> int:
|
||||||
|
return len(self._source)
|
||||||
|
|
||||||
|
def add(self, item: T) -> None:
|
||||||
|
if self._length == self.length:
|
||||||
|
raise IndexError("Array is full")
|
||||||
|
self._source.append(item)
|
||||||
|
|
||||||
|
def extend(self, items: Iterable[T]) -> None:
|
||||||
|
if self._length == self.length:
|
||||||
|
raise IndexError("Array is full")
|
||||||
|
self._source.extend(items)
|
||||||
|
|
||||||
|
def insert(self, index: int, item: T) -> None:
|
||||||
|
if index < 0 or index > self.length:
|
||||||
|
raise IndexError("Index out of range")
|
||||||
|
self._source.insert(index, item)
|
||||||
|
|
||||||
|
def remove(self, item: T) -> None:
|
||||||
|
self._source.remove(item)
|
||||||
|
|
||||||
|
def pop(self, index: int = -1) -> T:
|
||||||
|
return self._source.pop(index)
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
self._source.clear()
|
||||||
|
|
||||||
|
def to_enumerable(self) -> "Enumerable[T]":
|
||||||
|
from cpl.query.enumerable import Enumerable
|
||||||
|
|
||||||
|
return Enumerable(self._source)
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
from itertools import islice, groupby
|
|
||||||
from typing import Generic, Callable, Iterable, Iterator, Dict, Tuple, Optional
|
|
||||||
|
|
||||||
from cpl.core.typing import T, R
|
|
||||||
from cpl.query.list import List
|
|
||||||
from cpl.query.typing import Predicate, K, Selector
|
|
||||||
|
|
||||||
|
|
||||||
class Collection(Generic[T]):
|
|
||||||
def __init__(self, source: Iterable[T]):
|
|
||||||
self._source = source
|
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[T]:
|
|
||||||
return iter(self._source)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def length(self) -> int:
|
|
||||||
return sum(1 for _ in self._source)
|
|
||||||
|
|
||||||
def where(self, f: Predicate) -> "Collection[T]":
|
|
||||||
return Collection([x for x in self._source if f(x)])
|
|
||||||
|
|
||||||
def select(self, f: Selector) -> "Collection[R]":
|
|
||||||
return Collection([f(x) for x in self._source])
|
|
||||||
|
|
||||||
def select_many(self, f: Callable[[T], Iterable[R]]) -> "Collection[R]":
|
|
||||||
return Collection([y for x in self._source for y in f(x)])
|
|
||||||
|
|
||||||
def take(self, count: int) -> "Collection[T]":
|
|
||||||
return Collection(islice(self._source, count))
|
|
||||||
|
|
||||||
def skip(self, count: int) -> "Collection[T]":
|
|
||||||
return Collection(islice(self._source, count, None))
|
|
||||||
|
|
||||||
def take_while(self, f: Predicate) -> "Collection[T]":
|
|
||||||
result = []
|
|
||||||
for x in self._source:
|
|
||||||
if f(x):
|
|
||||||
result.append(x)
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
return Collection(result)
|
|
||||||
|
|
||||||
def skip_while(self, f: Predicate) -> "Collection[T]":
|
|
||||||
it = iter(self._source)
|
|
||||||
for x in it:
|
|
||||||
if not f(x):
|
|
||||||
return Collection([x] + list(it))
|
|
||||||
return Collection([])
|
|
||||||
|
|
||||||
def distinct(self) -> "Collection[T]":
|
|
||||||
seen = set()
|
|
||||||
return Collection([x for x in self._source if not (x in seen or seen.add(x))])
|
|
||||||
|
|
||||||
def union(self, other: Iterable[T]) -> "Collection[T]":
|
|
||||||
return self.concat(other).distinct()
|
|
||||||
|
|
||||||
def intersect(self, other: Iterable[T]) -> "Collection[T]":
|
|
||||||
other_set = set(other)
|
|
||||||
return Collection([x for x in self._source if x in other_set])
|
|
||||||
|
|
||||||
def except_(self, other: Iterable[T]) -> "Collection[T]":
|
|
||||||
other_set = set(other)
|
|
||||||
return Collection([x for x in self._source if x not in other_set])
|
|
||||||
|
|
||||||
def concat(self, other: Iterable[T]) -> "Collection[T]":
|
|
||||||
return Collection(self._source) + list(other)
|
|
||||||
|
|
||||||
def count(self) -> int:
|
|
||||||
return len(list(self._source))
|
|
||||||
|
|
||||||
def sum(self, f: Optional[Selector] = None) -> R:
|
|
||||||
return sum([f(x) for x in self._source]) if f else sum(self._source) # type: ignore
|
|
||||||
|
|
||||||
def min(self, f: Optional[Selector] = None) -> R:
|
|
||||||
return min([f(x) for x in self._source]) if f else min(self._source) # type: ignore
|
|
||||||
|
|
||||||
def max(self, f: Optional[Selector] = None) -> R:
|
|
||||||
return max([f(x) for x in self._source]) if f else max(self._source) # type: ignore
|
|
||||||
|
|
||||||
def average(self, f: Optional[Callable[[T], float]] = None) -> float:
|
|
||||||
values = [f(x) for x in self._source] if f else list(self._source)
|
|
||||||
return sum(values) / len(values) if values else 0.0
|
|
||||||
|
|
||||||
def aggregate(self, func: Callable[[R, T], R], seed: Optional[R] = None) -> R:
|
|
||||||
it = iter(self._source)
|
|
||||||
if seed is None:
|
|
||||||
acc = next(it) # type: ignore
|
|
||||||
else:
|
|
||||||
acc = seed
|
|
||||||
for x in it:
|
|
||||||
acc = func(acc, x)
|
|
||||||
return acc
|
|
||||||
|
|
||||||
def any(self, f: Optional[Predicate] = None) -> bool:
|
|
||||||
return any(f(x) if f else x for x in self._source)
|
|
||||||
|
|
||||||
def all(self, f: Predicate) -> bool:
|
|
||||||
return all(f(x) for x in self._source)
|
|
||||||
|
|
||||||
def contains(self, value: T) -> bool:
|
|
||||||
return value in self._source
|
|
||||||
|
|
||||||
def sequence_equal(self, other: Iterable[T]) -> bool:
|
|
||||||
return list(self._source) == list(other)
|
|
||||||
|
|
||||||
def group_by(self, key_f: Callable[[T], K]) -> "Collection[Tuple[K, List[T]]]":
|
|
||||||
sorted_data = sorted(self._source, key=key_f)
|
|
||||||
return Collection([(key, list(group)) for key, group in groupby(sorted_data, key=key_f)])
|
|
||||||
|
|
||||||
def join(
|
|
||||||
self, inner: Iterable[R], outer_key: Callable[[T], K], inner_key: Callable[[R], K], result: Callable[[T, R], R]
|
|
||||||
) -> "Collection[R]":
|
|
||||||
lookup: Dict[K, List[R]] = {}
|
|
||||||
for i in inner:
|
|
||||||
k = inner_key(i)
|
|
||||||
lookup.setdefault(k, []).append(i)
|
|
||||||
return Collection([result(o, i) for o in self._source for i in lookup.get(outer_key(o), [])])
|
|
||||||
|
|
||||||
def first(self, f: Optional[Predicate] = None) -> T:
|
|
||||||
if f:
|
|
||||||
for x in self._source:
|
|
||||||
if f(x):
|
|
||||||
return x
|
|
||||||
raise ValueError("No matching element")
|
|
||||||
return next(iter(self._source))
|
|
||||||
|
|
||||||
def first_or_default(self, default: Optional[T] = None) -> Optional[T]:
|
|
||||||
return next(iter(self._source), default)
|
|
||||||
|
|
||||||
def last(self) -> T:
|
|
||||||
return list(self._source)[-1]
|
|
||||||
|
|
||||||
def single(self, f: Optional[Predicate] = None) -> T:
|
|
||||||
items = [x for x in self._source if f(x)] if f else list(self._source)
|
|
||||||
if len(items) != 1:
|
|
||||||
raise ValueError("Sequence does not contain exactly one element")
|
|
||||||
return items[0]
|
|
||||||
|
|
||||||
def to_list(self) -> List[T]:
|
|
||||||
return List(self._source)
|
|
||||||
|
|
||||||
def to_enumerable(self) -> "Enumerable[T]":
|
|
||||||
from cpl.query.enumerable import Enumerable
|
|
||||||
|
|
||||||
return Enumerable(self._source)
|
|
||||||
|
|
||||||
def to_dict(self, key_f: Callable[[T], K], value_f: Selector) -> Dict[K, R]:
|
|
||||||
return {key_f(x): value_f(x) for x in self._source}
|
|
||||||
|
|
||||||
def cast(self, t: Selector) -> "Collection[R]":
|
|
||||||
return Collection([t(x) for x in self._source])
|
|
||||||
|
|
||||||
def of_type(self, t: type) -> "Collection[T]":
|
|
||||||
return Collection([x for x in self._source if isinstance(x, t)])
|
|
||||||
|
|
||||||
def reverse(self) -> "Collection[T]":
|
|
||||||
return Collection(reversed(list(self._source)))
|
|
||||||
|
|
||||||
def zip(self, other: Iterable[R]) -> "Collection[Tuple[T, R]]":
|
|
||||||
return Collection(zip(self._source, other))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def range(start: int, count: int) -> "Collection[int]":
|
|
||||||
return Collection(range(start, start + count))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def repeat(value: T, count: int) -> "Collection[T]":
|
|
||||||
return Collection([value for _ in range(count)])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def empty() -> "Collection[T]":
|
|
||||||
return Collection([])
|
|
||||||
@@ -167,17 +167,13 @@ class Enumerable(Generic[T]):
|
|||||||
|
|
||||||
def to_list(self) -> "List[T]":
|
def to_list(self) -> "List[T]":
|
||||||
from cpl.query.list import List
|
from cpl.query.list import List
|
||||||
return List(self._source)
|
|
||||||
|
|
||||||
def to_collection(self) -> "Collection[T]":
|
return List(self)
|
||||||
from cpl.query.collection import Collection
|
|
||||||
|
|
||||||
return Collection(self._source)
|
|
||||||
|
|
||||||
def to_set(self) -> "Set[T]":
|
def to_set(self) -> "Set[T]":
|
||||||
from cpl.query.set import Set
|
from cpl.query.set import Set
|
||||||
|
|
||||||
return Set(self._source)
|
return Set(self)
|
||||||
|
|
||||||
def to_dict(self, key_f: Callable[[T], K], value_f: Selector) -> Dict[K, R]:
|
def to_dict(self, key_f: Callable[[T], K], value_f: Selector) -> Dict[K, R]:
|
||||||
return {key_f(x): value_f(x) for x in self._source}
|
return {key_f(x): value_f(x) for x in self._source}
|
||||||
@@ -194,6 +190,16 @@ class Enumerable(Generic[T]):
|
|||||||
def zip(self, other: Iterable[R]) -> "Enumerable[Tuple[T, R]]":
|
def zip(self, other: Iterable[R]) -> "Enumerable[Tuple[T, R]]":
|
||||||
return Enumerable(zip(self._source, other))
|
return Enumerable(zip(self._source, other))
|
||||||
|
|
||||||
|
def order_by(self, key_selector: Callable[[T], K]) -> "OrderedEnumerable[T]":
|
||||||
|
from cpl.query.ordered_enumerable import OrderedEnumerable
|
||||||
|
|
||||||
|
return OrderedEnumerable(self._source, [(key_selector, False)])
|
||||||
|
|
||||||
|
def order_by_descending(self, key_selector: Callable[[T], K]) -> "OrderedEnumerable[T]":
|
||||||
|
from cpl.query.ordered_enumerable import OrderedEnumerable
|
||||||
|
|
||||||
|
return OrderedEnumerable(self._source, [(key_selector, True)])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def range(start: int, count: int) -> "Enumerable[int]":
|
def range(start: int, count: int) -> "Enumerable[int]":
|
||||||
return Enumerable(range(start, start + count))
|
return Enumerable(range(start, start + count))
|
||||||
|
|||||||
@@ -6,40 +6,60 @@ from cpl.query.enumerable import Enumerable
|
|||||||
|
|
||||||
class ImmutableList(Generic[T], Enumerable[T]):
|
class ImmutableList(Generic[T], Enumerable[T]):
|
||||||
def __init__(self, source: Optional[Iterable[T]] = None):
|
def __init__(self, source: Optional[Iterable[T]] = None):
|
||||||
|
Enumerable.__init__(self, [])
|
||||||
if source is None:
|
if source is None:
|
||||||
source = []
|
source = []
|
||||||
|
elif not isinstance(source, list):
|
||||||
|
source = list(source)
|
||||||
|
|
||||||
Enumerable.__init__(self, source)
|
self.__source = source
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _items(self) -> list[T]:
|
def _source(self) -> list[T]:
|
||||||
return list(self._source)
|
return self.__source
|
||||||
|
|
||||||
|
@_source.setter
|
||||||
|
def _source(self, value: list[T]) -> None:
|
||||||
|
self.__source = value
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[T]:
|
def __iter__(self) -> Iterator[T]:
|
||||||
return iter(self._items)
|
return iter(self._source)
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return len(self._items)
|
return len(self._source)
|
||||||
|
|
||||||
def __getitem__(self, index: int) -> T:
|
def __getitem__(self, index: int) -> T:
|
||||||
return self._items[index]
|
return self._source[index]
|
||||||
|
|
||||||
def __contains__(self, item: T) -> bool:
|
def __contains__(self, item: T) -> bool:
|
||||||
return item in self._items
|
return item in self._source
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"List({self._items!r})"
|
return f"List({self._source!r})"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def length(self) -> int:
|
def length(self) -> int:
|
||||||
return len(self._items)
|
return len(self._source)
|
||||||
|
|
||||||
|
def add(self, item: T) -> None:
|
||||||
|
self._source.append(item)
|
||||||
|
|
||||||
|
def extend(self, items: Iterable[T]) -> None:
|
||||||
|
self._source.extend(items)
|
||||||
|
|
||||||
|
def insert(self, index: int, item: T) -> None:
|
||||||
|
self._source.insert(index, item)
|
||||||
|
|
||||||
|
def remove(self, item: T) -> None:
|
||||||
|
self._source.remove(item)
|
||||||
|
|
||||||
|
def pop(self, index: int = -1) -> T:
|
||||||
|
return self._source.pop(index)
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
self._source.clear()
|
||||||
|
|
||||||
def to_enumerable(self) -> "Enumerable[T]":
|
def to_enumerable(self) -> "Enumerable[T]":
|
||||||
from cpl.query.enumerable import Enumerable
|
from cpl.query.enumerable import Enumerable
|
||||||
|
|
||||||
return Enumerable(self._items)
|
return Enumerable(self._source)
|
||||||
|
|
||||||
def to_collection(self) -> "Collection[T]":
|
|
||||||
from cpl.query.collection import Collection
|
|
||||||
|
|
||||||
return Collection(self._items)
|
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ from cpl.query.enumerable import Enumerable
|
|||||||
|
|
||||||
class ImmutableSet(Generic[T], Enumerable[T]):
|
class ImmutableSet(Generic[T], Enumerable[T]):
|
||||||
def __init__(self, source: Optional[Iterable[T]] = None):
|
def __init__(self, source: Optional[Iterable[T]] = None):
|
||||||
|
Enumerable.__init__(self, [])
|
||||||
if source is None:
|
if source is None:
|
||||||
source = set()
|
source = set()
|
||||||
|
elif not isinstance(source, set):
|
||||||
|
source = set(source)
|
||||||
|
|
||||||
self.__source = source
|
self.__source = source
|
||||||
Enumerable.__init__(self, [])
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _source(self) -> set[T]:
|
def _source(self) -> set[T]:
|
||||||
@@ -41,4 +43,5 @@ class ImmutableSet(Generic[T], Enumerable[T]):
|
|||||||
|
|
||||||
def to_enumerable(self) -> "Enumerable[T]":
|
def to_enumerable(self) -> "Enumerable[T]":
|
||||||
from cpl.query.enumerable import Enumerable
|
from cpl.query.enumerable import Enumerable
|
||||||
return Enumerable(self._source)
|
|
||||||
|
return Enumerable(self._source)
|
||||||
|
|||||||
@@ -1,66 +1,36 @@
|
|||||||
from typing import Generic, Iterable, Iterator, Optional
|
from typing import Generic, Iterable, Optional
|
||||||
|
|
||||||
from cpl.core.typing import T
|
from cpl.core.typing import T
|
||||||
|
from cpl.query.immutable_list import ImmutableList
|
||||||
from cpl.query.enumerable import Enumerable
|
from cpl.query.enumerable import Enumerable
|
||||||
|
|
||||||
|
|
||||||
class List(Generic[T], Enumerable[T]):
|
class List(Generic[T], ImmutableList[T]):
|
||||||
def __init__(self, source: Optional[Iterable[T]] = None):
|
def __init__(self, source: Optional[Iterable[T]] = None):
|
||||||
if source is None:
|
ImmutableList.__init__(self, source)
|
||||||
source = []
|
|
||||||
|
|
||||||
Enumerable.__init__(self, source)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _items(self) -> list[T]:
|
|
||||||
return list(self._source)
|
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[T]:
|
|
||||||
return iter(self._items)
|
|
||||||
|
|
||||||
def __len__(self) -> int:
|
|
||||||
return len(self._items)
|
|
||||||
|
|
||||||
def __getitem__(self, index: int) -> T:
|
|
||||||
return self._items[index]
|
|
||||||
|
|
||||||
def __setitem__(self, index: int, value: T) -> None:
|
def __setitem__(self, index: int, value: T) -> None:
|
||||||
self._items[index] = value
|
self._source[index] = value
|
||||||
|
|
||||||
def __contains__(self, item: T) -> bool:
|
|
||||||
return item in self._items
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"List({self._items!r})"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def length(self) -> int:
|
|
||||||
return len(self._items)
|
|
||||||
|
|
||||||
def add(self, item: T) -> None:
|
def add(self, item: T) -> None:
|
||||||
self._items.append(item)
|
self._source.append(item)
|
||||||
|
|
||||||
def extend(self, items: Iterable[T]) -> None:
|
def extend(self, items: Iterable[T]) -> None:
|
||||||
self._items.extend(items)
|
self._source.extend(items)
|
||||||
|
|
||||||
def insert(self, index: int, item: T) -> None:
|
def insert(self, index: int, item: T) -> None:
|
||||||
self._items.insert(index, item)
|
self._source.insert(index, item)
|
||||||
|
|
||||||
def remove(self, item: T) -> None:
|
def remove(self, item: T) -> None:
|
||||||
self._items.remove(item)
|
self._source.remove(item)
|
||||||
|
|
||||||
def pop(self, index: int = -1) -> T:
|
def pop(self, index: int = -1) -> T:
|
||||||
return self._items.pop(index)
|
return self._source.pop(index)
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
self._items.clear()
|
self._source.clear()
|
||||||
|
|
||||||
def to_enumerable(self) -> "Enumerable[T]":
|
def to_enumerable(self) -> "Enumerable[T]":
|
||||||
from cpl.query.enumerable import Enumerable
|
from cpl.query.enumerable import Enumerable
|
||||||
|
|
||||||
return Enumerable(self._items)
|
return Enumerable(self._source)
|
||||||
|
|
||||||
def to_collection(self) -> "Collection[T]":
|
|
||||||
from cpl.query.collection import Collection
|
|
||||||
|
|
||||||
return Collection(self._items)
|
|
||||||
|
|||||||
40
src/cpl-query/cpl/query/ordered_enumerable.py
Normal file
40
src/cpl-query/cpl/query/ordered_enumerable.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from typing import Callable, List, Generic, Iterator
|
||||||
|
from cpl.core.typing import T
|
||||||
|
from cpl.query.enumerable import Enumerable
|
||||||
|
from cpl.query.typing import K
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedEnumerable(Enumerable[T]):
|
||||||
|
def __init__(self, source, key_selectors: List[tuple[Callable[[T], K], bool]]):
|
||||||
|
super().__init__(source)
|
||||||
|
self._key_selectors = key_selectors
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterator[T]:
|
||||||
|
def composite_key(x):
|
||||||
|
keys = []
|
||||||
|
for selector, descending in self._key_selectors:
|
||||||
|
k = selector(x)
|
||||||
|
keys.append((k, not descending))
|
||||||
|
return tuple(k if asc else _DescendingWrapper(k) for k, asc in keys)
|
||||||
|
|
||||||
|
return iter(sorted(self._source, key=composite_key))
|
||||||
|
|
||||||
|
def then_by(self, key_selector: Callable[[T], K]) -> "OrderedEnumerable[T]":
|
||||||
|
return OrderedEnumerable(self._source, self._key_selectors + [(key_selector, False)])
|
||||||
|
|
||||||
|
def then_by_descending(self, key_selector: Callable[[T], K]) -> "OrderedEnumerable[T]":
|
||||||
|
return OrderedEnumerable(self._source, self._key_selectors + [(key_selector, True)])
|
||||||
|
|
||||||
|
|
||||||
|
class _DescendingWrapper:
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.value > other.value
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self.value < other.value
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.value == other.value
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
from .sequence import Sequence
|
||||||
|
|||||||
@@ -1,39 +1,13 @@
|
|||||||
from typing import Generic, Iterable, Iterator, Optional
|
from typing import Generic, Iterable, Optional
|
||||||
|
|
||||||
from cpl.core.typing import T
|
from cpl.core.typing import T
|
||||||
|
from cpl.query.immutable_set import ImmutableSet
|
||||||
from cpl.query.enumerable import Enumerable
|
from cpl.query.enumerable import Enumerable
|
||||||
|
|
||||||
|
|
||||||
class Set(Generic[T], Enumerable[T]):
|
class Set(Generic[T], ImmutableSet[T]):
|
||||||
def __init__(self, source: Optional[Iterable[T]] = None):
|
def __init__(self, source: Optional[Iterable[T]] = None):
|
||||||
if source is None:
|
ImmutableSet.__init__(self, source)
|
||||||
source = set()
|
|
||||||
|
|
||||||
self.__source = source
|
|
||||||
Enumerable.__init__(self, [])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _source(self) -> set[T]:
|
|
||||||
return self.__source
|
|
||||||
|
|
||||||
@_source.setter
|
|
||||||
def _source(self, value: set[T]) -> None:
|
|
||||||
if not isinstance(value, set):
|
|
||||||
value = set(value)
|
|
||||||
|
|
||||||
self.__source = value
|
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[T]:
|
|
||||||
return iter(self._source)
|
|
||||||
|
|
||||||
def __len__(self) -> int:
|
|
||||||
return len(self._source)
|
|
||||||
|
|
||||||
def __contains__(self, item: T) -> bool:
|
|
||||||
return item in self._source
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"Set({self._source!r})"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def length(self) -> int:
|
def length(self) -> int:
|
||||||
@@ -50,4 +24,5 @@ class Set(Generic[T], Enumerable[T]):
|
|||||||
|
|
||||||
def to_enumerable(self) -> "Enumerable[T]":
|
def to_enumerable(self) -> "Enumerable[T]":
|
||||||
from cpl.query.enumerable import Enumerable
|
from cpl.query.enumerable import Enumerable
|
||||||
return Enumerable(self._source)
|
|
||||||
|
return Enumerable(self._source)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import unittest
|
|||||||
|
|
||||||
from cpl.application import ApplicationABC
|
from cpl.application import ApplicationABC
|
||||||
from cpl.core.configuration import ConfigurationABC
|
from cpl.core.configuration import ConfigurationABC
|
||||||
from cpl.dependency import ServiceProviderABC
|
from cpl.dependency import ServiceProvider
|
||||||
from unittests_cli.cli_test_suite import CLITestSuite
|
from unittests_cli.cli_test_suite import CLITestSuite
|
||||||
from unittests_core.core_test_suite import CoreTestSuite
|
from unittests_core.core_test_suite import CoreTestSuite
|
||||||
from unittests_query.query_test_suite import QueryTestSuite
|
from unittests_query.query_test_suite import QueryTestSuite
|
||||||
@@ -10,7 +10,7 @@ from unittests_translation.translation_test_suite import TranslationTestSuite
|
|||||||
|
|
||||||
|
|
||||||
class Application(ApplicationABC):
|
class Application(ApplicationABC):
|
||||||
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
|
def __init__(self, config: ConfigurationABC, services: ServiceProvider):
|
||||||
ApplicationABC.__init__(self, config, services)
|
ApplicationABC.__init__(self, config, services)
|
||||||
|
|
||||||
def configure(self): ...
|
def configure(self): ...
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import unittest
|
|||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
from cpl.dependency import ServiceCollection, ServiceLifetimeEnum, ServiceProviderABC
|
from cpl.dependency import ServiceCollection, ServiceLifetimeEnum, ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class ServiceCollectionTestCase(unittest.TestCase):
|
class ServiceCollectionTestCase(unittest.TestCase):
|
||||||
@@ -51,6 +51,6 @@ class ServiceCollectionTestCase(unittest.TestCase):
|
|||||||
service = self._sc._service_descriptors[0]
|
service = self._sc._service_descriptors[0]
|
||||||
self.assertIsNone(service.implementation)
|
self.assertIsNone(service.implementation)
|
||||||
sp = self._sc.build()
|
sp = self._sc.build()
|
||||||
self.assertTrue(isinstance(sp, ServiceProviderABC))
|
self.assertTrue(isinstance(sp, ServiceProvider))
|
||||||
self.assertTrue(isinstance(sp.get_service(Mock), Mock))
|
self.assertTrue(isinstance(sp.get_service(Mock), Mock))
|
||||||
self.assertIsNotNone(service.implementation)
|
self.assertIsNotNone(service.implementation)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
from cpl.dependency import ServiceCollection, ServiceProviderABC
|
from cpl.dependency import ServiceCollection, ServiceProvider
|
||||||
|
|
||||||
|
|
||||||
class ServiceCount:
|
class ServiceCount:
|
||||||
@@ -10,21 +10,21 @@ class ServiceCount:
|
|||||||
|
|
||||||
|
|
||||||
class TestService:
|
class TestService:
|
||||||
def __init__(self, sp: ServiceProviderABC, count: ServiceCount):
|
def __init__(self, sp: ServiceProvider, count: ServiceCount):
|
||||||
count.count += 1
|
count.count += 1
|
||||||
self.sp = sp
|
self.sp = sp
|
||||||
self.id = count.count
|
self.id = count.count
|
||||||
|
|
||||||
|
|
||||||
class DifferentService:
|
class DifferentService:
|
||||||
def __init__(self, sp: ServiceProviderABC, count: ServiceCount):
|
def __init__(self, sp: ServiceProvider, count: ServiceCount):
|
||||||
count.count += 1
|
count.count += 1
|
||||||
self.sp = sp
|
self.sp = sp
|
||||||
self.id = count.count
|
self.id = count.count
|
||||||
|
|
||||||
|
|
||||||
class MoreDifferentService:
|
class MoreDifferentService:
|
||||||
def __init__(self, sp: ServiceProviderABC, count: ServiceCount):
|
def __init__(self, sp: ServiceProvider, count: ServiceCount):
|
||||||
count.count += 1
|
count.count += 1
|
||||||
self.sp = sp
|
self.sp = sp
|
||||||
self.id = count.count
|
self.id = count.count
|
||||||
@@ -72,7 +72,7 @@ class ServiceProviderTestCase(unittest.TestCase):
|
|||||||
singleton = self._services.get_service(TestService)
|
singleton = self._services.get_service(TestService)
|
||||||
transient = self._services.get_service(DifferentService)
|
transient = self._services.get_service(DifferentService)
|
||||||
with self._services.create_scope() as scope:
|
with self._services.create_scope() as scope:
|
||||||
sp: ServiceProviderABC = scope.service_provider
|
sp: ServiceProvider = scope.service_provider
|
||||||
self.assertNotEqual(sp, self._services)
|
self.assertNotEqual(sp, self._services)
|
||||||
y = sp.get_service(DifferentService)
|
y = sp.get_service(DifferentService)
|
||||||
self.assertIsNotNone(y)
|
self.assertIsNotNone(y)
|
||||||
|
|||||||
Reference in New Issue
Block a user