py.lib.aw_config
2024-05-04
Child:81fc92335324
0:bece3a8a67a5 Browse Files
.. init
.hgignore pyproject.toml requirements.txt setup.py src/aw_config/__init__.py src/aw_config/env.py src/aw_config/error.py src/aw_config/file.py src/aw_config/type_helpers.py tools/make_pkg.sh
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/.hgignore Sat May 04 18:23:23 2024 +0300 1.3 @@ -0,0 +1,7 @@ 1.4 +syntax: glob 1.5 +.idea/* 1.6 +.e/* 1.7 +build/* 1.8 +dist/* 1.9 +*.egg-info/* 1.10 +
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/pyproject.toml Sat May 04 18:23:23 2024 +0300 2.3 @@ -0,0 +1,16 @@ 2.4 +[build-system] 2.5 +# Minimum requirements for the build system to execute. 2.6 +requires = ["setuptools", "wheel"] # PEP 508 specifications. 2.7 + 2.8 +[project] 2.9 +name = "aw_config" 2.10 +version = "0.202405.1" 2.11 +description = "Модуль поддержки процесса конфигурирования приложения" 2.12 +#readme = "README.md" 2.13 +requires-python = ">=3.9" 2.14 +dependencies = [ 2.15 + "toml>=0.10.2" 2.16 +] 2.17 + 2.18 +[project.urls] 2.19 +src = "https://devel.a0fs.ru/py.lib.aw_config"
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/requirements.txt Sat May 04 18:23:23 2024 +0300 3.3 @@ -0,0 +1,2 @@ 3.4 +toml>=0.10.2 3.5 +setuptools>=59.6.0 3.6 \ No newline at end of file
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/setup.py Sat May 04 18:23:23 2024 +0300 4.3 @@ -0,0 +1,15 @@ 4.4 +# coding: utf-8 4.5 +from setuptools import setup 4.6 + 4.7 +setup( 4.8 + name='aw_config', 4.9 + version='0.202405.1', 4.10 + packages=['aw_config'], 4.11 + package_dir={'aw_config': 'src/aw_config'}, 4.12 + url='https://devel.a0fs.ru/py.lib.aw_config', 4.13 + license='BSD', 4.14 + author='awgur', 4.15 + author_email='devel@a0fs.ru', 4.16 + description='Модуль поддержки процесса конфигурирования приложения' 4.17 +) 4.18 +
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/aw_config/__init__.py Sat May 04 18:23:23 2024 +0300 5.3 @@ -0,0 +1,11 @@ 5.4 +# coding: utf-8 5.5 +from .env import get_env 5.6 +from .type_helpers import ANY, DICT, LIST 5.7 +from .file import ConfigFile, ConfigSectionNotFound, ConfigFileError 5.8 + 5.9 + 5.10 +__all__ = [ 5.11 + 'get_env', 5.12 + 'ANY', 'DICT', 'LIST', 5.13 + 'ConfigFile', 'ConfigSectionNotFound', 'ConfigFileError', 5.14 +]
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/aw_config/env.py Sat May 04 18:23:23 2024 +0300 6.3 @@ -0,0 +1,25 @@ 6.4 +# coding: utf-8 6.5 + 6.6 +from os import environ as sysenv 6.7 +from typing import Optional, Any 6.8 +from .error import Error 6.9 + 6.10 + 6.11 +class EnvConfigError(Error): 6.12 + def __init__(self, param_name: str, message: Any): 6.13 + super().__init__(f'Ошибка в параметре: {param_name} - {message}') 6.14 + 6.15 + 6.16 +def get_env(name: str, type_name: type, default: Optional[Any] = None, mandatory: bool = False): 6.17 + try: 6.18 + return type_name(sysenv[name]) 6.19 + 6.20 + except (ValueError, TypeError) as e: 6.21 + raise EnvConfigError(name, f'Ошибка преобразования типа - {e}') 6.22 + 6.23 + except KeyError: 6.24 + if default is None and not mandatory: 6.25 + raise EnvConfigError(name, 'Не установлен параметр') 6.26 + 6.27 + else: 6.28 + return default
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/src/aw_config/error.py Sat May 04 18:23:23 2024 +0300 7.3 @@ -0,0 +1,6 @@ 7.4 +# coding: utf-8 7.5 + 7.6 +class Error(Exception): 7.7 + @staticmethod 7.8 + def fmt_error(err: Exception): 7.9 + return '%s(%s)' % (type(err).__name__, err)
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/src/aw_config/file.py Sat May 04 18:23:23 2024 +0300 8.3 @@ -0,0 +1,123 @@ 8.4 +# coding: utf-8 8.5 +"""\ 8.6 +Функции, помогающие в разборе конфигурационного файла 8.7 +""" 8.8 +import toml 8.9 +from os.path import exists 8.10 +from typing import Union, Any 8.11 + 8.12 +from .error import Error 8.13 +from .type_helpers import BaseTypeHelper 8.14 + 8.15 + 8.16 +class ConfigFileError(Error): 8.17 + """\ 8.18 + Базовое исключение при обработке конфигурационного файла 8.19 + """ 8.20 + 8.21 + 8.22 +class ConfigSectionNotFound(ConfigFileError): 8.23 + """\ 8.24 + Отсутствие нужного раздела в конфигурации 8.25 + """ 8.26 + 8.27 + 8.28 +class ConfigFileContent(object): 8.29 + """\ 8.30 + Работа с содержимым файла конфигурации 8.31 + """ 8.32 + def __init__(self, file_data, section_name=None): 8.33 + """\ 8.34 + :param file_data: Разобранное в словарь содержимое файла конфигурации 8.35 + :param section_name: Имя секции в конфигурации 8.36 + """ 8.37 + self.d = file_data 8.38 + self.section = section_name 8.39 + 8.40 + def _format_err_msg(self, msg: str) -> str: 8.41 + if self.section is not None: 8.42 + return f'Раздел конфигурации "{self.section}": {msg}' 8.43 + 8.44 + else: 8.45 + return msg 8.46 + 8.47 + def __enter__(self): 8.48 + return self 8.49 + 8.50 + def __exit__(self, exc_type, exc_val, exc_tb): 8.51 + pass 8.52 + 8.53 + def get_value(self, name: str, 8.54 + val_type: Union[type, BaseTypeHelper], 8.55 + default: Any = None, 8.56 + mandatory: bool = True): 8.57 + """\ 8.58 + Получить из текущего раздела конфигурации нужный элемент 8.59 + :param name: имя параметра 8.60 + :param val_type: тип параметра 8.61 + :param default: значение параметра по умолчанию 8.62 + :param mandatory: является ли параметр обязательным. Если ``False``, то в случае отсутствия 8.63 + параметра будет использовано значение None 8.64 + """ 8.65 + if name not in self.d: 8.66 + if mandatory and default is None: 8.67 + raise ConfigFileError(self._format_err_msg(f'Параметр "{name}" в файле отсутствует')) 8.68 + 8.69 + else: 8.70 + return default 8.71 + 8.72 + else: 8.73 + _buf = self.d[name] 8.74 + try: 8.75 + return val_type(_buf) 8.76 + 8.77 + except (ValueError, TypeError) as e: 8.78 + raise ConfigFileError(self._format_err_msg(f'Не удалось привести значение "{_buf}" к типу ' 8.79 + f'{repr(val_type)}: {e}')) 8.80 + 8.81 + def get_section(self, name: str, mandatory: bool = True): 8.82 + """\ 8.83 + Получить раздел конфигурационного файла из текущего или корневого раздела 8.84 + :param name: имя раздела 8.85 + :param mandatory: является ли раздел обязательным или он может отсутствовать в конфигурации. Во втором 8.86 + случае, будет создан пустой словарь и использован в качестве данных раздела. 8.87 + :returns Экземпляр того-же класса, только для новой секции 8.88 + """ 8.89 + if name not in self.d: 8.90 + if mandatory: 8.91 + raise ConfigSectionNotFound(self._format_err_msg(f'Раздел "{name}" не найден')) 8.92 + 8.93 + else: 8.94 + return ConfigFileContent(file_data={}, section_name=f'{self.section}.{name}') 8.95 + 8.96 + else: 8.97 + _val = self.d[name] 8.98 + 8.99 + if not isinstance(_val, dict): 8.100 + raise ConfigFileError(self._format_err_msg(f'Параметр конфигурации "{name}" не является разделом')) 8.101 + 8.102 + else: 8.103 + return ConfigFileContent(file_data=_val, section_name=f'{self.section}.{name}') 8.104 + 8.105 + 8.106 +class ConfigFile(object): 8.107 + """\ 8.108 + Работа с самим файлом конфигурации 8.109 + """ 8.110 + def __init__(self, file_name: str): 8.111 + if not exists(file_name): 8.112 + raise ConfigFileError(f'Файл конфигурации "{file_name}" не существует') 8.113 + 8.114 + self.file_name = file_name 8.115 + 8.116 + def get(self): 8.117 + """\ 8.118 + Производим разбор файла конфигурации и создаём корневой раздел конфигурации 8.119 + """ 8.120 + return ConfigFileContent(toml.load(self.file_name)) 8.121 + 8.122 + def __exit__(self, exc_type, exc_val, exc_tb): 8.123 + pass 8.124 + 8.125 + def __enter__(self): 8.126 + return self.get()
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/aw_config/type_helpers.py Sat May 04 18:23:23 2024 +0300 9.3 @@ -0,0 +1,88 @@ 9.4 +# coding: utf-8 9.5 +"""\ 9.6 +Описание различных обёрток над типами, для выделения из конфигураций параметров со сложными типами 9.7 +""" 9.8 + 9.9 +from typing import TypeVar, Type, Any, List, Iterable, Dict, Union, Callable 9.10 + 9.11 + 9.12 +T = TypeVar('T') 9.13 +K = TypeVar('K') 9.14 + 9.15 + 9.16 +class BaseTypeHelper(object): 9.17 + """\ 9.18 + Базовый класс для типов-обёрток 9.19 + """ 9.20 + __name__ = 'aw_config.type_helpers.BaseTypeHelper' 9.21 + 9.22 + def __call__(self, val: Any): 9.23 + raise NotImplemented() 9.24 + 9.25 + 9.26 +class ANY(BaseTypeHelper): 9.27 + """\ 9.28 + Класс не проводящий никакой обработки содержимого 9.29 + """ 9.30 + def __call__(self, val: Any) -> Any: 9.31 + return val 9.32 + 9.33 + 9.34 +class LIST(BaseTypeHelper): 9.35 + """\ 9.36 + Преобразуем параметр в список нужного типа 9.37 + """ 9.38 + def __init__(self, el_type: Union[Type[T], Callable[[Any], T]]): 9.39 + self.el_type = el_type 9.40 + 9.41 + def __repr__(self) -> str: 9.42 + return f"<class '{type(self).__name__}[{repr(self.el_type)}]'>" 9.43 + 9.44 + def __call__(self, val: Iterable) -> List[T]: 9.45 + res = [] 9.46 + for i in val: 9.47 + try: 9.48 + res.append(self.el_type(i)) 9.49 + 9.50 + except (ValueError, TypeError) as e: 9.51 + raise ValueError(f'Не удалось привести значение "{i}" к типу "{repr(self.el_type)}" ' 9.52 + f'в составе списка. Ошибка: {e}') 9.53 + 9.54 + return res 9.55 + 9.56 + 9.57 +class DICT(BaseTypeHelper): 9.58 + """\ 9.59 + Преобразуем параметр в словарь, с нужными типами ключа и значения 9.60 + """ 9.61 + __name__ = 'aw_config.type_helpers.DICT' 9.62 + 9.63 + def __init__(self, key_type: Union[Type[K], Callable[[Any], K]], el_type: Union[Type[T], Callable[[Any], T]]): 9.64 + self.el_type = el_type 9.65 + self.k_type = key_type 9.66 + 9.67 + def __repr__(self) -> str: 9.68 + return f"<class '{type(self).__name__}[{repr(self.k_type)}, {repr(self.el_type)}]'>" 9.69 + 9.70 + def __call__(self, val: Dict) -> Dict[K, T]: 9.71 + res = dict() 9.72 + for k, v in val.items(): 9.73 + try: 9.74 + _k = self.k_type(k) 9.75 + 9.76 + except (ValueError, TypeError) as e: 9.77 + raise ValueError(f'Ключ "{k}" не может быть преобразован в тип {repr(self.k_type)} ' 9.78 + f'для комбинации ключ-значение: "{k}", "{v}" ' 9.79 + f'Ошибка: "{e}"') 9.80 + 9.81 + try: 9.82 + _v = self.el_type(v) 9.83 + 9.84 + except (ValueError, TypeError) as e: 9.85 + raise ValueError(f'Значение "{v}" не может быть преобразован в тип {repr(self.el_type)} ' 9.86 + f'для комбинации ключ-значение: "{k}", "{v}" ' 9.87 + f'Ошибка: "{e}"') 9.88 + 9.89 + res[_k] = _v 9.90 + 9.91 + return res
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/tools/make_pkg.sh Sat May 04 18:23:23 2024 +0300 10.3 @@ -0,0 +1,11 @@ 10.4 +#!/bin/sh 10.5 +# devel.a0fs.ru -- devel.python.tools:make_pkg.sh -- v0.r202405.1 10.6 +this_dir="$(dirname "$(readlink -f "$0")")" 10.7 +this_pkg="$(dirname "$this_dir")" 10.8 + 10.9 +cd "${this_pkg}" || exit 10.10 +if [ -d "${this_pkg}/.e" ] ; then 10.11 + . "${this_pkg}/.e/bin/activate" 10.12 +fi 10.13 + 10.14 +python3 setup.py bdist_wheel