platform-core/modules/serviceCenter/resolver.py
ValueOn AG 4f8473bd70
Some checks failed
Deploy Plattform-Core (Int) / test (push) Failing after 1m2s
Deploy Plattform-Core (Int) / deploy (push) Has been skipped
cleaned servicebag and removed servicehub
2026-06-08 23:35:31 +02:00

98 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 getResolutionCache() -> Dict[str, Any]:
"""Get the module-level resolution cache (for preWarm/clear)."""
return _resolution_cache
def clearCache() -> 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