diff options
Diffstat (limited to 'tools/cru-py/cru/config.py')
-rw-r--r-- | tools/cru-py/cru/config.py | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/tools/cru-py/cru/config.py b/tools/cru-py/cru/config.py new file mode 100644 index 0000000..b0c83d5 --- /dev/null +++ b/tools/cru-py/cru/config.py @@ -0,0 +1,128 @@ +from typing import Any, TypeVar, Generic + +from .excp import CruInternalLogicError +from .value import ValueType, ValueGenerator, ValidationError + +T = TypeVar("T") + + +class ConfigItem(Generic[T]): + OptionalValueGenerator = ValueGenerator[T, []] | None + + def __init__(self, name: str, description: str, value_type: ValueType[T], value: T | None, default_value: T, *, + value_generator: OptionalValueGenerator = None) -> None: + self._name = name + self._description = description + self._value_type = value_type + self._default_value = default_value + self._value_generator = value_generator + self._value: T | None = value + + @property + def name(self) -> str: + return self._name + + @property + def description(self) -> str: + return self._description + + @property + def value_type(self) -> ValueType[T]: + return self._value_type + + @property + def default_value(self) -> T: + return self._default_value + + @property + def is_default(self) -> bool: + return self._value is None + + @property + def is_set(self) -> bool: + return not self.is_default + + @property + def value(self) -> T: + return self._value or self._default_value + + def set_value(self, v: T | str, /, allow_convert_from_str=False): + if allow_convert_from_str: + self._value = self.value_type.check_value(v) + else: + self._value = self.value_type.check_value_or_try_convert_from_str(v) + + @value.setter + def value(self, v: T) -> None: + self.set_value(v) + + @property + def value_generator(self) -> OptionalValueGenerator: + return self._value_generator + + def generate_value(self, allow_interactive=False) -> T | None: + if self.value_generator is None: return None + if self.value_generator.interactive and not allow_interactive: + return None + else: + v = self.generate_value() + try: + self.value_type.check_value(v) + return v + except ValidationError as e: + raise CruInternalLogicError("Config value generator returns invalid value.", name=self.name, inner=e) + + def copy(self) -> "ConfigItem": + return ConfigItem(self.name, self.description, self.value_type, + self._value.copy() if self._value is not None else None, self._default_value.copy(), + value_generator=self.value_generator) + + +class Configuration: + def __init__(self, items: None | list[ConfigItem] = None) -> None: + self._items: list[ConfigItem] = items or [] + + @property + def items(self) -> list[ConfigItem]: + return self._items + + @property + def item_map(self) -> dict[str, ConfigItem]: + return {i.name: i for i in self.items} + + def get_optional_item(self, name: str) -> ConfigItem | None: + for i in self.items: + if i.name == name: + return i + return None + + def clear(self) -> None: + self._items.clear() + + def has_item(self, name: str) -> bool: + return self.get_optional_item(name) is not None + + def add_item(self, item: ConfigItem): + i = self.get_optional_item(item.name) + if i is not None: + raise CruInternalLogicError("Config item of the name already exists.", name=item.name) + self.items.append(item) + return item + + def set_value(self, name: str, v: Any, /, allow_convert_from_str=False): + i = self.get_optional_item(name) + if i is None: + raise CruInternalLogicError("No config item of the name. Can't set value.", name=name) + i.set_value(v, allow_convert_from_str) + + def copy(self) -> "Configuration": + return Configuration([i.copy() for i in self.items]) + + def __getitem__(self, name: str) -> ConfigItem: + i = self.get_optional_item(name) + if i is not None: + return i + raise CruInternalLogicError('No config item of the name.', name=name) + + def __contains__(self, name: str): + return self.has_item(name) |