from __future__ import annotations from abc import ABC, abstractmethod from typing import Any, Callable, Dict, Optional, TypeVar from core.logging import get_logger class BaseServiceProvider(ABC): def __init__(self, service_name: str) -> None: self.service_name = service_name self._initialized = False self.logger = get_logger("services.base").bind(service=service_name) @abstractmethod async def initialize(self, **kwargs: Any) -> bool: raise NotImplementedError @abstractmethod async def close(self) -> bool: raise NotImplementedError @abstractmethod async def health_check(self) -> Dict[str, Any]: raise NotImplementedError @property def is_initialized(self) -> bool: return self._initialized def _set_initialized(self, value: bool) -> None: self._initialized = value def get_service_info(self) -> Dict[str, Any]: return { "name": self.service_name, "initialized": self._initialized, "type": self.__class__.__name__, } class ServiceRegistry: _services: Dict[str, Callable[..., BaseServiceProvider]] = {} @classmethod def register( cls, service_name: str, factory: Callable[..., BaseServiceProvider] ) -> None: cls._services = {**cls._services, service_name: factory} @classmethod def get_service_factory( cls, service_name: str ) -> Optional[Callable[..., BaseServiceProvider]]: return cls._services.get(service_name) @classmethod def list_services(cls) -> list[str]: return sorted(cls._services.keys()) @classmethod def create_service( cls, service_name: str, **kwargs: Any ) -> Optional[BaseServiceProvider]: return cls.get_service(service_name, **kwargs) @classmethod def get_service( cls, service_name: str, **kwargs: Any ) -> Optional[BaseServiceProvider]: factory = cls.get_service_factory(service_name) if not factory: return None return factory(**kwargs) def register_service(service_name: str) -> Callable[[type], type]: def decorator(service_class: type) -> type: ServiceRegistry.register(service_name, service_class) return service_class return decorator TService = TypeVar("TService", bound=BaseServiceProvider) def register_service_instance(service_name: str, service: TService) -> TService: ServiceRegistry.register(service_name, lambda: service) return service def resolve_registered_services(service_names: list[str]) -> list[BaseServiceProvider]: services: list[BaseServiceProvider] = [] for service_name in service_names: service = ServiceRegistry.get_service(service_name) if service is None: raise RuntimeError(f"Service is not registered: {service_name}") services.append(service) return services async def close_registered_services(services: list[BaseServiceProvider]) -> bool: lifecycle_logger = get_logger("services.base.lifecycle") all_closed = True for service in reversed(services): try: closed = await service.close() except Exception as exc: # noqa: BLE001 lifecycle_logger.warning( "Failed to close service", service=service.service_name, error=str(exc), ) all_closed = False continue if not closed: lifecycle_logger.warning( "Service close returned false", service=service.service_name, ) all_closed = False return all_closed async def initialize_registered_services( service_names: list[str], ) -> tuple[bool, list[BaseServiceProvider]]: lifecycle_logger = get_logger("services.base.lifecycle") initialized_services: list[BaseServiceProvider] = [] try: services = resolve_registered_services(service_names) except RuntimeError as exc: lifecycle_logger.error("Failed to resolve registered services", error=str(exc)) return False, [] for service in services: try: initialized = await service.initialize() except Exception as exc: # noqa: BLE001 lifecycle_logger.warning( "Service initialization raised exception", service=service.service_name, error=str(exc), ) initialized = False if not initialized: lifecycle_logger.error( "Service initialization failed, rolling back", service=service.service_name, ) await close_registered_services(initialized_services) return False, [] initialized_services.append(service) return True, initialized_services