singleton.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. # -*- coding: utf-8 -*-
  2. """
  3. Singleton-Scope für Services.
  4. Erstellt genau eine Instanz pro Service, die während der
  5. gesamten Lebensdauer der Anwendung wiederverwendet wird.
  6. """
  7. from __future__ import annotations
  8. import threading
  9. from typing import Callable, Generic, TypeVar
  10. from trixy_core.service.scopes.scope import ServiceScope, ScopeType
  11. T = TypeVar("T")
  12. class SingletonScope(ServiceScope[T], Generic[T]):
  13. """
  14. Singleton-Scope für Services.
  15. Garantiert, dass nur eine Instanz eines Services existiert.
  16. Thread-safe durch Locking.
  17. Example:
  18. scope = SingletonScope(lambda: MyService())
  19. instance1 = scope.get_instance("my_service")
  20. instance2 = scope.get_instance("my_service")
  21. assert instance1 is instance2
  22. """
  23. scope_type = ScopeType.SINGLETON
  24. def __init__(self, factory: Callable[[], T]) -> None:
  25. """
  26. Initialisiert den Singleton-Scope.
  27. Args:
  28. factory: Factory-Funktion zur Instanz-Erstellung.
  29. """
  30. super().__init__(factory)
  31. self._instance: T | None = None
  32. self._lock = threading.Lock()
  33. self._initialized = False
  34. @property
  35. def has_instance(self) -> bool:
  36. """Prüft, ob bereits eine Instanz erstellt wurde."""
  37. return self._initialized
  38. @property
  39. def instance(self) -> T | None:
  40. """Gibt die Instanz zurück (ohne sie zu erstellen)."""
  41. return self._instance
  42. def get_instance(self, key: str) -> T:
  43. """
  44. Holt oder erstellt die Singleton-Instanz.
  45. Thread-safe durch Double-Checked Locking.
  46. Args:
  47. key: Service-Schlüssel (wird für Singleton ignoriert).
  48. Returns:
  49. Die Singleton-Instanz.
  50. """
  51. if self._initialized:
  52. return self._instance # type: ignore
  53. with self._lock:
  54. if not self._initialized:
  55. self._instance = self._factory()
  56. self._initialized = True
  57. return self._instance # type: ignore
  58. def reset(self) -> None:
  59. """
  60. Setzt den Singleton zurück.
  61. Die nächste Anfrage erstellt eine neue Instanz.
  62. """
  63. with self._lock:
  64. if self._instance is not None:
  65. # Dispose aufrufen falls vorhanden
  66. if hasattr(self._instance, "dispose"):
  67. try:
  68. self._instance.dispose()
  69. except Exception:
  70. pass
  71. elif hasattr(self._instance, "close"):
  72. try:
  73. self._instance.close()
  74. except Exception:
  75. pass
  76. self._instance = None
  77. self._initialized = False
  78. def dispose(self) -> None:
  79. """Disposed den Singleton."""
  80. self.reset()
  81. class LazySingletonScope(SingletonScope[T], Generic[T]):
  82. """
  83. Lazy-Singleton-Scope.
  84. Erstellt die Instanz erst beim ersten Zugriff.
  85. Identisch mit SingletonScope, aber mit explizitem Namen.
  86. """
  87. pass
  88. class EagerSingletonScope(SingletonScope[T], Generic[T]):
  89. """
  90. Eager-Singleton-Scope.
  91. Erstellt die Instanz sofort bei der Scope-Erstellung.
  92. """
  93. def __init__(self, factory: Callable[[], T]) -> None:
  94. """
  95. Initialisiert den Eager-Singleton-Scope.
  96. Args:
  97. factory: Factory-Funktion zur Instanz-Erstellung.
  98. """
  99. super().__init__(factory)
  100. # Instanz sofort erstellen
  101. self._instance = self._factory()
  102. self._initialized = True