""" config/loader.py — загрузка сервисов и глобальные конфигурационные переменные. Этот модуль загружает все YAML-конфиги сервисов при первом импорте и хранит их как модульные переменные. Все routes/*.py импортируют SERVICES, OPS_INDEX, DELAY, VERSION отсюда — без дублирования загрузки и без циклических импортов. Переменные модуля (вычисляются ОДИН раз при старте): VERSION — str, версия полигона (единая для app.py и root.py) SERVICES — {service_id: service_def} все сервисы OPS_INDEX — {svcOperationId: service_def} поиск сервиса по ID операции DELAY — float, задержка операции из MOCK_OP_DELAY (по умолчанию 0.1) """ import os import yaml # Константы для загрузки YAML _YAML_EXTS = (".yaml", ".yml") _DEFAULT_SERVICES_DIR = "services" def _load_all(services_dir=_DEFAULT_SERVICES_DIR): """Загружает все YAML-файлы из директории сервисов. Возвращает кортеж (services, ops_index): services — {service_id: {name, operations, cfsParams, stateParams, stateOut, ...}} ops_index — {svcOperationId: service_def} для быстрого поиска «чей это opId» """ if not os.path.isdir(services_dir): raise FileNotFoundError(f"Services directory not found: {services_dir}") services = {} ops_index = {} # Итерируем ВСЕ .yaml/.yml — без хардкода количества сервисов for fname in sorted(os.listdir(services_dir)): if not fname.lower().endswith(_YAML_EXTS): continue path = os.path.join(services_dir, fname) with open(path, "r", encoding="utf-8") as f: cfg = yaml.safe_load(f) if not cfg: continue svc_id = cfg["service_id"] services[svc_id] = cfg # Индекс: svcOperationId → service_def (нужен для поиска «чей это opId») for op in cfg.get("operations", []): ops_index[op["svcOperationId"]] = cfg return services, ops_index # --------------------------------------------------------------------------- # Модульные переменные — инициализируются ОДИН раз при первом импорте. # Все routes/*.py импортируют SERVICES, OPS_INDEX, DELAY отсюда. # --------------------------------------------------------------------------- # Загружаем конфиги сервисов (37+ YAML из services/) SERVICES, OPS_INDEX = _load_all() # Задержка операции в секундах (для синхронного sleep в /run). # Меняется на лету через POST /_mock/delay/ — см. mock_routes.py. # ⛔ Импортировать ТОЛЬКО через `import config.loader as _cfg` и читать `_cfg.DELAY` — # иначе float копируется по значению и не реагирует на изменение. DELAY = float(os.getenv("MOCK_OP_DELAY", "0.1")) # Версия полигона — единый источник правды для app.py и routes/root.py. # Меняется при КАЖДОМ изменении кода. VERSION = "0.2.4"