start adding Config
authorJacob Lifshay <programmerjake@gmail.com>
Tue, 8 Sep 2020 02:06:45 +0000 (19:06 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Tue, 8 Sep 2020 02:06:45 +0000 (19:06 -0700)
src/budget_sync/config.py [new file with mode: 0644]
src/budget_sync/test/test_config.py [new file with mode: 0644]

diff --git a/src/budget_sync/config.py b/src/budget_sync/config.py
new file mode 100644 (file)
index 0000000..357857b
--- /dev/null
@@ -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 (file)
index 0000000..6c27899
--- /dev/null
@@ -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()