py.lib.aw_config

Yohn Y. 2024-10-30 Parent:26be458b26bb

12:75523df5b946 Go to Latest

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

. Оптимизация изменения 493b42d1 и распространение его ещё и на разбор файлов конфигураций.

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