123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- import json
- from os import PathLike
- from pathlib import Path
- from typing import Any, Union, Optional, Literal
- import yaml
-
- class Config(object):
- def __init__(self, data: dict, base_path: str):
- self._write_mode = True
- self._base_path = base_path
-
- for key, val in data.items():
- if isinstance(val, (list, tuple)):
- generator = (self.__parse_value(item) for item in val)
- setattr(self, key, tuple(generator))
- else:
- setattr(self, key, self.__parse_value(val))
-
- delattr(self, '_base_path')
- delattr(self, '_write_mode')
-
- def __parse_value(self, value: Any):
- if isinstance(value, dict):
- return self.__class__(value, self._base_path)
-
- if isinstance(value, str):
- if value.startswith('path:'):
- value = value[len('path:'):]
- value = str((Path(self._base_path) / value).absolute())
-
- return value
-
- def __setattr__(self, key, value):
- if key == '_write_mode' or hasattr(self, '_write_mode'):
- super().__setattr__(key, value)
- else:
- raise Exception('Set config')
-
- def __delattr__(self, item):
- if item == '_write_mode' or hasattr(self, '_write_mode'):
- super().__delattr__(item)
- else:
- raise Exception('Del config')
-
- def __contains__(self, name):
- return name in self.__dict__
-
- def __getitem__(self, name):
- return self.__dict__[name]
-
- def __repr__(self):
- return repr(self.to_dict())
-
- @staticmethod
- def __item_to_dict(val):
- if isinstance(val, Config):
- return val.to_dict()
- if isinstance(val, (list, tuple)):
- generator = (Config.__item_to_dict(item) for item in val)
- return list(generator)
- return val
-
- def merge(self, other_conf):
- return Config(
- data={**self.to_dict(), **other_conf.to_dict()},
- base_path=''
- )
-
- def get(self, key, default=None):
- return self.__dict__.get(key, default)
-
- def to_dict(self) -> dict:
- """
- Convert object to dict recursively!
- :return: Dictionary output
- """
- return {
- key: Config.__item_to_dict(val) for key, val in self.__dict__.items()
- }
-
-
- def load_config(config_file_path: Union[str, PathLike], base_path: Optional[Union[str, PathLike]] = None,
- file_type: Literal['json', 'JSON', 'yml', 'YML', 'yaml', 'YAML', None] = None) -> Config:
- """
- Load configs from a YAML or JSON file.
- :param config_file_path: File path as a string or pathlike object
- :param base_path: Base path for `path:` strings, default value is parent of `config_file_path`
- :param file_type: What is the format of the file. If none it will look at the file extension
- :return: A config object
- """
- if base_path is None:
- base_path = str(Path(config_file_path).resolve().parent)
- if file_type is None:
- file_type = Path(config_file_path).suffix
- file_type = file_type[1:] # remove extra first dot!
-
- content = Path(config_file_path).read_text(encoding='utf-8')
- load_content = {
- 'json': json.loads,
- 'yaml': yaml.safe_load,
- 'yml': yaml.safe_load
- }[file_type.lower()]
- return Config(load_content(content), base_path)
|