1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
from typing import Any, TypeVar, Generic
from .error 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)
|