mmrech's picture
feat: v0.2 — real FITS support, TAI/UTC fix, SkyBoT, two-pass bg
41d98e2 verified
"""AsteroidNET configuration loader with env-var overrides."""
from __future__ import annotations
import hashlib, os, copy
from pathlib import Path
from typing import Any
import yaml
_DEFAULTS = Path(__file__).parent / "defaults.yaml"
_ENV_PREFIX = "ASTEROIDNET_"
def load_config(path: str | Path | None = None) -> dict:
with open(_DEFAULTS) as f:
cfg = yaml.safe_load(f)
if path:
with open(path) as f:
user = yaml.safe_load(f) or {}
cfg = _deep_merge(cfg, user)
cfg = _apply_env(cfg)
cfg["_hash"] = hashlib.sha256(
yaml.dump(cfg, sort_keys=True).encode()
).hexdigest()[:12]
return cfg
def _deep_merge(base: dict, override: dict) -> dict:
out = copy.deepcopy(base)
for k, v in override.items():
if k in out and isinstance(out[k], dict) and isinstance(v, dict):
out[k] = _deep_merge(out[k], v)
else:
out[k] = v
return out
def _apply_env(cfg: dict, _prefix: str = _ENV_PREFIX) -> dict:
"""Override config from ASTEROIDNET_SECTION__KEY=value env vars."""
out = copy.deepcopy(cfg)
for key, val in os.environ.items():
if not key.startswith(_prefix):
continue
parts = key[len(_prefix):].lower().split("__")
node = out
for part in parts[:-1]:
node = node.setdefault(part, {})
leaf = parts[-1]
try:
node[leaf] = yaml.safe_load(val)
except Exception:
node[leaf] = val
return out