# -*- coding: utf-8 -*- """ Scoped-Scopes für request-basierte Services. Erstellt eine Instanz pro Scope (z.B. HTTP-Request). """ from __future__ import annotations import threading from typing import Callable, Generic, TypeVar from trixy_core.service.scopes.scope import ( ServiceScope, ScopeContext, ScopeType, get_current_scope, ) T = TypeVar("T") class ScopedScope(ServiceScope[T], Generic[T]): """ Scoped-Scope für request-basierte Services. Eine Instanz wird pro ScopeContext erstellt und geteilt. Wenn kein Scope aktiv ist, verhält sich dieser wie Transient. Example: scope = ScopedScope(lambda: MyService()) with scope_context(): instance1 = scope.get_instance("my_service") instance2 = scope.get_instance("my_service") assert instance1 is instance2 with scope_context(): instance3 = scope.get_instance("my_service") assert instance3 is not instance1 """ scope_type = ScopeType.SCOPED def __init__( self, factory: Callable[[], T], require_scope: bool = False, ) -> None: """ Initialisiert den Scoped-Scope. Args: factory: Factory-Funktion zur Instanz-Erstellung. require_scope: Ob ein aktiver Scope erforderlich ist. """ super().__init__(factory) self._require_scope = require_scope def get_instance(self, key: str) -> T: """ Holt oder erstellt eine Instanz im aktuellen Scope. Args: key: Service-Schlüssel. Returns: Service-Instanz. Raises: RuntimeError: Wenn require_scope=True und kein Scope aktiv. """ scope = get_current_scope() if scope is None: if self._require_scope: raise RuntimeError( f"Kein aktiver Scope für Service '{key}'" ) # Fallback zu Transient return self._factory() # Im Scope nachschauen if scope.contains(key): return scope.get(key) # Neue Instanz erstellen und cachen instance = self._factory() scope.set(key, instance) return instance class RequestScope(ScopedScope[T], Generic[T]): """ Alias für ScopedScope, optimiert für HTTP-Requests. Erfordert immer einen aktiven Scope. """ def __init__(self, factory: Callable[[], T]) -> None: """ Initialisiert den Request-Scope. Args: factory: Factory-Funktion zur Instanz-Erstellung. """ super().__init__(factory, require_scope=True) class ThreadScope(ServiceScope[T], Generic[T]): """ Thread-lokaler Scope für Services. Eine Instanz pro Thread. """ scope_type = ScopeType.THREAD def __init__(self, factory: Callable[[], T]) -> None: """ Initialisiert den Thread-Scope. Args: factory: Factory-Funktion zur Instanz-Erstellung. """ super().__init__(factory) self._local = threading.local() def get_instance(self, key: str) -> T: """ Holt oder erstellt die Thread-lokale Instanz. Args: key: Service-Schlüssel. Returns: Thread-lokale Service-Instanz. """ if not hasattr(self._local, "instance"): self._local.instance = self._factory() return self._local.instance def dispose(self) -> None: """Disposed die Instanz des aktuellen Threads.""" if hasattr(self._local, "instance"): instance = self._local.instance if hasattr(instance, "dispose"): try: instance.dispose() except Exception: pass elif hasattr(instance, "close"): try: instance.close() except Exception: pass delattr(self._local, "instance")