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