from typing import Any, TypeVar, Callable class ParseError(Exception): """An error for when parsing a config fails""" def __init__(self, keypath: list[str], issue: str): path = '.'.join(keypath) message = f'{path}: {issue}' super().__init__(message) self.keypath = keypath self.issue = issue def assert_key_of_type(config: dict[str, Any], key: str, kind: Any): if key not in config: raise ParseError([key], 'missing') if not isinstance(config[key], kind): raise ParseError([key], f'type of "{key}" was {type(config[key])}, expected {kind}') T = TypeVar('T') def parse_nested_config(config: dict[str, Any], key: str, parse: Callable[[dict[str, Any]], T]) -> T: assert_key_of_type(config, key, dict) try: return parse(config[key]) except ParseError as e: raise ParseError([key, *e.keypath], e.issue)