From: Jacob Lifshay Date: Tue, 8 Sep 2020 02:06:45 +0000 (-0700) Subject: start adding Config X-Git-Url: https://git.libre-soc.org/?p=utils.git;a=commitdiff_plain;h=da34f3bae9bdd1dcb6001eed905432fc8f419a56 start adding Config --- diff --git a/src/budget_sync/config.py b/src/budget_sync/config.py new file mode 100644 index 0000000..357857b --- /dev/null +++ b/src/budget_sync/config.py @@ -0,0 +1,99 @@ +import toml +import sys +from typing import Set, Dict, Any + + +class ConfigParseError(Exception): + pass + + +class Person: + def __init__(self, config: "Config", identifier: str): + self.config = config + self.identifier = identifier + + def __eq__(self, other): + return self.identifier == other.identifier + + def __hash__(self): + return hash(self.identifier) + + def __repr__(self): + return f"Person(config=..., identifier={self.identifier!r})" + + +class Config: + def __init__(self, bugzilla_url: str, people: Dict[str, Person], aliases: Dict[str, Person] = None): + self.bugzilla_url = bugzilla_url + self.people = people + if aliases is None: + aliases = {} + for i in people: + aliases[i.identifier] = i + self.aliases = aliases + + def __repr__(self): + return f"Config(bugzilla_url={self.bugzilla_url!r}, " \ + f"people={self.people!r}, aliases={self.aliases!r})" + + @staticmethod + def _parse_people(people: Any) -> Dict[str, Person]: + raise NotImplementedError() + + @staticmethod + def _parse_aliases(people: Dict[str, Person], aliases: Any) -> Dict[str, Person]: + if not isinstance(aliases, dict): + raise ConfigParseError("`aliases` entry must be a table") + retval = {} + raise NotImplementedError() + return retval + + @staticmethod + def _from_toml(parsed_toml: Dict[str, Any]) -> "Config": + people = None + aliases = None + bugzilla_url = None + for k, v in parsed_toml.items(): + if k == "people": + people = Config._parse_people(v) + elif k == "aliases": + aliases = v + elif k == "bugzilla_url": + if not isinstance(v, str): + raise ConfigParseError("`bugzilla_url` must be a string") + bugzilla_url = v + else: + raise ConfigParseError(f"unknown config entry: `{k}`") + + if people is None: + raise ConfigParseError("`people` key is missing") + + if bugzilla_url is None: + raise ConfigParseError("`bugzilla_url` key is missing") + + if aliases is not None: + aliases = Config._parse_aliases(people, aliases) + + return Config(bugzilla_url=bugzilla_url, + people=people, + aliases=aliases) + + @staticmethod + def from_str(text: str) -> "Config": + try: + parsed_toml = toml.loads(text) + except toml.TomlDecodeError as e: + new_err = ConfigParseError(f"TOML parse error: {e}") + raise new_err.with_traceback(sys.exc_info()[2]) + return Config._from_toml(parsed_toml) + + @staticmethod + def from_file(file: Any) -> "Config": + if isinstance(file, list): + raise TypeError("list is not a valid file or path") + try: + parsed_toml = toml.load(file) + except toml.TomlDecodeError as e: + new_err = ConfigParseError(f"TOML parse error: {e}") + raise new_err.with_traceback(sys.exc_info()[2]) + return Config._from_toml(parsed_toml) diff --git a/src/budget_sync/test/test_config.py b/src/budget_sync/test/test_config.py new file mode 100644 index 0000000..6c27899 --- /dev/null +++ b/src/budget_sync/test/test_config.py @@ -0,0 +1,19 @@ +import unittest +from budget_sync.config import Config, ConfigParseError + + +class TestConfig(unittest.TestCase): + def test_config_parsing(self): + def check_error(text: str, expected_error_text: str): + with self.assertRaises(ConfigParseError) as e: + Config.from_str(text) + self.assertEqual(str(e.exception), expected_error_text) + + def check(text: str, expected_repr_text: str): + self.assertEqual(repr(Config.from_str(text)), expected_repr_text) + + raise NotImplementedError("finish adding test cases") + + +if __name__ == "__main__": + unittest.main()