py.lib.aw_config

Yohn Y. 2024-05-05 Parent:b76a704f31b1 Child:26be458b26bb

3:bac1fe0751a9 Go to Latest

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

. Декорируем возможные ошибки в чтении файла конфигурации.

History
awgur@0 1 # coding: utf-8
awgur@0 2 """\
awgur@0 3 Функции, помогающие в разборе конфигурационного файла
awgur@0 4 """
awgur@0 5 import toml
awgur@0 6 from os.path import exists
awgur@0 7 from typing import Union, Any
awgur@0 8
awgur@0 9 from .error import Error
awgur@0 10 from .type_helpers import BaseTypeHelper
awgur@0 11
awgur@0 12
awgur@0 13 class ConfigFileError(Error):
awgur@0 14 """\
awgur@0 15 Базовое исключение при обработке конфигурационного файла
awgur@0 16 """
awgur@0 17
awgur@0 18
awgur@0 19 class ConfigSectionNotFound(ConfigFileError):
awgur@0 20 """\
awgur@0 21 Отсутствие нужного раздела в конфигурации
awgur@0 22 """
awgur@0 23
awgur@0 24
awgur@0 25 class ConfigFileContent(object):
awgur@0 26 """\
awgur@0 27 Работа с содержимым файла конфигурации
awgur@0 28 """
awgur@0 29 def __init__(self, file_data, section_name=None):
awgur@0 30 """\
awgur@0 31 :param file_data: Разобранное в словарь содержимое файла конфигурации
awgur@0 32 :param section_name: Имя секции в конфигурации
awgur@0 33 """
awgur@0 34 self.d = file_data
awgur@0 35 self.section = section_name
awgur@0 36
awgur@0 37 def _format_err_msg(self, msg: str) -> str:
awgur@0 38 if self.section is not None:
awgur@0 39 return f'Раздел конфигурации "{self.section}": {msg}'
awgur@0 40
awgur@0 41 else:
awgur@0 42 return msg
awgur@0 43
awgur@0 44 def __enter__(self):
awgur@0 45 return self
awgur@0 46
awgur@0 47 def __exit__(self, exc_type, exc_val, exc_tb):
awgur@0 48 pass
awgur@0 49
awgur@0 50 def get_value(self, name: str,
awgur@2 51 val_type: Union[type, BaseTypeHelper] = str,
awgur@0 52 default: Any = None,
awgur@0 53 mandatory: bool = True):
awgur@0 54 """\
awgur@0 55 Получить из текущего раздела конфигурации нужный элемент
awgur@0 56 :param name: имя параметра
awgur@0 57 :param val_type: тип параметра
awgur@0 58 :param default: значение параметра по умолчанию
awgur@0 59 :param mandatory: является ли параметр обязательным. Если ``False``, то в случае отсутствия
awgur@0 60 параметра будет использовано значение None
awgur@0 61 """
awgur@0 62 if name not in self.d:
awgur@0 63 if mandatory and default is None:
awgur@0 64 raise ConfigFileError(self._format_err_msg(f'Параметр "{name}" в файле отсутствует'))
awgur@0 65
awgur@0 66 else:
awgur@0 67 return default
awgur@0 68
awgur@0 69 else:
awgur@0 70 _buf = self.d[name]
awgur@0 71 try:
awgur@0 72 return val_type(_buf)
awgur@0 73
awgur@0 74 except (ValueError, TypeError) as e:
awgur@0 75 raise ConfigFileError(self._format_err_msg(f'Не удалось привести значение "{_buf}" к типу '
awgur@0 76 f'{repr(val_type)}: {e}'))
awgur@0 77
awgur@0 78 def get_section(self, name: str, mandatory: bool = True):
awgur@0 79 """\
awgur@0 80 Получить раздел конфигурационного файла из текущего или корневого раздела
awgur@0 81 :param name: имя раздела
awgur@0 82 :param mandatory: является ли раздел обязательным или он может отсутствовать в конфигурации. Во втором
awgur@0 83 случае, будет создан пустой словарь и использован в качестве данных раздела.
awgur@0 84 :returns Экземпляр того-же класса, только для новой секции
awgur@0 85 """
awgur@0 86 if name not in self.d:
awgur@0 87 if mandatory:
awgur@0 88 raise ConfigSectionNotFound(self._format_err_msg(f'Раздел "{name}" не найден'))
awgur@0 89
awgur@0 90 else:
awgur@0 91 return ConfigFileContent(file_data={}, section_name=f'{self.section}.{name}')
awgur@0 92
awgur@0 93 else:
awgur@0 94 _val = self.d[name]
awgur@0 95
awgur@0 96 if not isinstance(_val, dict):
awgur@0 97 raise ConfigFileError(self._format_err_msg(f'Параметр конфигурации "{name}" не является разделом'))
awgur@0 98
awgur@0 99 else:
awgur@0 100 return ConfigFileContent(file_data=_val, section_name=f'{self.section}.{name}')
awgur@0 101
awgur@0 102
awgur@0 103 class ConfigFile(object):
awgur@0 104 """\
awgur@0 105 Работа с самим файлом конфигурации
awgur@0 106 """
awgur@0 107 def __init__(self, file_name: str):
awgur@0 108 if not exists(file_name):
awgur@0 109 raise ConfigFileError(f'Файл конфигурации "{file_name}" не существует')
awgur@0 110
awgur@0 111 self.file_name = file_name
awgur@0 112
awgur@0 113 def get(self):
awgur@0 114 """\
awgur@0 115 Производим разбор файла конфигурации и создаём корневой раздел конфигурации
awgur@0 116 """
awgur@3 117 try:
awgur@3 118 return ConfigFileContent(toml.load(self.file_name))
awgur@3 119
awgur@3 120 except OSError as e:
awgur@3 121 raise ConfigFileError(f'Ошибка чтения файла "{self.file_name}": {e}')
awgur@3 122
awgur@3 123 except toml.TomlDecodeError as e:
awgur@3 124 raise ConfigFileError(f'Ошибка в разборе файла конфигурации: {e}')
awgur@3 125
awgur@3 126 except ValueError as e:
awgur@3 127 raise ConfigFileError(f'Ошибка разбора файла конфигурации: {ConfigFileError.fmt_error(e)}')
awgur@0 128
awgur@0 129 def __exit__(self, exc_type, exc_val, exc_tb):
awgur@0 130 pass
awgur@0 131
awgur@0 132 def __enter__(self):
awgur@0 133 return self.get()