You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

_config.py 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import json
  2. from os import PathLike
  3. from pathlib import Path
  4. from typing import Any, Union, Optional, Literal
  5. import yaml
  6. class Config(object):
  7. def __init__(self, data: dict, base_path: str):
  8. self._write_mode = True
  9. self._base_path = base_path
  10. for key, val in data.items():
  11. if isinstance(val, (list, tuple)):
  12. generator = (self.__parse_value(item) for item in val)
  13. setattr(self, key, tuple(generator))
  14. else:
  15. setattr(self, key, self.__parse_value(val))
  16. delattr(self, '_base_path')
  17. delattr(self, '_write_mode')
  18. def __parse_value(self, value: Any):
  19. if isinstance(value, dict):
  20. return self.__class__(value, self._base_path)
  21. if isinstance(value, str):
  22. if value.startswith('path:'):
  23. value = value[len('path:'):]
  24. value = str((Path(self._base_path) / value).absolute())
  25. return value
  26. def __setattr__(self, key, value):
  27. if key == '_write_mode' or hasattr(self, '_write_mode'):
  28. super().__setattr__(key, value)
  29. else:
  30. raise Exception('Set config')
  31. def __delattr__(self, item):
  32. if item == '_write_mode' or hasattr(self, '_write_mode'):
  33. super().__delattr__(item)
  34. else:
  35. raise Exception('Del config')
  36. def __contains__(self, name):
  37. return name in self.__dict__
  38. def __getitem__(self, name):
  39. return self.__dict__[name]
  40. def __repr__(self):
  41. return repr(self.to_dict())
  42. @staticmethod
  43. def __item_to_dict(val):
  44. if isinstance(val, Config):
  45. return val.to_dict()
  46. if isinstance(val, (list, tuple)):
  47. generator = (Config.__item_to_dict(item) for item in val)
  48. return list(generator)
  49. return val
  50. def merge(self, other_conf):
  51. return Config(
  52. data={**self.to_dict(), **other_conf.to_dict()},
  53. base_path=''
  54. )
  55. def get(self, key, default=None):
  56. return self.__dict__.get(key, default)
  57. def to_dict(self) -> dict:
  58. """
  59. Convert object to dict recursively!
  60. :return: Dictionary output
  61. """
  62. return {
  63. key: Config.__item_to_dict(val) for key, val in self.__dict__.items()
  64. }
  65. def load_config(config_file_path: Union[str, PathLike], base_path: Optional[Union[str, PathLike]] = None,
  66. file_type: Literal['json', 'JSON', 'yml', 'YML', 'yaml', 'YAML', None] = None) -> Config:
  67. """
  68. Load configs from a YAML or JSON file.
  69. :param config_file_path: File path as a string or pathlike object
  70. :param base_path: Base path for `path:` strings, default value is parent of `config_file_path`
  71. :param file_type: What is the format of the file. If none it will look at the file extension
  72. :return: A config object
  73. """
  74. if base_path is None:
  75. base_path = str(Path(config_file_path).resolve().parent)
  76. if file_type is None:
  77. file_type = Path(config_file_path).suffix
  78. file_type = file_type[1:] # remove extra first dot!
  79. content = Path(config_file_path).read_text(encoding='utf-8')
  80. load_content = {
  81. 'json': json.loads,
  82. 'yaml': yaml.safe_load,
  83. 'yml': yaml.safe_load
  84. }[file_type.lower()]
  85. return Config(load_content(content), base_path)