Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

dgsettings

Универсальный менеджер конфигураций Python-приложений с поддержкой версионирования

  • ✔️ Чтение и авто-перезагрузка YAML/JSON-файлов с поддержкой переменных среды и .env
  • ✔️ Хранение и обновление конфигов из различных источников: файлов, PostgreSQL, Redis
  • ✔️ Версионирование конфигураций — сохранение истории изменений и возможность загрузки конкретных версий
  • ✔️ Поддержка RedisJSON и dgredis — автоматическое определение и использование оптимального формата хранения
  • ✔️ Плагинная архитектура — легко расширяется под ваши нужды
  • ✔️ Работа с несколькими сервисами (service_name)
  • ✔️ Легкая интеграция c любой средой (FastAPI, Celery, классические сервисы, микросервисы)

Возможности

  • Settings: Основной класс для работы с деревом конфигурации.
  • FileConfigStorage: Файловое хранение с автоматическим обновлением при изменении.
  • DBConfigStorage: Встроенная поддержка PostgreSQL и других СУБД через SQLAlchemy с версионированием, автоматическое создание таблицы с JSON-полями.
  • RedisConfigStorage: Быстрый конфиг через Redis с поддержкой RedisJSON и dgredis.
  • check_reload(): Позволяет легко реализовать "hot reload" настроек сервиса, если источник изменился.

Установка

pip install dgsettings  # базовая версия
pip install dgsettings[redis]    # для Redis-хранилища
pip install dgsettings[db]       # для работы с БД (PostgreSQL)
pip install dgsettings[dgredis]  # для dgredis клиента
pip install dgsettings[all]      # полная установка

Пример использования

Самый простой вариант: использование файлового слоя

from dgsettings import Settings

settings = Settings(file_="config.yaml", env_file=".env")
db = settings.get("db")
print(db["user"])   # поддерживаются переменные окружения и .env-файлов: db_user: "%PGUSER%"

# Авто-перезагрузка (например, в цикле сервиса)
if settings.check_reload():
    print("Конфиг-файл изменился! Нужно обновить состояние приложения")

Использование с PostgreSQL (DBConfigStorage) с версионированием

from dgsettings import Settings, DBConfigStorage
from sqlalchemy import create_engine

engine = create_engine("postgresql://user:pass@localhost:5432/mydb")

# Простой вариант с автосозданием таблицы
storage = DBConfigStorage(
    engine=engine,
    service_name='my_service',
    auto_reload=True,
    create_table=True  # автоматически создаст таблицу app_configs если её нет
)

settings = Settings(storage=storage)

# Сохранение создаёт новую версию
version = storage.save({'db_host': 'localhost', 'db_port': 5432})
print(f"Saved as version {version}")

# Загрузка конкретной версии
config_v1 = storage.load(version=1)

# Получение всех версий
versions = storage.get_versions()
print(f"Available versions: {versions}")

# Удаление старых версий
storage.delete_version(1)

Расширенный пример с кастомной таблицей

from sqlalchemy import Table, MetaData

# Если нужна кастомная таблица
metadata = MetaData()
table = Table("custom_configs", metadata, autoload_with=engine)

storage = DBConfigStorage(
    engine=engine,
    table=table,
    key_column='app_name',
    value_column='json_data',
    version_column='version_num',
    service_name='my_service',
    auto_reload=True
)

Использование с Redis (RedisConfigStorage)

from dgsettings import Settings, RedisConfigStorage
import redis

# Стандартный redis клиент
client = redis.Redis(host="localhost", decode_responses=True)

storage = RedisConfigStorage(
    redis_client=client,
    redis_key="config:myservice",
    ts_key="config:myservice:ts",  # для hot reload
    auto_reload=True,
    use_redis_json=True  # попытаться использовать RedisJSON если доступен
)

settings = Settings(storage=storage)

# Проверка используемого формата хранения
print(storage.get_storage_info())
# {'use_redis_json': True, 'storage_format': 'RedisJSON', 'is_dgredis': False}

Использование с dgredis (с автоматическим JSON)

from dgsettings import Settings, RedisConfigStorage
import dgredis

