serviceCenter = DI-Container (Resolver, Registry, Context) fuer Service-Instanziierung serviceHub = Consumer-facing Aggregation (DB-Interfaces, Runtime-State, lazy Service-Resolution via serviceCenter) - modules/serviceHub/ erstellt: ServiceHub, PublicService, getInterface() - 22 Consumer-Dateien migriert (routes, features, tests): imports von modules.services auf serviceHub bzw. serviceCenter umgestellt - resolver.py: legacy fallback auf altes services/ entfernt - modules/services/ komplett geloescht (83 Dateien inkl. dead code mainAiChat.py) - pre-extraction: progress callback durch chunk-pipeline propagiert, operationType DATA_EXTRACT->DATA_ANALYSE fuer guenstigeres Modell
95 lines
2.5 KiB
Python
95 lines
2.5 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""
|
|
Service Center Resolver.
|
|
Resolution logic and dependency injection for service instantiation.
|
|
"""
|
|
|
|
import importlib
|
|
import logging
|
|
from typing import Any, Callable, Dict, Optional, Set
|
|
|
|
from modules.serviceCenter.context import ServiceCenterContext
|
|
from modules.serviceCenter.registry import CORE_SERVICES, IMPORTABLE_SERVICES
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
GetServiceFunc = Callable[[str], Any]
|
|
|
|
|
|
def _make_context_id(ctx: ServiceCenterContext) -> str:
|
|
"""Create a stable cache key from context."""
|
|
return f"{id(ctx.user)}_{ctx.mandate_id or ''}_{ctx.feature_instance_id or ''}"
|
|
|
|
|
|
def _load_service_class(module_path: str, class_name: str):
|
|
"""Load service class from module."""
|
|
module = importlib.import_module(module_path)
|
|
return getattr(module, class_name)
|
|
|
|
|
|
def resolve(
|
|
key: str,
|
|
context: ServiceCenterContext,
|
|
cache: Dict[str, Any],
|
|
resolving: Set[str],
|
|
) -> Any:
|
|
"""
|
|
Resolve a service by key. Uses cache, resolves dependencies recursively.
|
|
Raises KeyError if the service is not registered.
|
|
"""
|
|
cache_key = f"{_make_context_id(context)}_{key}"
|
|
if cache_key in cache:
|
|
return cache[cache_key]
|
|
|
|
if key in resolving:
|
|
raise RuntimeError(f"Circular dependency detected for service: {key}")
|
|
|
|
def get_service(dep_key: str) -> Any:
|
|
return resolve(dep_key, context, cache, resolving)
|
|
|
|
spec = CORE_SERVICES.get(key) or IMPORTABLE_SERVICES.get(key)
|
|
if spec:
|
|
cls = _load_service_class(spec["module"], spec["class"])
|
|
resolving.add(key)
|
|
try:
|
|
for dep in spec.get("dependencies", []):
|
|
get_service(dep)
|
|
finally:
|
|
resolving.discard(key)
|
|
instance = cls(context, get_service)
|
|
cache[cache_key] = instance
|
|
return instance
|
|
|
|
raise KeyError(f"Unknown service: {key}")
|
|
|
|
|
|
# Module-level cache for service instances (per context)
|
|
_resolution_cache: Dict[str, Any] = {}
|
|
_cache_lock: Optional[Any] = None
|
|
|
|
try:
|
|
from threading import Lock
|
|
_cache_lock = Lock()
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
def get_resolution_cache() -> Dict[str, Any]:
|
|
"""Get the module-level resolution cache (for preWarm/clear)."""
|
|
return _resolution_cache
|
|
|
|
|
|
def clear_cache() -> None:
|
|
"""Clear the resolution cache."""
|
|
lock = _cache_lock if _cache_lock is not None else _DummyLock()
|
|
with lock:
|
|
_resolution_cache.clear()
|
|
|
|
|
|
class _DummyLock:
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *args):
|
|
pass
|