from __future__ import annotations from dataclasses import dataclass, field from typing import Any @dataclass(frozen=True) class TimeoutConfig: connect: float = 1.0 read: float = 5.0 write: float = 5.0 pool: float = 1.0 @classmethod def from_mapping(cls, value: dict[str, Any] | None) -> "TimeoutConfig": value = value or {} return cls( connect=float(value.get("connect", cls.connect)), read=float(value.get("read", cls.read)), write=float(value.get("write", cls.write)), pool=float(value.get("pool", cls.pool)), ) @dataclass(frozen=True) class RetryConfig: attempts: int = 1 backoff: float = 0.2 statuses: tuple[int, ...] = field(default_factory=lambda: (502, 503, 504)) methods: tuple[str, ...] = field(default_factory=lambda: ("GET", "HEAD", "OPTIONS")) @classmethod def from_mapping(cls, value: dict[str, Any] | None) -> "RetryConfig": value = value or {} return cls( attempts=max(int(value.get("attempts", cls.attempts)), 1), backoff=float(value.get("backoff", cls.backoff)), statuses=tuple(int(item) for item in value.get("statuses", cls().statuses)), methods=tuple(str(item).upper() for item in value.get("methods", cls().methods)), ) @dataclass(frozen=True) class CircuitBreakerConfig: enabled: bool = False fail_max: int = 5 reset_timeout: int = 30 @classmethod def from_mapping(cls, value: dict[str, Any] | None) -> "CircuitBreakerConfig": value = value or {} return cls( enabled=bool(value.get("enabled", cls.enabled)), fail_max=max(int(value.get("fail_max", cls.fail_max)), 1), reset_timeout=max(int(value.get("reset_timeout", cls.reset_timeout)), 1), ) @dataclass(frozen=True) class ServiceConfig: name: str base_url: str token: str | None = None timeout: TimeoutConfig = field(default_factory=TimeoutConfig) retry: RetryConfig = field(default_factory=RetryConfig) circuit_breaker: CircuitBreakerConfig = field(default_factory=CircuitBreakerConfig) @classmethod def from_mapping(cls, name: str, value: dict[str, Any]) -> "ServiceConfig": return cls( name=name, base_url=str(value["base_url"]).rstrip("/"), token=value.get("token"), timeout=TimeoutConfig.from_mapping(value.get("timeout")), retry=RetryConfig.from_mapping(value.get("retry")), circuit_breaker=CircuitBreakerConfig.from_mapping( value.get("circuit_breaker") ), )