Управление конфигурацией приложений на Python

Раздел: Администрирование -> Настройка окружения

Выбор способа настройки приложения на Python

Как реализовать настройки с автоматической валидацией и поддержкой файла .env?

Наиболее эффективным решением для управления конфигурацией является использование модуля pydantic-settings. Он позволяет объявить класс настроек с типами, значениями по умолчанию, валидацией, а также автоматически загружать переменные из окружения и файлов .env.

pip install pydantic-settings python-dotenv

как добавить русский язык в python (добавление поддержки русского языка в python)

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    app_name: str = "MyApp"
    debug: bool = False
    database_url: str
    secret_key: str = "default-secret"

    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")

settings = Settings()
print(settings.database_url)

Python настройки приложения (настройки приложения на python)

В файле .env указываются значения:

DATABASE_URL=postgresql://user:pass@localhost/db
SECRET_KEY=mysecret

Python переменные окружения (переменные окружения в python)

Pydantic автоматически прочитает .env и сопоставит имена полей с переменными окружения (без учёта регистра, но обычно в верхнем регистре). Если обязательное поле не задано, возникнет ValidationError.

Типичная ошибка: забыть указать env_file в конфиге, тогда .env не будет прочитан. Другая проблема: имена переменных окружения могут конфликтовать с системными. Рекомендуется использовать префиксы, например MYAPP_ и задать env_prefix.

Как получить настройки из системных переменных окружения без внешних библиотек?

Модуль os предоставляет простой способ: функции os.environ.get() или os.getenv().

import os

app_name = os.getenv('APP_NAME', 'DefaultApp')
debug = os.getenv('DEBUG', 'false').lower() == 'true'
database_url = os.environ.get('DATABASE_URL')
if not database_url:
    raise ValueError('DATABASE_URL must be set')

Path python (путь к python)

Этот подход не требует установки дополнительных библиотек, но все значения являются строками, и требуется ручное преобразование типов.

Проблема: если переменная не задана, os.getenv() вернет None, что может вызвать ошибку при преобразовании. Нужно всегда проверять или задавать значение по умолчанию.

Как использовать стандартный модуль configparser для ini-файлов?

ConfigParser удобен для конфигурационных файлов в стиле INI, популярных в Windows.

import configparser

config = configparser.ConfigParser()
config.read('config.ini')

app_name = config.get('DEFAULT', 'AppName', fallback='MyApp')
debug = config.getboolean('DEFAULT', 'Debug', fallback=False)
database_url = config.get('Database', 'url')

Python environment path (путь к окружению python)

Файл config.ini:

[DEFAULT]
AppName = MyApp
Debug = false

[Database]
url = sqlite:///db.sqlite3

Python windows paths (работа с путями в python на windows)

Плюсы: встроенный модуль, поддержка секций, автоматическое преобразование boolean. Минусы: только строки и числа, нет поддержки вложенных структур.

Ошибка: если файл отсутствует, read() просто пропускает, и config.get упадет с KeyError, если не указан fallback. Стоит проверять список прочитанных файлов.

Как организовать конфигурацию в формате YAML?

Библиотека PyYAML позволяет загружать настройки из YAML-файлов, поддерживающих списки, словари и комментарии.

import yaml

with open('config.yaml', 'r') as f:
    config = yaml.safe_load(f)

app_name = config.get('app', {}).get('name', 'MyApp')
debug = config.get('app', {}).get('debug', False)
database_url = config.get('database', {}).get('url')
print(config)

Python файлы настроек (файлы конфигурации в python)

Файл config.yaml:

app:
  name: MyApp
  debug: false
database:
  url: postgresql://localhost/db

Open python 2 (запуск python 2)

Важно использовать safe_load для предотвращения выполнения произвольного кода.

Ошибка: загрузка с помощью yaml.load() может привести к инъекции кода, если файл поступает из ненадёжного источника. Всегда применяйте safe_load.

Как хранить настройки в JSON?

JSON - простой и распространённый формат. Чтение через стандартный модуль json.

import json

with open('config.json', 'r') as f:
    config = json.load(f)

app_name = config.get('app_name', 'MyApp')
debug = config.get('debug', False)
database_url = config.get('database_url')
print(config)

Python online interpreter (онлайн интерпретатор python)

Файл config.json:

{
  "app_name": "MyApp",
  "debug": false,
  "database_url": "sqlite:///db.sqlite3"
}

Using python pip (установка пакетов через pip в python)

JSON не поддерживает комментарии и не допускает лишних запятых.

Проблема: синтаксическая ошибка в JSON (например, лишняя запятая) вызовет json.JSONDecodeError. Рекомендуется использовать валидаторы при редактировании.

Как использовать TOML для конфигурации Python приложений?

TOML - формат, используемый в экосистеме Python (pyproject.toml). Для чтения применяется модуль tomllib (Python 3.11+) или сторонняя библиотека tomli.

import tomllib  # или import tomli для старых версий

with open('config.toml', 'rb') as f:
    config = tomllib.load(f)

app_name = config.get('app', {}).get('name', 'MyApp')
debug = config.get('app', {}).get('debug', False)
database_url = config.get('database', {}).get('url')
print(config)

Python interpreter (интерпретатор python)

Файл config.toml:

[app]
name = "MyApp"
debug = false