# dgredis синхронный
dgredis_client = dgredis.RedisClient(host='localhost', port=6379, db=0)
storage = RedisConfigStorage(
    redis_client=dgredis_client,
    redis_key='config:myservice',
    use_redis_json=True  # dgredis поддерживает JSON нативно
)

# dgredis асинхронный
async_client = dgredis.AsyncRedisClient(host='localhost')
async_storage = RedisConfigStorage(
    redis_client=async_client,
    redis_key='config:myservice',
    use_redis_json=True
)

# Асинхронное использование
async def load_config():
    config = await async_storage.load()
    await async_storage.save({'new_setting': 'value'})
    changed = await async_storage.has_changed()

API кратко

Основные методы Settings

  • Settings(file_="...", env_file="...") — создание и авто-парсинг конфига.
  • parse(file_) — парсинг файла (JSON/YAML).
  • save(file_) — сохранить текущий конфиг в файл.
  • get(key, default=None) — получить значение по ключу.
  • set(obj) — установить словарь настроек.
  • check_reload() — если источник изменился, перезагружает, возвращает True.
  • convert("config.yaml", "config.json", use_yaml=False) — переводит между форматами JSON/YAML.

Методы DBConfigStorage с версионированием

  • load(version=None) — загружает конкретную версию или последнюю.
  • save(data, increment_version=True) — сохраняет с созданием новой версии или обновлением текущей.
  • get_versions() — возвращает список всех доступных версий.
  • delete_version(version) — удаляет конкретную версию.
  • get_current_version() — возвращает номер текущей версии.

Методы RedisConfigStorage

  • get_client_info() — информация о типе клиента и формате хранения.
  • force_string_mode() — принудительное переключение на строковое хранение.
  • retry_json_mode() — попытка включить JSON режим.
  • clear_config() — удаление конфигурации.
  • exists() — проверка существования конфигурации.

Переменные среды и .env

Любая строка вида $VARNAME$ или ${VARNAME} в конфиге подменяется на значение переменной среды или из файла .env

.env:

PGUSER=postgres
PGPASSWORD=secret

config.yaml:

db_user: "$PGUSER$"
db_pass: "${PGPASSWORD}"

Автосоздание таблицы PostgreSQL с JSON и версионированием

Таблица создаётся автоматически с оптимальной структурой:

