py.lib.aw_config
py.lib.aw_config/src/aw_config/file.py
.. 0.202606.1 . Рефакторинг документирования функций в file.py . Создание служебных методов для упрощения последующего развития модуля + Возможность получить раздел по заданному пути в иерархии toml-файла + Работа с списками хэш-таблиц toml (возможность в конфигурации использовать массивы однотипных секций.
1.1 --- a/src/aw_config/file.py Wed Oct 30 15:07:14 2024 +0300 1.2 +++ b/src/aw_config/file.py Tue Jun 23 11:09:15 2026 +0300 1.3 @@ -54,6 +54,7 @@ 1.4 mandatory: bool = True): 1.5 """\ 1.6 Получить из текущего раздела конфигурации нужный элемент 1.7 + 1.8 :param name: имя параметра 1.9 :param val_type: тип параметра 1.10 :param default: значение параметра по умолчанию 1.11 @@ -79,29 +80,153 @@ 1.12 raise ConfigFileError(self._format_err_msg(f'Не удалось привести значение "{_buf}" к типу ' 1.13 f'{repr(val_type)}: {e}')) 1.14 1.15 + def _get_section_name(self, *names: str) -> str: 1.16 + """\ 1.17 + Поучить имя текущего раздели или подраздела по заданному имени 1.18 + 1.19 + :type names: str 1.20 + :param names: Последовательные имена подразделов для получения доступа к глубокой иерархии 1.21 + :returns: Имя подраздела 1.22 + """ 1.23 + res = self.section if self.section is not None else '' 1.24 + if not names: 1.25 + return res 1.26 + 1.27 + else: 1.28 + if res: 1.29 + res += '.' 1.30 + 1.31 + return res + '.'.join(names) 1.32 + 1.33 + def _get_part_by_path(self, *names: str): 1.34 + """\ 1.35 + Получить конфигурационный элемент по заданному пути 1.36 + """ 1.37 + if not names: 1.38 + raise ValueError(f'Не заданно имя запрашиваемого раздела') 1.39 + 1.40 + upper_path = names[:-1] 1.41 + last_path_el = names[-1] 1.42 + cur_level = self.d 1.43 + cur_section = self._get_section_name() 1.44 + 1.45 + for el in upper_path: 1.46 + cur_section = el if not cur_section else f'{cur_section}.{el}' 1.47 + 1.48 + if el in cur_level: 1.49 + cur_level = cur_level[el] 1.50 + 1.51 + else: 1.52 + raise ConfigSectionNotFound(f'Раздел "{cur_section}" не найден') 1.53 + 1.54 + cur_section = last_path_el if not cur_section else f'{cur_section}.{last_path_el}' 1.55 + if last_path_el in cur_level: 1.56 + return cur_level[last_path_el] 1.57 + 1.58 + else: 1.59 + raise ConfigSectionNotFound(f'Раздел "{cur_section}" не найден') 1.60 + 1.61 + 1.62 def get_section(self, name: str, mandatory: bool = True): 1.63 """\ 1.64 Получить раздел конфигурационного файла из текущего или корневого раздела 1.65 + 1.66 :param name: имя раздела 1.67 :param mandatory: является ли раздел обязательным или он может отсутствовать в конфигурации. Во втором 1.68 случае, будет создан пустой словарь и использован в качестве данных раздела. 1.69 - :returns Экземпляр того-же класса, только для новой секции 1.70 + :returns: Экземпляр того-же класса, только для новой секции 1.71 """ 1.72 - if name not in self.d: 1.73 + section_name = self._get_section_name(name) 1.74 + try: 1.75 + section_val = self._get_part_by_path(name) 1.76 + 1.77 + if not isinstance(section_val, dict): 1.78 + raise ConfigFileError(f'Параметр конфигурации "{section_name}" не является разделом') 1.79 + 1.80 + else: 1.81 + return ConfigFileContent(file_data=section_val, section_name=f'{section_name}') 1.82 + 1.83 + except ConfigSectionNotFound as e: 1.84 if mandatory: 1.85 - raise ConfigSectionNotFound(self._format_err_msg(f'Раздел "{name}" не найден')) 1.86 + raise e 1.87 + 1.88 + else: 1.89 + return ConfigFileContent(file_data={}, section_name=f'{section_name}') 1.90 + 1.91 + def get_section_by_path(self, *names: str, mandatory: bool = True): 1.92 + """\ 1.93 + Поучить секцию конфигурации по явно заданному пути 1.94 + 1.95 + :type names: str 1.96 + :param names: Имя секции на текущем уровне. Можно задавать несколько 1.97 + для прохода по всей иерархии необходимой иерархии одним вызовом. 1.98 + 1.99 + :type mandatory: bool 1.100 + :param mandatory: Является ли искомая секция обязательной? 1.101 + 1.102 + :returns: Экземпляр того-же класса, только для новой секции 1.103 + """ 1.104 + section_name = self._get_section_name(*names) 1.105 + try: 1.106 + section_val = self._get_part_by_path(*names) 1.107 + 1.108 + if not isinstance(section_val, dict): 1.109 + raise ConfigFileError(f'Параметр конфигурации "{section_name}" не является разделом') 1.110 + 1.111 + else: 1.112 + return ConfigFileContent(file_data=section_val, section_name=f'{section_name}') 1.113 + 1.114 + except ConfigSectionNotFound as e: 1.115 + if mandatory: 1.116 + raise e 1.117 1.118 else: 1.119 - return ConfigFileContent(file_data={}, section_name=f'{self.section}.{name}') 1.120 + return ConfigFileContent(file_data={}, section_name=f'{section_name}') 1.121 + 1.122 + def get_list_section(self, name: str, mandatory: bool = True): 1.123 + """\ 1.124 + Для случая, когда раздел конфигурации задан массивом toml ([[${! SOME_SECTION !}]]) 1.125 + 1.126 + :type name: str 1.127 + :param name: Имя раздела 1.128 + 1.129 + :type mandatory: bool 1.130 + :param mandatory: Данный раздел обязателен и не может отсутствовать 1.131 1.132 - else: 1.133 - _val = self.d[name] 1.134 + :returns: Спискол из экземпляров данного класса, нацеленные на элементы массива хэш-таблиц файла toml 1.135 + """ 1.136 + section_name = self._get_section_name(name) 1.137 + try: 1.138 + section_val = self._get_part_by_path(name) 1.139 1.140 - if not isinstance(_val, dict): 1.141 - raise ConfigFileError(self._format_err_msg(f'Параметр конфигурации "{name}" не является разделом')) 1.142 + if isinstance(section_val, (list, tuple)): 1.143 + res = [] 1.144 + for el in section_val: 1.145 + if isinstance(el, dict): 1.146 + res.append(ConfigFileContent(file_data=el, section_name=f'{section_name}')) 1.147 + 1.148 + else: 1.149 + raise ConfigFileError(f'Параметр конфигурации "{section_name}"' 1.150 + f' со значением "{el}"' 1.151 + f' не является разделом') 1.152 + return res 1.153 + 1.154 + elif isinstance(section_val, dict): 1.155 + return [ 1.156 + ConfigFileContent(file_data=section_val, section_name=f'{section_name}') 1.157 + ] 1.158 1.159 else: 1.160 - return ConfigFileContent(file_data=_val, section_name=f'{self.section}.{name}') 1.161 + raise ConfigFileError(f'Параметр конфигурации "{section_name}" не является разделом') 1.162 + 1.163 + except ConfigSectionNotFound as e: 1.164 + if mandatory: 1.165 + raise e 1.166 + 1.167 + else: 1.168 + return [ 1.169 + ConfigFileContent(file_data={}, section_name=f'{section_name}') 1.170 + ] 1.171 1.172 1.173 class ConfigFile(object):