aboutsummaryrefslogtreecommitdiff
path: root/tools/cru-py/cru/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/cru-py/cru/config.py')
-rw-r--r--tools/cru-py/cru/config.py128
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)