kupferbootstrap/config/scheme.py
InsanePrawn 11125e525f config: remove accidental scheme validation circumvention, improve errors, warn on config loading failure in main()
This will fail on values of the wrong type, but still warn about and allow unknown keys.
2022-11-09 20:19:04 +01:00

159 lines
3.9 KiB
Python

from __future__ import annotations
from munch import Munch
from typing import Any, Optional, Mapping, Union
from dataclass import DataClass, munchclass
from constants import Arch
@munchclass()
class SparseProfile(DataClass):
parent: Optional[str]
device: Optional[str]
flavour: Optional[str]
pkgs_include: Optional[list[str]]
pkgs_exclude: Optional[list[str]]
hostname: Optional[str]
username: Optional[str]
password: Optional[str]
size_extra_mb: Optional[Union[str, int]]
def __repr__(self):
return f'{type(self)}{dict.__repr__(self.toDict())}'
@munchclass()
class Profile(SparseProfile):
parent: Optional[str]
device: str
flavour: str
pkgs_include: list[str]
pkgs_exclude: list[str]
hostname: str
username: str
password: Optional[str]
size_extra_mb: Union[str, int]
@munchclass()
class WrapperSection(DataClass):
type: str # NOTE: rename to 'wrapper_type' if this causes problems
@munchclass()
class BuildSection(DataClass):
ccache: bool
clean_mode: bool
crosscompile: bool
crossdirect: bool
threads: int
@munchclass()
class PkgbuildsSection(DataClass):
git_repo: str
git_branch: str
@munchclass()
class PacmanSection(DataClass):
parallel_downloads: int
check_space: bool
repo_branch: str
@munchclass()
class PathsSection(DataClass):
cache_dir: str
chroots: str
pacman: str
packages: str
pkgbuilds: str
jumpdrive: str
images: str
ccache: str
rust: str
class ProfilesSection(DataClass):
current: str
default: SparseProfile
@classmethod
def transform(cls, values: Mapping[str, Any], validate: bool = True, allow_extra: bool = True):
results = {}
for k, v in values.items():
if k == 'current':
results[k] = v
continue
if not allow_extra and k != 'default':
raise Exception(f'Unknown key {k} in profiles section (Hint: extra_keys not allowed for some reason)')
if not isinstance(v, dict):
raise Exception(f'profile {v} is not a dict!')
results[k] = SparseProfile.fromDict(v, validate=True)
return results
def update(self, d, validate: bool = True):
Munch.update(self, self.transform(values=d, validate=validate))
def __repr__(self):
return f'{type(self)}{dict.__repr__(self.toDict())}'
@munchclass()
class Config(DataClass):
wrapper: WrapperSection
build: BuildSection
pkgbuilds: PkgbuildsSection
pacman: PacmanSection
paths: PathsSection
profiles: ProfilesSection
@classmethod
def fromDict(
cls,
values: Mapping[str, Any],
validate: bool = True,
allow_extra: bool = False,
allow_incomplete: bool = False,
):
values = dict(values) # copy for later modification
_vals = {}
for name, _class in cls._type_hints.items():
if name not in values:
if not allow_incomplete:
raise Exception(f'Config key "{name}" not in input dictionary')
continue
value = values.pop(name)
if not isinstance(value, _class):
value = _class.fromDict(value, validate=validate)
_vals[name] = value
if values:
if validate:
raise Exception(f'values contained unknown keys: {list(values.keys())}')
_vals |= values
return Config(_vals, validate=validate)
@munchclass()
class RuntimeConfiguration(DataClass):
verbose: bool
no_wrap: bool
error_shell: bool
config_file: Optional[str]
script_source_dir: Optional[str]
arch: Optional[Arch]
uid: Optional[int]
class ConfigLoadState(DataClass):
load_finished: bool
exception: Optional[Exception]
def __init__(self, d: dict = {}):
self.load_finished = False
self.exception = None
self.update(d)