54 lines
1.5 KiB
Python
54 lines
1.5 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
from ..errors import ConfigError, ValidationError
|
|
from ..validate import validate_dict
|
|
from .schemas import GLOBAL_SCHEMA, HOST_SCHEMA
|
|
|
|
|
|
def load_yaml_file(path: Path) -> dict[str, Any]:
|
|
if not path.exists():
|
|
raise ConfigError(f"Missing config file: {path}")
|
|
try:
|
|
raw = path.read_text(encoding="utf-8")
|
|
except OSError as e:
|
|
raise ConfigError(f"Cannot read config file: {path}: {e}") from e
|
|
|
|
try:
|
|
data = yaml.safe_load(raw)
|
|
except yaml.YAMLError as e:
|
|
raise ConfigError(f"Invalid YAML in {path}: {e}") from e
|
|
|
|
if data is None:
|
|
data = {}
|
|
if not isinstance(data, dict):
|
|
raise ConfigError(f"Config root must be a mapping in {path}")
|
|
return data
|
|
|
|
|
|
def load_global_config(path: Path) -> dict[str, Any]:
|
|
data = load_yaml_file(path)
|
|
try:
|
|
return validate_dict(data, GLOBAL_SCHEMA, path="global")
|
|
except ValidationError as e:
|
|
raise ConfigError(f"Invalid global config at {path}: {format_validation_error(e)}") from e
|
|
|
|
|
|
def load_host_config(path: Path) -> dict[str, Any]:
|
|
data = load_yaml_file(path)
|
|
try:
|
|
return validate_dict(data, HOST_SCHEMA, path="host")
|
|
except ValidationError as e:
|
|
raise ConfigError(f"Invalid host config at {path}: {format_validation_error(e)}") from e
|
|
|
|
|
|
def format_validation_error(err: ValidationError) -> str:
|
|
if err.path:
|
|
return f"{err.path}: {err}"
|
|
return str(err)
|
|
|