lazy.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. # -*- coding: utf-8 -*-
  2. """
  3. Lazy-Service-Wrapper für verzögerte Initialisierung.
  4. Ermöglicht die Deklaration von Service-Abhängigkeiten ohne
  5. sofortige Instanziierung.
  6. """
  7. from __future__ import annotations
  8. import threading
  9. from typing import Callable, Generic, TypeVar, Any
  10. T = TypeVar("T")
  11. class LazyService(Generic[T]):
  12. """
  13. Wrapper für verzögerte Service-Initialisierung.
  14. Die Instanz wird erst beim ersten Zugriff erstellt.
  15. Alle Attributzugriffe werden an die Instanz delegiert.
  16. Example:
  17. # Factory für verzögerte Erstellung
  18. db_service = LazyService(lambda: DatabaseService())
  19. # Keine Instanz erstellt
  20. print(db_service.is_initialized) # False
  21. # Erster Zugriff erstellt die Instanz
  22. result = db_service.query("SELECT * FROM users")
  23. print(db_service.is_initialized) # True
  24. """
  25. __slots__ = (
  26. "_factory",
  27. "_instance",
  28. "_initialized",
  29. "_lock",
  30. "_initializing",
  31. )
  32. def __init__(self, factory: Callable[[], T]) -> None:
  33. """
  34. Initialisiert den LazyService.
  35. Args:
  36. factory: Factory-Funktion zur Instanz-Erstellung.
  37. """
  38. object.__setattr__(self, "_factory", factory)
  39. object.__setattr__(self, "_instance", None)
  40. object.__setattr__(self, "_initialized", False)
  41. object.__setattr__(self, "_lock", threading.Lock())
  42. object.__setattr__(self, "_initializing", False)
  43. def _get_instance(self) -> T:
  44. """
  45. Holt oder erstellt die Instanz.
  46. Thread-safe durch Double-Checked Locking.
  47. Returns:
  48. Die Service-Instanz.
  49. """
  50. if self._initialized:
  51. return self._instance # type: ignore
  52. with self._lock:
  53. if not self._initialized:
  54. if self._initializing:
  55. raise RuntimeError(
  56. "Zirkuläre Abhängigkeit bei Lazy-Initialisierung"
  57. )
  58. object.__setattr__(self, "_initializing", True)
  59. try:
  60. instance = self._factory()
  61. object.__setattr__(self, "_instance", instance)
  62. object.__setattr__(self, "_initialized", True)
  63. finally:
  64. object.__setattr__(self, "_initializing", False)
  65. return self._instance # type: ignore
  66. @property
  67. def is_initialized(self) -> bool:
  68. """Prüft, ob die Instanz bereits erstellt wurde."""
  69. return self._initialized
  70. @property
  71. def instance(self) -> T:
  72. """Gibt die Instanz zurück (erstellt sie bei Bedarf)."""
  73. return self._get_instance()
  74. def reset(self) -> None:
  75. """
  76. Setzt den LazyService zurück.
  77. Die nächste Zugriff erstellt eine neue Instanz.
  78. """
  79. with self._lock:
  80. if self._initialized and self._instance is not None:
  81. # Dispose aufrufen falls vorhanden
  82. if hasattr(self._instance, "dispose"):
  83. try:
  84. self._instance.dispose()
  85. except Exception:
  86. pass
  87. elif hasattr(self._instance, "close"):
  88. try:
  89. self._instance.close()
  90. except Exception:
  91. pass
  92. object.__setattr__(self, "_instance", None)
  93. object.__setattr__(self, "_initialized", False)
  94. def __getattr__(self, name: str) -> Any:
  95. """Delegiert Attributzugriffe an die Instanz."""
  96. # Vermeidet Endlosrekursion bei speziellen Attributen
  97. if name.startswith("_"):
  98. raise AttributeError(
  99. f"'{type(self).__name__}' object has no attribute '{name}'"
  100. )
  101. return getattr(self._get_instance(), name)
  102. def __setattr__(self, name: str, value: Any) -> None:
  103. """Delegiert Attributsetzungen an die Instanz."""
  104. if name.startswith("_"):
  105. object.__setattr__(self, name, value)
  106. else:
  107. setattr(self._get_instance(), name, value)
  108. def __delattr__(self, name: str) -> None:
  109. """Delegiert Attributlöschungen an die Instanz."""
  110. if name.startswith("_"):
  111. object.__delattr__(self, name)
  112. else:
  113. delattr(self._get_instance(), name)
  114. def __call__(self, *args: Any, **kwargs: Any) -> Any:
  115. """Ermöglicht Aufruf als Funktion."""
  116. instance = self._get_instance()
  117. if callable(instance):
  118. return instance(*args, **kwargs)
  119. raise TypeError(
  120. f"'{type(instance).__name__}' object is not callable"
  121. )
  122. def __repr__(self) -> str:
  123. """String-Repräsentation."""
  124. if self._initialized:
  125. return f"LazyService(initialized={self._instance!r})"
  126. return f"LazyService(pending)"
  127. def __bool__(self) -> bool:
  128. """Boolean-Konvertierung."""
  129. return bool(self._get_instance())
  130. class LazyProxy(Generic[T]):
  131. """
  132. Proxy für lazy-geladene Services mit vollständiger Delegation.
  133. Ähnlich wie LazyService, aber mit erweiterter Protokoll-
  134. Unterstützung (Iterator, Context Manager, etc.).
  135. """
  136. def __init__(self, factory: Callable[[], T]) -> None:
  137. """
  138. Initialisiert den LazyProxy.
  139. Args:
  140. factory: Factory-Funktion zur Instanz-Erstellung.
  141. """
  142. object.__setattr__(self, "_lazy_factory", factory)
  143. object.__setattr__(self, "_lazy_instance", None)
  144. object.__setattr__(self, "_lazy_initialized", False)
  145. object.__setattr__(self, "_lazy_lock", threading.Lock())
  146. def _lazy_get_instance(self) -> T:
  147. """Holt oder erstellt die Instanz."""
  148. if not self._lazy_initialized:
  149. with self._lazy_lock:
  150. if not self._lazy_initialized:
  151. instance = self._lazy_factory()
  152. object.__setattr__(self, "_lazy_instance", instance)
  153. object.__setattr__(self, "_lazy_initialized", True)
  154. return self._lazy_instance # type: ignore
  155. def __getattr__(self, name: str) -> Any:
  156. """Delegiert Attributzugriffe."""
  157. if name.startswith("_lazy_"):
  158. raise AttributeError(name)
  159. return getattr(self._lazy_get_instance(), name)
  160. def __setattr__(self, name: str, value: Any) -> None:
  161. """Delegiert Attributsetzungen."""
  162. if name.startswith("_lazy_"):
  163. object.__setattr__(self, name, value)
  164. else:
  165. setattr(self._lazy_get_instance(), name, value)
  166. def __iter__(self):
  167. """Iterator-Protokoll."""
  168. return iter(self._lazy_get_instance())
  169. def __len__(self) -> int:
  170. """Length-Protokoll."""
  171. return len(self._lazy_get_instance()) # type: ignore
  172. def __getitem__(self, key: Any) -> Any:
  173. """Item-Protokoll."""
  174. return self._lazy_get_instance()[key] # type: ignore
  175. def __setitem__(self, key: Any, value: Any) -> None:
  176. """Item-Setzung."""
  177. self._lazy_get_instance()[key] = value # type: ignore
  178. def __enter__(self) -> T:
  179. """Context-Manager-Eintritt."""
  180. return self._lazy_get_instance().__enter__() # type: ignore
  181. def __exit__(self, exc_type, exc_val, exc_tb) -> Any:
  182. """Context-Manager-Austritt."""
  183. return self._lazy_get_instance().__exit__(exc_type, exc_val, exc_tb) # type: ignore
  184. def __await__(self):
  185. """Await-Protokoll für async."""
  186. return self._lazy_get_instance().__await__() # type: ignore
  187. def lazy(factory: Callable[[], T]) -> LazyService[T]:
  188. """
  189. Factory-Funktion für LazyService.
  190. Args:
  191. factory: Factory-Funktion.
  192. Returns:
  193. LazyService-Instanz.
  194. Example:
  195. @lazy
  196. def create_service():
  197. return ExpensiveService()
  198. service = create_service
  199. # Oder:
  200. service = lazy(lambda: ExpensiveService())
  201. """
  202. return LazyService(factory)