159 lines
4.8 KiB
Python
159 lines
4.8 KiB
Python
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
|