31 lines
908 B
Python
31 lines
908 B
Python
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)
|