py.lib.aw_config

Yohn Y. 2024-05-04 Child:b76a704f31b1

0:bece3a8a67a5 Go to Latest

py.lib.aw_config/src/aw_config/file.py

.. init

History
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/aw_config/file.py	Sat May 04 18:23:23 2024 +0300
     1.3 @@ -0,0 +1,123 @@
     1.4 +# coding: utf-8
     1.5 +"""\
     1.6 +Функции, помогающие в разборе конфигурационного файла
     1.7 +"""
     1.8 +import toml
     1.9 +from os.path import exists
    1.10 +from typing import Union, Any
    1.11 +
    1.12 +from .error import Error
    1.13 +from .type_helpers import BaseTypeHelper
    1.14 +
    1.15 +
    1.16 +class ConfigFileError(Error):
    1.17 +    """\
    1.18 +    Базовое исключение при обработке конфигурационного файла
    1.19 +    """
    1.20 +
    1.21 +
    1.22 +class ConfigSectionNotFound(ConfigFileError):
    1.23 +    """\
    1.24 +    Отсутствие нужного раздела в конфигурации
    1.25 +    """
    1.26 +
    1.27 +
    1.28 +class ConfigFileContent(object):
    1.29 +    """\
    1.30 +    Работа с содержимым файла конфигурации
    1.31 +    """
    1.32 +    def __init__(self, file_data, section_name=None):
    1.33 +        """\
    1.34 +        :param file_data: Разобранное в словарь содержимое файла конфигурации
    1.35 +        :param section_name: Имя секции в конфигурации
    1.36 +        """
    1.37 +        self.d = file_data
    1.38 +        self.section = section_name
    1.39 +
    1.40 +    def _format_err_msg(self, msg: str) -> str:
    1.41 +        if self.section is not None:
    1.42 +            return f'Раздел конфигурации "{self.section}": {msg}'
    1.43 +
    1.44 +        else:
    1.45 +            return msg
    1.46 +
    1.47 +    def __enter__(self):
    1.48 +        return self
    1.49 +
    1.50 +    def __exit__(self, exc_type, exc_val, exc_tb):
    1.51 +        pass
    1.52 +
    1.53 +    def get_value(self, name: str,
    1.54 +                  val_type: Union[type, BaseTypeHelper],
    1.55 +                  default: Any = None,
    1.56 +                  mandatory: bool = True):
    1.57 +        """\
    1.58 +        Получить из текущего раздела конфигурации нужный элемент
    1.59 +        :param name: имя параметра
    1.60 +        :param val_type: тип параметра
    1.61 +        :param default: значение параметра по умолчанию
    1.62 +        :param mandatory: является ли параметр обязательным. Если ``False``, то в случае отсутствия
    1.63 +                          параметра будет использовано значение None
    1.64 +        """
    1.65 +        if name not in self.d:
    1.66 +            if mandatory and default is None:
    1.67 +                raise ConfigFileError(self._format_err_msg(f'Параметр "{name}" в файле отсутствует'))
    1.68 +
    1.69 +            else:
    1.70 +                return default
    1.71 +
    1.72 +        else:
    1.73 +            _buf = self.d[name]
    1.74 +            try:
    1.75 +                return val_type(_buf)
    1.76 +
    1.77 +            except (ValueError, TypeError) as e:
    1.78 +                raise ConfigFileError(self._format_err_msg(f'Не удалось привести значение "{_buf}" к типу '
    1.79 +                                                           f'{repr(val_type)}: {e}'))
    1.80 +
    1.81 +    def get_section(self, name: str, mandatory: bool = True):
    1.82 +        """\
    1.83 +        Получить раздел конфигурационного файла из текущего или корневого раздела
    1.84 +        :param name: имя раздела
    1.85 +        :param mandatory: является ли раздел обязательным или он может отсутствовать в конфигурации. Во втором
    1.86 +                          случае, будет создан пустой словарь и использован в качестве данных раздела.
    1.87 +        :returns Экземпляр того-же класса, только для новой секции
    1.88 +        """
    1.89 +        if name not in self.d:
    1.90 +            if mandatory:
    1.91 +                raise ConfigSectionNotFound(self._format_err_msg(f'Раздел "{name}" не найден'))
    1.92 +
    1.93 +            else:
    1.94 +                return ConfigFileContent(file_data={}, section_name=f'{self.section}.{name}')
    1.95 +
    1.96 +        else:
    1.97 +            _val = self.d[name]
    1.98 +
    1.99 +            if not isinstance(_val, dict):
   1.100 +                raise ConfigFileError(self._format_err_msg(f'Параметр конфигурации "{name}" не является разделом'))
   1.101 +
   1.102 +            else:
   1.103 +                return ConfigFileContent(file_data=_val, section_name=f'{self.section}.{name}')
   1.104 +
   1.105 +
   1.106 +class ConfigFile(object):
   1.107 +    """\
   1.108 +    Работа с самим файлом конфигурации
   1.109 +    """
   1.110 +    def __init__(self, file_name: str):
   1.111 +        if not exists(file_name):
   1.112 +            raise ConfigFileError(f'Файл конфигурации "{file_name}" не существует')
   1.113 +
   1.114 +        self.file_name = file_name
   1.115 +
   1.116 +    def get(self):
   1.117 +        """\
   1.118 +        Производим разбор файла конфигурации и создаём корневой раздел конфигурации
   1.119 +        """
   1.120 +        return ConfigFileContent(toml.load(self.file_name))
   1.121 +
   1.122 +    def __exit__(self, exc_type, exc_val, exc_tb):
   1.123 +        pass
   1.124 +
   1.125 +    def __enter__(self):
   1.126 +        return self.get()