[database]
url = "sqlite:///db.sqlite3"

Python добавить путь (добавление пути в sys.path в python)

TOML поддерживает типы: строки, числа, даты, вложенные таблицы.

Проблема: некоторые версии Python не имеют встроенного tomllib; требуется установка tomli. Также при попытке загрузить файл с неверным синтаксисом возникает tomllib.TOMLDecodeError.

Как передавать настройки через аргументы командной строки?

Модуль argparse позволяет определить параметры запуска, которые переопределяют настройки из файлов или окружения.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--app-name', default='MyApp')
parser.add_argument('--debug', action='store_true')
parser.add_argument('--database-url', required=True)

args = parser.parse_args()

app_name = args.app_name
debug = args.debug
database_url = args.database_url
print(f"{app_name=}, {debug=}, {database_url=}")

Вызов: python app.py --app-name Demo --database-url postgres://...

Аргументы полезны для временных изменений, не требующих редактирования файлов.

Ошибка: если обязательный аргумент не указан, argparse выведет ошибку и завершит программу. Следует задавать разумные значения по умолчанию.

Расширенные примеры конфигурации

Комбинирование переменных окружения, .env и pydantic-settings с префиксом

В продакшене часто требуется задавать переменные с префиксом, чтобы избежать конфликтов. Пример с префиксом и использованием модели с вложенными настройками.

Пример
# .env
MYAPP_DATABASE_URL=postgresql://prod:pass@pg:5432/db
MYAPP_DEBUG=false
MYAPP_SECRET_KEY=supersecret

# settings.py
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing import Optional

class DatabaseConfig(BaseSettings):
    url: str
    pool_size: int = 5

class AppSettings(BaseSettings):
    app_name: str = "MyApp"
    debug: bool = False
    secret_key: Optional[str] = None
    database: DatabaseConfig = DatabaseConfig()

    model_config = SettingsConfigDict(env_prefix='MYAPP_', env_file='.env', env_nested_delimiter='__')

settings = AppSettings()
print(f"DB URL: {settings.database.url}, pool size: {settings.database.pool_size}")

Результат выполнения при наличии .env как выше:

DB URL: postgresql://prod:pass@pg:5432/db, pool size: 5

Если в .env не указан pool_size, используется значение по умолчанию. Ошибка возникнет, если обязательное поле url не будет задано.

Проблема: вложенные модели требуют разделителя (env_nested_delimiter). По умолчанию используется '__', но можно изменить. Без него pydantic не сможет сопоставить переменные окружения с вложенными полями.

Загрузка конфигурации из YAML с проверкой схемы через pydantic

Иногда удобно загружать YAML, но при этом проверять типы. Можно объединить yaml и pydantic.

Пример
import yaml
from pydantic import BaseModel, Field
from typing import List

class ServerConfig(BaseModel):
    host: str = "0.0.0.0"
    port: int = 8000

class DatabaseConfig(BaseModel):
    url: str
    pool_size: int = 5

class AppConfig(BaseModel):
    app_name: str
    debug: bool = False
    servers: List[ServerConfig] = []
    database: DatabaseConfig

# config.yaml
# app_name: MyYamlApp
# debug: true
# database:
#   url: sqlite:///test.db
# servers:
#   - host: localhost
#     port: 8080
#   - host: 0.0.0.0
#     port: 9090

with open('config.yaml') as f:
    raw = yaml.safe_load(f)

config = AppConfig(**raw)
print(config.model_dump_json(indent=2))

Результат:

{
  "app_name": "MyYamlApp",
  "debug": true,
  "servers": [
    {"host": "localhost", "port": 8080},
    {"host": "0.0.0.0", "port": 9090}
  ],
  "database": {
    "url": "sqlite:///test.db",
    "pool_size": 5
  }
}

Этот подход гарантирует, что структура данных соответствует ожидаемой схеме.

Ошибка: если в YAML присутствует лишнее поле, pydantic по умолчанию его проигнорирует (если не настроено extra). Чтобы выявить лишние ключи, задайте model_config = ConfigDict(extra='forbid').

Использование argparse совместно с .env файлом

Часто требуется, чтобы аргументы командной строки имели приоритет над переменными окружения, которые, в свою очередь, переопределяют значения по умолчанию.

Пример
import argparse
import os
from dotenv import load_dotenv

load_dotenv('.env')

def get_config():
    parser = argparse.ArgumentParser()
    parser.add_argument('--app-name', default=None)
    parser.add_argument('--debug', action='store_true', default=None)
    parser.add_argument('--database-url', default=None)
    args = parser.parse_args()

    config = {
        'app_name': args.app_name or os.getenv('APP_NAME', 'DefaultApp'),
        'debug': args.debug if args.debug is not None else os.getenv('DEBUG', 'false').lower() == 'true',
        'database_url': args.database_url or os.getenv('DATABASE_URL')
    }
    return config

config = get_config()
print(config)

Вызов без аргументов подхватит переменные из .env. Вызов python app.py --debug --app-name CliName переопределит их.

Проблема: сложно обеспечить корректное преобразование типов для разных источников. Для булевых значений нужно аккуратно обрабатывать флаги. Лучше использовать библиотеки вроде pydantic-settings, которые делают это автоматически.

Настройки приложения на Python - comments

En
Python настройки приложения (python)