py.lib.aw_config
2024-05-04
Child:b76a704f31b1
py.lib.aw_config/src/aw_config/file.py
.. init
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()