confighelper: add support for getlist and getdict

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Eric Callahan 2021-11-20 08:44:36 -05:00
parent c0ae10bef6
commit 0b25350ebc
1 changed files with 106 additions and 0 deletions

View File

@ -16,10 +16,12 @@ from typing import (
Callable,
IO,
Optional,
Tuple,
TypeVar,
Union,
Dict,
List,
Type,
)
if TYPE_CHECKING:
from moonraker import Server
@ -168,6 +170,110 @@ class ConfigHelper:
self.config.getfloat, option, default,
above, below, minval, maxval)
def getlists(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
list_type: Type = str,
separators: Tuple[str, ...] = ('\n',),
count: Optional[Tuple[Optional[int], ...]] = None
) -> Union[List[Any], _T]:
if count is not None and len(count) != len(separators):
raise ConfigError(
f"Option '{option}' in section "
f"[{self.section}]: length of 'count' argument must ",
"match length of 'separators' argument")
else:
count = tuple(None for _ in range(len(separators)))
def list_parser(value: str,
ltype: Type,
seps: Tuple[str, ...],
expected_cnt: Tuple[Optional[int], ...]
) -> List[Any]:
sep = seps[0]
seps = seps[1:]
cnt = expected_cnt[0]
expected_cnt = expected_cnt[1:]
ret: List[Any] = []
if seps:
sub_lists = [val.strip() for val in value.split(sep)
if val.strip()]
for sub_list in sub_lists:
ret.append(list_parser(sub_list, ltype, seps,
expected_cnt))
else:
ret = [ltype(val.strip()) for val in value.split(sep)
if val.strip()]
if cnt is not None and len(ret) != cnt:
raise ConfigError(
f"List length mismatch, expected {cnt}, "
f"parsed {len(ret)}")
return ret
def getlist_wrapper(sec: str, opt: str) -> List[Any]:
val = self.config.get(sec, opt)
assert count is not None
return list_parser(val, list_type, separators, count)
return self._get_option(getlist_wrapper, option, default)
def getlist(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
separator: str = '\n',
count: Optional[int] = None
) -> Union[List[str], _T]:
return self.getlists(option, default, str, (separator,), (count,))
def getintlist(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
separator: str = '\n',
count: Optional[int] = None
) -> Union[List[int], _T]:
return self.getlists(option, default, int, (separator,), (count,))
def getfloatlist(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
separator: str = '\n',
count: Optional[int] = None
) -> Union[List[float], _T]:
return self.getlists(option, default, float, (separator,), (count,))
def getdict(self,
option: str,
default: Union[SentinelClass, _T] = SENTINEL,
separators: Tuple[str, str] = ('\n', '='),
dict_type: Type = str,
allow_empty_fields: bool = False
) -> Union[Dict[str, Any], _T]:
if len(separators) != 2:
raise ConfigError(
"The `separators` argument of getdict() must be a Tuple"
"of length of 2")
def getdict_wrapper(sec: str, opt: str) -> Dict[str, Any]:
val = self.config.get(sec, opt)
ret: Dict[str, Any] = {}
for line in val.split(separators[0]):
line = line.strip()
if not line:
continue
parts = line.split(separators[1], 1)
if len(parts) == 1:
if allow_empty_fields:
ret[parts[0].strip()] = None
else:
raise ConfigError(
f"Failed to parse dictionary field, {line}")
else:
ret[parts[0].strip()] = dict_type(parts[1].strip())
return ret
return self._get_option(getdict_wrapper, option, default)
def read_supplemental_config(self, file_name: str) -> ConfigHelper:
cfg_file_path = os.path.normpath(os.path.expanduser(file_name))
if not os.path.isfile(cfg_file_path):