Расскажу про свою минималистичную библиотеку, которая решает задачу удобства переопределения и переиспользования элементов конфигурации в микросервисной среде.
Пока подбирал библиотеку управления конфигурацией, просмотрел ��опулярные решения, которые по отдельности реализуют те или иные потребности, но идеальный вариант найти не удалось. Ключевые особенности библиотеки, которых (суммарно) мне не хватило в изученных решениях:
Можно указать множество источников конфигурации;
добавляемые в конце источники перезаписывают добавленные ранее;
можно сослаться на определенный ранее элемент конфигурации;
можно выстроить цепочку вычислений одних свойств на основе других;
можно переопределить один элемент в цепочке зависимых свойств не нарушая формулы расчета остальных элементов цепочки
можно использовать популярный и простой формат для конфигурации, YAML;
можно переопределять элементы через переменные окружени;
можно расширять набор источников данных своими реализациями.
Библиотека называется Toya и находится здесь: GitHub и PyPI.
Особенности реализации
Для воплощения в жизнь указанных возможностей используется:
pyyaml для работы с форматом YAML;
jinja для шаблонизации значений;
кастомный YAML-тег (по умолчанию
!t, но можно указать свой).
Пример использования
Есть базовый файл конфигурации со значениями по умолчанию, поставляемый в дистрибутиве сервиса, base.yml, содержащий такой фрагмент:
db:
user: ""
password: ""
host: ""
port: "5432"
dbname: ""
schema: "public"
options: !t "-c search_path={{ schema }}"
connection_string_base: !t "{{ user }}:{{ password }}@{{ host }}:{{ port }}/{{ dbname }}?options={{ options|urlencode }}"
sync_connecting_string: !t "postgresql+psycopg://{{ connection_string_base }}"
async_connecting_string: !t "{{ sync_connecting_string }}"
У нас может появиться потребность перезаписать db.host или db.connection_string_base. При этом мы, естественно, не хотели бы потом руками указывать десяток полей, которые зависят от перезаписываемого поля. А именно так будет при использовании большинства популярных библиотек управления конфигурацией.
Также у нас есть отдельные файлы для каждого из стендов, со специфичными настройками (IFT, STAGE, PROD). Например:
Настройки, общие, для всех стендов:
# all-stands.yml
db:
user: user
password: password
dbname: app_db
Настройки только для прода:
# prod.yml
db:
host: prod-db.host
тогда можно прочитать конфигурацияю так:
from pathlib import Path
from toya.config import load_and_eval_config
from toya.supplier.env_supplier import EnvSupplier
from toya.supplier.mapping_supplier import MappingSupplier
from toya.supplier.yaml_supplier import YamlSupplier
cfg = load_and_eval_config([
YamlSupplier(Path("base.yml")),
YamlSupplier(Path("all-stands.yml")),
YamlSupplier(Path("prod.yml"))
])
assert cfg["db"]["async_connecting_string"] == "postgresql+psycopg://user:password@prod-db.host:5432/app_db?options=-c%20search_path%3Dpublic"
Далее можно совместить сырую конфигурацию с Pydantic для работы с типизированными значениями:
from pydantic import BaseModel
class Config(BaseModel):
class DB(BaseModel):
sync_connecting_string: str
async_connecting_string: str
db: DB
cfg = load_and_eval_config([...])
typed_cfg = Config.model_validate(cfg)
Поддерживаемые источники данных
YamlSupplier - для чтения из YAML-файла
EnvSupplier - для чтения переменных окружения
MappingSupplier - для чтения конфигурации из словаря и всего совместимого с ним
Название | Описание | Поддерживает шаблонизацию? |
|---|---|---|
YamlSupplier | для чтения из YAML-файла | + |
EnvSupplier | для чтения переменных окружения | - |
MappingSupplier | для чтения конфигурации из словаря и всего совместимого с ним | + |