Newer
Older
gn-synapse-client-py / src / gnexus_synapse / config.py
@Eugene Sukhodolskiy Eugene Sukhodolskiy 1 day ago 1 KB Initial client library skeleton (v0.1.0)
"""Конфигурация клиента.

Прямые аргументы конструктора сильнее env: клиент в коде сервиса обычно
создают с url/api_key, а env-фолбэк — путь «развернул по умолчанию».
Имена переменных фиксированы для всей экосистемы: SYNAPSE_URL,
SYNAPSE_API_KEY, SYNAPSE_TIMEOUT, SYNAPSE_DEFAULT_SOURCE.
"""

from __future__ import annotations

import os
from collections.abc import Mapping
from dataclasses import dataclass, field

ENV_URL = "SYNAPSE_URL"
ENV_API_KEY = "SYNAPSE_API_KEY"
ENV_TIMEOUT = "SYNAPSE_TIMEOUT"
ENV_DEFAULT_SOURCE = "SYNAPSE_DEFAULT_SOURCE"

DEFAULT_TIMEOUT = 10.0


@dataclass(frozen=True, slots=True)
class SynapseConfig:
    base_url: str  # без хвостового "/"
    api_key: str | None = None
    timeout: float = DEFAULT_TIMEOUT
    default_source: str | None = None
    env: Mapping[str, str] = field(default_factory=lambda: dict(os.environ), repr=False)

    @classmethod
    def from_env(cls, env: Mapping[str, str] | None = None) -> SynapseConfig:
        env = dict(os.environ) if env is None else dict(env)
        return cls(
            base_url=env.get(ENV_URL, ""),
            api_key=env.get(ENV_API_KEY) or None,
            timeout=_timeout(env),
            default_source=env.get(ENV_DEFAULT_SOURCE) or None,
            env=env,
        )

    def url(self, path: str) -> str:
        """base_url + path; путь в path — с начальным '/'."""
        return f"{self.base_url.rstrip('/')}{path}"


def _timeout(env: Mapping[str, str]) -> float:
    raw = (env.get(ENV_TIMEOUT) or "").strip()
    if not raw:
        return DEFAULT_TIMEOUT
    try:
        value = float(raw)
    except ValueError:
        raise ValueError(f"{ENV_TIMEOUT}={raw!r} не числится как число секунд") from None
    if value <= 0:
        raise ValueError(f"{ENV_TIMEOUT}={raw!r} должен быть > 0")
    return value