Расскажу про свою минималистичную библиотеку, которая решает задачу удобства переопределения и переиспользования элементов конфигурации в микросервисной среде.
Пока подбирал библиотеку управления конфигурацией, просмотрел популярные решения, которые по отдельности реализуют те или иные потребности, но идеальный вариант найти не удалось. Ключевые особенности библиотеки, которых (суммарно) мне не хватило в изученных решениях:
Можно указать множество источников конфигурации;
добавляемые в конце источники перезаписывают добавленные ранее;
можно сослаться на определенный ранее элемент конфигурации;
можно выстроить цепочку вычислений одних свойств на основе других;
можно переопределить один элемент в цепочке зависимых свойств не нарушая формулы расчета остальных элементов цепочки
можно использовать популярный и простой формат для конфигурации, 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 |
для чтения конфигурации из словаря и всего совместимого с ним |
+ |