diff options
Diffstat (limited to 'tools/cru-py/cru/config.py')
-rw-r--r-- | tools/cru-py/cru/config.py | 99 |
1 files changed, 72 insertions, 27 deletions
diff --git a/tools/cru-py/cru/config.py b/tools/cru-py/cru/config.py index 3fd994b..926ed6a 100644 --- a/tools/cru-py/cru/config.py +++ b/tools/cru-py/cru/config.py @@ -1,10 +1,12 @@ -from typing import TypeVar, Generic -import copy +from __future__ import annotations +from typing import TypeVar, Generic +from ._error import CruInternalError, CruException from .list import CruUniqueKeyList -from ._error import CruInternalError from .value import ( + INTEGER_VALUE_TYPE, + TEXT_VALUE_TYPE, CruValueTypeError, ValueGeneratorBase, ValueType, @@ -13,23 +15,32 @@ from .value import ( _T = TypeVar("_T") +class CruConfigError(CruException): + def __init__(self, message: str, item: ConfigItem, *args, **kwargs): + super().__init__(message, *args, **kwargs) + self._item = item + + @property + def item(self) -> ConfigItem: + return self._item + + class ConfigItem(Generic[_T]): def __init__( self, name: str, description: str, value_type: ValueType[_T], - value: _T | None, - default_value: _T, - *, - value_generator: ValueGeneratorBase | None = None, + value: _T | None = None, + /, + default: ValueGeneratorBase[_T] | _T | None = 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 = value + self._default = default + self._default_value: _T | None = None @property def name(self) -> str: @@ -44,24 +55,32 @@ class ConfigItem(Generic[_T]): return self._value_type @property - def default_value(self) -> _T: - return self._default_value + def is_set(self) -> bool: + return self._value is not None @property - def is_default(self) -> bool: - return self._value is None + def value(self) -> _T: + if self._value is None: + raise CruConfigError("Config value is not set.", self) + return self._value @property - def is_set(self) -> bool: - return not self.is_default + def value_or_default(self) -> _T: + if self._value is not None: + return self._value + elif self._default_value is not None: + return self._default_value + else: + self._default_value = self.generate_default_value() + return self._default_value @property - def value(self) -> _T: - return self._value or self._default_value + def default(self) -> ValueGeneratorBase[_T] | _T | None: + return self._default @property - def value_generator(self) -> ValueGeneratorBase | None: - return self._value_generator + def can_generate_default(self) -> bool: + return self.default is not None def set_value(self, v: _T | str, /, allow_convert_from_str=False): if allow_convert_from_str: @@ -69,16 +88,21 @@ class ConfigItem(Generic[_T]): else: self._value = self.value_type.check_value_or_try_convert_from_str(v) - def generate_value(self) -> _T | None: - if self.value_generator is None: - return None - v = self.generate_value() + def generate_default_value(self) -> _T: + if self.default is None: + raise CruConfigError( + "Config item does not support default value generation.", self + ) + elif isinstance(self.default, ValueGeneratorBase): + v = self.default.generate() + else: + v = self.default try: self.value_type.check_value(v) return v except CruValueTypeError as e: raise CruInternalError( - "Config value generator returns invalid value." + "Config value generator returns an invalid value." ) from e def copy(self) -> "ConfigItem": @@ -86,12 +110,33 @@ class ConfigItem(Generic[_T]): self.name, self.description, self.value_type, - copy.deepcopy(self._value) if self._value is not None else None, - copy.deepcopy(self._default_value), - value_generator=self.value_generator, + self.value, + self.default, ) class Configuration(CruUniqueKeyList[ConfigItem, str]): def __init__(self): super().__init__(lambda c: c.name) + + def add_text_config( + self, + name: str, + description: str, + value: str | None = None, + default: ValueGeneratorBase[str] | str | None = None, + ) -> ConfigItem: + item = ConfigItem(name, description, TEXT_VALUE_TYPE, value, default) + self.add(item) + return item + + def add_int_config( + self, + name: str, + description: str, + value: int | None = None, + default: ValueGeneratorBase[int] | int | None = None, + ) -> ConfigItem: + item = ConfigItem(name, description, INTEGER_VALUE_TYPE, value, default) + self.add(item) + return item |