Move config to main package

This commit is contained in:
Joscha 2019-05-16 11:11:32 +00:00
parent 4dfd31e5fc
commit 5cfdb8f8e2
2 changed files with 2 additions and 0 deletions

128
cheuph/config.py Normal file
View file

@ -0,0 +1,128 @@
from typing import Any, Dict
__all__ = ["Fields", "Config", "ConfigView"]
Fields = Dict[str, Any]
class ConfigException(Exception):
pass
class Config:
@staticmethod
def from_tree(tree: Any, prefix: str = "") -> Fields:
"""
This function takes a nested dict using str-s as keys, and converts it
to a flat Fields dict. This means that an option's value can't be a
dict, and all dicts are expected to only use str-s as keys.
It uses the '.' character as separator, so {"a":{"b": 1, "c": 2}}
becomes {"a.b": 1, "a.c": 2}.
"""
result: Fields = {}
for k, v in tree.items():
if not isinstance(k, str):
raise ConfigException("Keys must be strings")
if "." in k:
raise ConfigException("Keys may not contain the '.' character")
name = prefix + k
if isinstance(v, dict):
result.update(Config.from_tree(v, prefix=name+"."))
else:
result[name] = v
return result
@staticmethod
def to_tree(fields: Fields) -> Fields:
"""
This function does the opposite of from_tree().
It uses the '.' character as separator, so {"a.b": 1, "a.c": 2}
becomes {"a":{"b": 1, "c": 2}}.
"""
result: Any = {}
for k, v in fields.items():
steps = k.split(".")
subdict = result
for step in steps[:-1]:
new_subdict = subdict.get(step, {})
subdict[step] = new_subdict
subdict = new_subdict
subdict[steps[-1]] = v
return result
def __init__(self, default_fields: Fields = {}) -> None:
self.default_fields = default_fields
self.fields: Fields = {}
def __getitem__(self, key: str) -> Any:
value = self.fields.get(key)
if value is None:
value = self.default_fields.get(key)
if value is None:
raise ConfigException(f"No value for {key} found")
return value
def __setitem__(self, key: str, value: Any) -> None:
if isinstance(value, dict):
raise ConfigException("No dicts allowed")
default = self.default_fields.get(key)
if value == default:
if self.fields.get(key)is not None:
self.fields.pop(key)
else:
self.fields[key] = value
@property
def view(self) -> "ConfigView":
return ConfigView(self.tree)
@property
def tree(self) -> Any:
both = dict(self.default_fields)
both.update(self.fields)
return self.to_tree(both)
class ConfigView:
def __init__(self,
fields: Any,
prefix: str = "",
) -> None:
self._fields = fields
self._prefix = prefix
def __getattr__(self, name: str) -> Any:
return self._get(name)
def __getitem__(self, name: str) -> Any:
return self._get(name)
def _get(self, name: str) -> Any:
"""
This function assumes that _default_fields and _fields have the same
dict structure.
"""
field = self._fields.get(name)
if isinstance(field, dict):
return ConfigView(field, f"{self._prefix}{name}.")
elif field is None:
raise ConfigException(f"Field {self._prefix}{name} does not exist")
return field