API scoped requests #186
Some checks failed
Test before pr merge / test-lint (pull_request) Failing after 6s
Build on push / prepare (push) Successful in 9s
Build on push / query (push) Successful in 18s
Build on push / core (push) Successful in 21s
Build on push / dependency (push) Successful in 14s
Build on push / api (push) Has been cancelled
Build on push / auth (push) Has been cancelled
Build on push / application (push) Has been cancelled
Build on push / database (push) Has been cancelled
Build on push / mail (push) Has been cancelled
Build on push / translation (push) Has been cancelled

This commit is contained in:
2025-09-24 21:47:52 +02:00
parent 287f5e3149
commit b49f663ae0
9 changed files with 67 additions and 48 deletions

View File

@@ -30,7 +30,6 @@ from cpl.core.configuration import Configuration
from cpl.dependency.inject import inject
from cpl.dependency.service_provider import ServiceProvider
PolicyInput = Union[dict[str, PolicyResolver], Policy]

View File

@@ -1,13 +0,0 @@
from cpl.api.abc import ASGIMiddleware
from cpl.dependency.service_provider import ServiceProvider
class ScopeMiddleware(ASGIMiddleware):
def __init__(self, app, provider: ServiceProvider):
ASGIMiddleware.__init__(self, app)
self._app = app
self._provider = provider
async def __call__(self, scope, receive, send):
with self._provider.create_scope():
await self._app(scope, receive, send)

View File

@@ -9,15 +9,18 @@ from starlette.types import Scope, Receive, Send
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
from cpl.api.logger import APILogger
from cpl.api.typing import TRequest
from cpl.dependency.inject import inject
from cpl.dependency.service_provider import ServiceProvider
_request_context: ContextVar[Union[TRequest, None]] = ContextVar("request", default=None)
class RequestMiddleware(ASGIMiddleware):
def __init__(self, app, logger: APILogger):
def __init__(self, app, provider: ServiceProvider, logger: APILogger):
ASGIMiddleware.__init__(self, app)
self._provider = provider
self._logger = logger
self._ctx_token = None
@@ -27,7 +30,8 @@ class RequestMiddleware(ASGIMiddleware):
await self.set_request_data(request)
try:
await self._app(scope, receive, send)
with self._provider.create_scope():
inject(await self._app(scope, receive, send))
finally:
await self.clean_request_data()

View File

@@ -10,7 +10,6 @@ def inject(f=None):
return functools.partial(inject)
if iscoroutinefunction(f):
@functools.wraps(f)
async def async_inner(*args, **kwargs):
from cpl.dependency.service_provider import ServiceProvider

View File

@@ -14,23 +14,9 @@ from cpl.dependency.service_lifetime import ServiceLifetimeEnum
class ServiceProvider:
r"""Provider for the services
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],
):
def __init__(self, service_descriptors: list[ServiceDescriptor], is_scope: bool = False):
self._service_descriptors: list[ServiceDescriptor] = service_descriptors
self._is_scope = is_scope
def _find_service(self, service_type: type) -> Optional[ServiceDescriptor]:
origin_type = typing.get_origin(service_type) or service_type
@@ -57,13 +43,13 @@ class ServiceProvider:
def _get_service(self, parameter: Parameter, origin_service_type: type = None) -> Optional[object]:
for descriptor in self._service_descriptors:
if descriptor.service_type == parameter.annotation or issubclass(
descriptor.service_type, parameter.annotation
descriptor.service_type, parameter.annotation
):
if descriptor.implementation is not None:
return descriptor.implementation
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
return implementation
@@ -81,7 +67,7 @@ class ServiceProvider:
implementation = self._build_service(
descriptor.service_type, origin_service_type=service_type, **kwargs
)
if descriptor.lifetime == ServiceLifetimeEnum.singleton:
if descriptor.lifetime in (ServiceLifetimeEnum.singleton, ServiceLifetimeEnum.scoped):
descriptor.implementation = implementation
implementations.append(implementation)
@@ -127,12 +113,10 @@ class ServiceProvider:
service_type = type(descriptor.implementation)
else:
service_type = descriptor.service_type
break
sig = signature(service_type.__init__)
params = self._build_by_signature(sig, origin_service_type)
return service_type(*params, *args, **kwargs)
@contextmanager
@@ -144,13 +128,12 @@ class ServiceProvider:
else:
scoped_descriptors.append(copy.deepcopy(d))
scoped_provider = ServiceProvider(scoped_descriptors)
scoped_provider = ServiceProvider(scoped_descriptors, is_scope=True)
with use_provider(scoped_provider):
yield scoped_provider
def get_service(self, service_type: T, *args, **kwargs) -> Optional[R]:
result = self._find_service(service_type)
if result is None:
return None
@@ -158,9 +141,10 @@ class ServiceProvider:
return result.implementation
implementation = self._build_service(service_type, *args, **kwargs)
if (
result.lifetime in (ServiceLifetimeEnum.singleton, ServiceLifetimeEnum.scoped)
):
if result.lifetime == ServiceLifetimeEnum.singleton:
result.implementation = implementation
elif result.lifetime == ServiceLifetimeEnum.scoped and self._is_scope:
result.implementation = implementation
return implementation
@@ -173,12 +157,9 @@ class ServiceProvider:
def get_services(self, service_type: T, *args, **kwargs) -> list[Optional[R]]:
implementations = []
if typing.get_origin(service_type) == list:
raise Exception(f"Invalid type {service_type}! Expected single type not list of type")
implementations.extend(self._get_services(service_type))
return implementations
def get_service_types(self, service_type: Type[T]) -> list[Type[T]]: