py.lib.aw_config

Yohn Y. 2026-06-23 Parent:23acfa856975 Child:34b98b38a9f0

15:ed99d4db56a7 Browse Files

.. 0.202606.1 . Рефакторинг документирования функций в file.py . Создание служебных методов для упрощения последующего развития модуля + Возможность получить раздел по заданному пути в иерархии toml-файла + Работа с списками хэш-таблиц toml (возможность в конфигурации использовать массивы однотипных секций.

setup.py src/aw_config/file.py tools/make_pkg.sh

     1.1 --- a/setup.py	Wed Oct 30 15:07:14 2024 +0300
     1.2 +++ b/setup.py	Tue Jun 23 11:09:15 2026 +0300
     1.3 @@ -3,7 +3,7 @@
     1.4  
     1.5  setup(
     1.6      name='aw_config',
     1.7 -    version='0.202410.2',
     1.8 +    version='0.202606.1',
     1.9      packages=['aw_config'],
    1.10      package_dir={'aw_config': 'src/aw_config'},
    1.11      description='Модуль поддержки процесса конфигурирования приложения'
     2.1 --- a/src/aw_config/file.py	Wed Oct 30 15:07:14 2024 +0300
     2.2 +++ b/src/aw_config/file.py	Tue Jun 23 11:09:15 2026 +0300
     2.3 @@ -54,6 +54,7 @@
     2.4                    mandatory: bool = True):
     2.5          """\
     2.6          Получить из текущего раздела конфигурации нужный элемент
     2.7 +
     2.8          :param name: имя параметра
     2.9          :param val_type: тип параметра
    2.10          :param default: значение параметра по умолчанию
    2.11 @@ -79,29 +80,153 @@
    2.12                  raise ConfigFileError(self._format_err_msg(f'Не удалось привести значение "{_buf}" к типу '
    2.13                                                             f'{repr(val_type)}: {e}'))
    2.14  
    2.15 +    def _get_section_name(self, *names: str) -> str:
    2.16 +        """\
    2.17 +        Поучить имя текущего раздели или подраздела по заданному имени
    2.18 +
    2.19 +        :type names: str
    2.20 +        :param names: Последовательные имена подразделов для получения доступа к глубокой иерархии
    2.21 +        :returns: Имя подраздела
    2.22 +        """
    2.23 +        res = self.section if self.section is not None else ''
    2.24 +        if not names:
    2.25 +            return res
    2.26 +
    2.27 +        else:
    2.28 +            if res:
    2.29 +                res += '.'
    2.30 +
    2.31 +            return res + '.'.join(names)
    2.32 +
    2.33 +    def _get_part_by_path(self, *names: str):
    2.34 +        """\
    2.35 +        Получить конфигурационный элемент по заданному пути
    2.36 +        """
    2.37 +        if not names:
    2.38 +            raise ValueError(f'Не заданно имя запрашиваемого раздела')
    2.39 +
    2.40 +        upper_path = names[:-1]
    2.41 +        last_path_el = names[-1]
    2.42 +        cur_level = self.d
    2.43 +        cur_section = self._get_section_name()
    2.44 +
    2.45 +        for el in upper_path:
    2.46 +            cur_section = el if not cur_section else f'{cur_section}.{el}'
    2.47 +
    2.48 +            if el in cur_level:
    2.49 +                cur_level = cur_level[el]
    2.50 +
    2.51 +            else:
    2.52 +                raise ConfigSectionNotFound(f'Раздел "{cur_section}" не найден')
    2.53 +
    2.54 +        cur_section = last_path_el if not cur_section else f'{cur_section}.{last_path_el}'
    2.55 +        if last_path_el in cur_level:
    2.56 +            return cur_level[last_path_el]
    2.57 +
    2.58 +        else:
    2.59 +            raise ConfigSectionNotFound(f'Раздел "{cur_section}" не найден')
    2.60 +
    2.61 +
    2.62      def get_section(self, name: str, mandatory: bool = True):
    2.63          """\
    2.64          Получить раздел конфигурационного файла из текущего или корневого раздела
    2.65 +
    2.66          :param name: имя раздела
    2.67          :param mandatory: является ли раздел обязательным или он может отсутствовать в конфигурации. Во втором
    2.68                            случае, будет создан пустой словарь и использован в качестве данных раздела.
    2.69 -        :returns Экземпляр того-же класса, только для новой секции
    2.70 +        :returns: Экземпляр того-же класса, только для новой секции
    2.71          """
    2.72 -        if name not in self.d:
    2.73 +        section_name = self._get_section_name(name)
    2.74 +        try:
    2.75 +            section_val = self._get_part_by_path(name)
    2.76 +
    2.77 +            if not isinstance(section_val, dict):
    2.78 +                raise ConfigFileError(f'Параметр конфигурации "{section_name}" не является разделом')
    2.79 +
    2.80 +            else:
    2.81 +                return ConfigFileContent(file_data=section_val, section_name=f'{section_name}')
    2.82 +
    2.83 +        except ConfigSectionNotFound as e:
    2.84              if mandatory:
    2.85 -                raise ConfigSectionNotFound(self._format_err_msg(f'Раздел "{name}" не найден'))
    2.86 +                raise e
    2.87 +
    2.88 +            else:
    2.89 +                return ConfigFileContent(file_data={}, section_name=f'{section_name}')
    2.90 +
    2.91 +    def get_section_by_path(self, *names: str, mandatory: bool = True):
    2.92 +        """\
    2.93 +        Поучить секцию конфигурации по явно заданному пути
    2.94 +
    2.95 +        :type names: str
    2.96 +        :param names: Имя секции на текущем уровне. Можно задавать несколько
    2.97 +             для прохода по всей иерархии необходимой иерархии одним вызовом.
    2.98 +
    2.99 +        :type mandatory: bool
   2.100 +        :param mandatory: Является ли искомая секция обязательной?
   2.101 +
   2.102 +        :returns: Экземпляр того-же класса, только для новой секции
   2.103 +        """
   2.104 +        section_name = self._get_section_name(*names)
   2.105 +        try:
   2.106 +            section_val = self._get_part_by_path(*names)
   2.107 +
   2.108 +            if not isinstance(section_val, dict):
   2.109 +                raise ConfigFileError(f'Параметр конфигурации "{section_name}" не является разделом')
   2.110 +
   2.111 +            else:
   2.112 +                return ConfigFileContent(file_data=section_val, section_name=f'{section_name}')
   2.113 +
   2.114 +        except ConfigSectionNotFound as e:
   2.115 +            if mandatory:
   2.116 +                raise e
   2.117  
   2.118              else:
   2.119 -                return ConfigFileContent(file_data={}, section_name=f'{self.section}.{name}')
   2.120 +                return ConfigFileContent(file_data={}, section_name=f'{section_name}')
   2.121 +
   2.122 +    def get_list_section(self, name: str, mandatory: bool = True):
   2.123 +        """\
   2.124 +        Для случая, когда раздел конфигурации задан массивом toml ([[${! SOME_SECTION !}]])
   2.125 +
   2.126 +        :type name: str
   2.127 +        :param name: Имя раздела
   2.128 +
   2.129 +        :type mandatory: bool
   2.130 +        :param mandatory: Данный раздел обязателен и не может отсутствовать
   2.131  
   2.132 -        else:
   2.133 -            _val = self.d[name]
   2.134 +        :returns: Спискол из экземпляров данного класса, нацеленные на элементы массива хэш-таблиц файла toml
   2.135 +        """
   2.136 +        section_name = self._get_section_name(name)
   2.137 +        try:
   2.138 +            section_val = self._get_part_by_path(name)
   2.139  
   2.140 -            if not isinstance(_val, dict):
   2.141 -                raise ConfigFileError(self._format_err_msg(f'Параметр конфигурации "{name}" не является разделом'))
   2.142 +            if isinstance(section_val, (list, tuple)):
   2.143 +                res = []
   2.144 +                for el in section_val:
   2.145 +                    if isinstance(el, dict):
   2.146 +                        res.append(ConfigFileContent(file_data=el, section_name=f'{section_name}'))
   2.147 +
   2.148 +                    else:
   2.149 +                        raise ConfigFileError(f'Параметр конфигурации "{section_name}"'
   2.150 +                                              f' со значением "{el}"'
   2.151 +                                              f' не является разделом')
   2.152 +                return res
   2.153 +
   2.154 +            elif isinstance(section_val, dict):
   2.155 +                return [
   2.156 +                    ConfigFileContent(file_data=section_val, section_name=f'{section_name}')
   2.157 +                ]
   2.158  
   2.159              else:
   2.160 -                return ConfigFileContent(file_data=_val, section_name=f'{self.section}.{name}')
   2.161 +                raise ConfigFileError(f'Параметр конфигурации "{section_name}" не является разделом')
   2.162 +
   2.163 +        except ConfigSectionNotFound as e:
   2.164 +            if mandatory:
   2.165 +                raise e
   2.166 +
   2.167 +            else:
   2.168 +                return [
   2.169 +                    ConfigFileContent(file_data={}, section_name=f'{section_name}')
   2.170 +                ]
   2.171  
   2.172  
   2.173  class ConfigFile(object):
     3.1 --- a/tools/make_pkg.sh	Wed Oct 30 15:07:14 2024 +0300
     3.2 +++ b/tools/make_pkg.sh	Tue Jun 23 11:09:15 2026 +0300
     3.3 @@ -1,5 +1,5 @@
     3.4  #!/bin/sh
     3.5 -# devel.a0fs.ru -- devel.python.tools:make_pkg.sh -- v0.r202405.2
     3.6 +# devel.a0fs.ru -- devel.python.tools:make_pkg.sh -- v0.r202606.1
     3.7  this_dir="$(dirname "$(readlink -f "$0")")"
     3.8  this_pkg="$(dirname "$this_dir")"
     3.9  
    3.10 @@ -8,4 +8,4 @@
    3.11    . "${this_pkg}/.e/bin/activate"
    3.12  fi
    3.13  
    3.14 -python3 setup.py bdist_wheel --build-number "$(date '+%d%H%M%S')"
    3.15 +python3 setup.py bdist_wheel