commit 90ed21c4a56bee0679c45c3d1b43219c74a7c59f Author: hhh Date: Fri Feb 2 21:53:35 2024 +0200 Initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e08268f --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.idea/ +/tests/ + +poetry.lock + +*/__pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..4267334 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +[nekomata is now in early-dev state](https://nekomata.kotikot.com/) diff --git a/neko_configparser/__init__.py b/neko_configparser/__init__.py new file mode 100644 index 0000000..2606a8f --- /dev/null +++ b/neko_configparser/__init__.py @@ -0,0 +1,4 @@ +from .neko.interfaces import ConfigParserInterface + + +__all__ = ['ConfigParserInterface'] diff --git a/neko_configparser/modules/__init__.py b/neko_configparser/modules/__init__.py new file mode 100644 index 0000000..2ae2839 --- /dev/null +++ b/neko_configparser/modules/__init__.py @@ -0,0 +1 @@ +pass diff --git a/neko_configparser/modules/toml_parser.py b/neko_configparser/modules/toml_parser.py new file mode 100644 index 0000000..37226da --- /dev/null +++ b/neko_configparser/modules/toml_parser.py @@ -0,0 +1,19 @@ +import toml + + +class TomlConfig(dict): + def __init__(self, config_path: str = 'config.neko.toml', _config: dict = None): + try: + if _config is None: + super().__init__(**toml.load(config_path)) + else: + super().__init__(**_config) + + except FileNotFoundError: + super().__init__() + + def __getattr__(self, item): + if type(self.get(item)) is not dict: + return self.get(item) + else: + return TomlConfig(_config=self.get(item)) diff --git a/neko_configparser/modules/writer.py b/neko_configparser/modules/writer.py new file mode 100644 index 0000000..677b0d9 --- /dev/null +++ b/neko_configparser/modules/writer.py @@ -0,0 +1,79 @@ +import toml +from typing import Dict, Any + + +def _update_dicts(dict1, dict2): + """ + Ensures that dict1 exists in dict2 or merges them in right way + """ + for key in dict1: + if key not in dict2 and not isinstance(dict1[key], WriteTomlConfig): + dict2[key] = dict1[key] + elif isinstance(dict1[key], dict) and isinstance(dict2[key], dict): + _update_dicts(dict1[key], dict2[key]) + + +class WriteTomlConfig(dict): + def __init__(self, filename: str = 'config.neko.toml'): + self.__filename = filename + self.__parent = None + super().__init__() + self.__load_from_file(filename) + + @classmethod + def __create_subsidiary(cls, data, parent): + d = cls(None) # type: ignore + d.__parent = parent + + for key, value in data.items(): + if isinstance(value, dict): + d[key] = cls.__create_subsidiary(value, parent=parent) + + return d + + def __getitem__(self, key): + val = super().__getitem__(key) + return val + + def __setitem__(self, key, value): + if isinstance(value, dict): + value = type(self).__create_subsidiary(value, parent=self) + super().__setitem__(key, value) + self.__propagate_write() + + def __delitem__(self, key): + super().__delitem__(key) + self.__propagate_write() + + def __propagate_write(self): + if self.__parent: + self.__parent.__propagate_write() + else: + self.__write() + + def to_dict(self): + standard_dict = {} + for key, value in self.items(): + if isinstance(value, type(self)): + standard_dict[key] = value.to_dict() + else: + standard_dict[key] = value + return standard_dict + + def __write(self): + with open(self.__filename, 'w') as f: + toml.dump(self.to_dict(), f) + + def __load_from_file(self, filename=None): + if not filename: + return + try: + data = toml.load(filename) + for key, value in data.items(): + self[key] = value + except FileNotFoundError: + open(filename, 'w+').close() + + def ensure(self, data: Dict[str, Any]): + _update_dicts(data, self) + self.__propagate_write() diff --git a/neko_configparser/neko/__init__.py b/neko_configparser/neko/__init__.py new file mode 100644 index 0000000..2ae2839 --- /dev/null +++ b/neko_configparser/neko/__init__.py @@ -0,0 +1 @@ +pass diff --git a/neko_configparser/neko/interfaces.py b/neko_configparser/neko/interfaces.py new file mode 100644 index 0000000..10b77ab --- /dev/null +++ b/neko_configparser/neko/interfaces.py @@ -0,0 +1,30 @@ +from ..modules.toml_parser import TomlConfig +from ..modules.writer import WriteTomlConfig +from typing import Dict, Any + + +class ConfigParserInterface: + @staticmethod + def parse_config(config_path: str = 'config.neko.toml') -> TomlConfig: + """ + Get toml configuration in our handy representation + :param config_path: + :return: + """ + return TomlConfig(config_path) + + @staticmethod + def ensure_config( + partition: str, + module_config: Dict[str, Any], + config_path: str = 'config.neko.toml' + ) -> None: + """ + Validates your module config and adds values if needed + :param partition: Your module name or any name under which you want to see this + key-value configuration table + :param module_config: Dictionary of default config keys and values, so they will + be added and verified in config + :param config_path: Path of config, if you want to use custom + """ + WriteTomlConfig(config_path).ensure({partition: module_config}) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6806995 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "neko-configparser" +version = "0.1.0" +description = "" +authors = ["hhh"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.11" +toml = "^0.10.2" + +[tool.poetry.group.dev.dependencies] +rich = "^13.7.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api"