| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- # -*- 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")
|