from __future__ import annotations from copy import deepcopy from pathlib import Path from typing import Any import yaml def load_yaml_config(path: str | Path) -> dict[str, Any]: with Path(path).open("r", encoding="utf-8") as handle: return yaml.safe_load(handle) or {} def deep_update(dst: dict[str, Any], src: dict[str, Any]) -> dict[str, Any]: for key, value in src.items(): if isinstance(value, dict) and isinstance(dst.get(key), dict): deep_update(dst[key], value) else: dst[key] = value return dst def apply_dotlist_overrides(cfg: dict[str, Any], overrides: list[str] | None) -> dict[str, Any]: if not overrides: return cfg updated = deepcopy(cfg) for item in overrides: if "=" not in item: raise ValueError(f"Invalid override '{item}'. Expected key=value format.") key, raw_value = item.split("=", 1) value = yaml.safe_load(raw_value) parts = key.split(".") current = updated for part in parts[:-1]: if part not in current or not isinstance(current[part], dict): current[part] = {} current = current[part] current[parts[-1]] = value return updated __all__ = ["load_yaml_config", "deep_update", "apply_dotlist_overrides"]