py.lib.aw_config
py.lib.aw_config/src/aw_config/file.py
. Разбираемся с зависимостями
| 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() |