CREATE TABLE app_configs (
    id SERIAL PRIMARY KEY,
    service_name VARCHAR(128) NOT NULL,
    config_data JSON NOT NULL,  -- JSON поле для эффективного хранения
    version INTEGER NOT NULL DEFAULT 1,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX ix_app_configs_service_version ON app_configs(service_name, version);
# Автосоздание таблицы
storage = DBConfigStorage(
    engine=engine,
    service_name='my_service',
    create_table=True  # создаст таблицу если не существует
)

Поддержка различных Redis клиентов

Автоматическое определение типа клиента:

  • redis-py: Стандартный клиент с поддержкой RedisJSON модуля
  • dgredis: Продвинутый клиент с нативной поддержкой JSON
  • Асинхронные клиенты: aioredis, dgredis.AsyncRedisClient

Автоматический выбор формата хранения:

  • RedisJSON: Если установлен модуль RedisJSON на сервере
  • dgredis JSON: Нативная поддержка JSON в dgredis
  • String fallback: Автоматический откат к строковому формату
# Проверка поддерживаемых возможностей
info = storage.get_client_info()
print(f"Client: {info['client_type']}")
print(f"Storage format: {info['storage_format']}")
print(f"Async support: {info['is_async']}")

Необязательные зависимости

  • Базовая функциональность: только PyYAML
  • БД (PostgreSQL): sqlalchemy, psycopg2-binary или asyncpg
  • Redis: redis-py для стандартного клиента
  • dgredis: dgredis для расширенного клиента
  • RedisJSON: модуль RedisJSON на сервере для оптимального хранения

Если зависимости не установлены, соответствующий storage будет недоступен, но базовая функциональность остается рабочей.

Пример сервисной интеграции с версионированием

from dgsettings import Settings, DBConfigStorage
from sqlalchemy import create_engine

# Инициализация
engine = create_engine("postgresql://user:pass@localhost/db")
storage = DBConfigStorage(engine=engine, service_name='web_service', create_table=True)
settings = Settings(storage=storage)

# В основном коде приложения
class MyService:
    def __init__(self):
        self.settings = settings
        self.current_version = storage.get_current_version()
    
    def check_and_reload(self):
        """Проверка и перезагрузка конфигурации"""
        if self.settings.check_reload():
            new_version = storage.get_current_version()
            print(f"Config reloaded! Version: {self.current_version} -> {new_version}")
            self.current_version = new_version
            self.apply_new_settings()
            return True
        return False
    
    def rollback_to_version(self, version: int):
        """Откат к предыдущей версии"""
        config = storage.load(version=version)
        if config:
            self.settings.set(config)
            print(f"Rolled back to version {version}")

# В потоке/таймере/worker-e проверяем:
service = MyService()
if service.check_and_reload():
    print("Settings were updated!")

Расширяемость

Вы можете реализовать свой storage (например, через S3, Consul, etcd). Просто реализуйте абстрактный класс ConfigStorageBase:

from dgsettings.storage import ConfigStorageBase

class MyCustomStorage(ConfigStorageBase):
    def load(self) -> dict:
        # Загрузка конфигурации из вашего источника
        pass
    
    def save(self, data: dict):
        # Сохранение конфигурации
        pass
    
    def has_changed(self) -> bool:
        # Проверка изменений для auto_reload
        pass

Миграция конфигураций между версиями

# Получение всех версий для анализа
versions = storage.get_versions()

for version in versions:
    config = storage.load(version=version)
    print(f"Version {version}: {list(config.keys())}")

# Создание новой версии на основе старой с изменениями
old_config = storage.load(version=1)
new_config = {**old_config, 'new_feature_enabled': True}
new_version = storage.save(new_config)

Производительность и рекомендации

Для PostgreSQL:

  • Используйте JSON поля для эффективных запросов
  • Регулярно очищайте старые версии конфигураций
  • Настройте индексы для быстрого поиска по service_name

Для Redis:

  • Предпочитайте RedisJSON для сложных конфигураций
  • Используйте dgredis для максимальной производительности
  • Настройте TTL для временных конфигураций

Общие рекомендации:

  • Включайте auto_reload=True только при необходимости
  • Используйте версионирование для критичных сервисов
  • Настройте мониторинг изменений конфигураций

Contributing

Пулреквесты, багрепорты и обсуждения приветствуются!

Запуск тестов

# Установка dev зависимостей
pip install -e .[dev]

# Запуск тестов
pytest tests/

# Запуск с покрытием
pytest --cov=dgsettings tests/

Лицензия

MIT

Авторы:
Roman Rasputin

Примеры, полная документация по API и архитектуре — см. исходный код и примеры в репозитории!

Metadata

Release files for dgsettings 1.0.0a10

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dgsettings 1.0.0a10
File Size Uploaded Provenance Actions
dgsettings-1.0.0a10.tar.gz 21.7 kB Without provenance Details for dgsettings-1.0.0a10.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for dgsettings 1.0.0a10
File Interpreter ABI Platform Actions
dgsettings-1.0.0a10-py3-none-any.whl Python 3 none any Details for dgsettings-1.0.0a10-py3-none-any.whl

Total release size: 41.7 kB

Release files / dgsettings-1.0.0a10.tar.gz

Download URL dgsettings-1.0.0a10.tar.gz
Size 21.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6ba2b68eebc36b64f4178f3cb758453a82628745380aebf2540efbd966f2de86
BLAKE2b-256 checksum
How to use checksums
fb4fce4232ed541dd98cdde83c38442861a6afecd994fce1f47303650e2fdb8e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.2

Release files / dgsettings-1.0.0a10-py3-none-any.whl

Download URL dgsettings-1.0.0a10-py3-none-any.whl
Size 19.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7c3891594dc865b0b409370093949317ecfdee8d313afcf5268e9bcecf6cde48
BLAKE2b-256 checksum
How to use checksums
f2086fade1e504cd87157edcb5c5c77cf4b65c9eb558e2911f69d748e65101eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.2

Security for dgsettings 1.0.0a10

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page