Руководство по переменным окружения для администратора

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

Основные методы работы с переменными окружения

Переменные окружения позволяют управлять конфигурацией приложения без изменения кода. В Python существует несколько способов их чтения и установки. Ниже рассматриваются наиболее распространённые подходы, их цели и возможные проблемы.

Эффективное решение: использование python-dotenv

Библиотека python-dotenv упрощает загрузку переменных из файла .env в os.environ. Это позволяет хранить чувствительные данные (токены, пароли) отдельно от кода и легко переключаться между окружениями (разработка, продакшн).

Как загрузить переменные из .env файла?


# Установка
# pip install python-dotenv

# .env файл в корне проекта
API_KEY=abc123
DATABASE_URL=postgresql://user:pass@localhost/db

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


# main.py
import os
from dotenv import load_dotenv

# Загрузка .env (по умолчанию ищет .env в текущем каталоге)
load_dotenv()

api_key = os.getenv('API_KEY')
database_url = os.getenv('DATABASE_URL')

print(api_key)  # abc123
print(database_url)  # postgresql://user:pass@localhost/db

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

Типичные ошибки:

  • Файл .env не найден - load_dotenv() вернёт False, переменные останутся без изменений. Рекомендуется проверять возвращаемое значение и выводить предупреждение.
  • Переменные в .env переопределяют системные? По умолчанию load_dotenv() не заменяет уже установленные переменные. Если нужно принудительно перезаписать, следует указать load_dotenv(override=True).
  • Пробелы в значениях: значения обрезаются. Для сохранения пробелов значения следует заключать в кавычки в .env: VAR=" value with spaces ".

Цель использования: отделить конфигурацию от кода, упростить развёртывание, избежать хранения ключей в репозитории.

Вариант 1: прямой доступ через os.environ

Модуль os предоставляет словарь environ, содержащий все текущие переменные окружения.

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


import os

# Чтение (может вызвать KeyError)
path = os.environ['PATH']
print(path)

# Безопасное чтение с умолчанием
home = os.environ.get('HOME', '/tmp')
print(home)

# Установка новой переменной (действует только в текущем процессе)
os.environ['MY_CUSTOM_VAR'] = 'hello'

Path python (путь к python)

Ошибки и проблемы:

  • Обращение к отсутствующему ключу - KeyError. Следует всегда использовать метод get() или проверять вхождение оператором in.
  • Значениями могут быть только строки. Попытка присвоить число приведёт к ошибке типа.
  • Изменения через os.environ не сохраняются после завершения процесса.

Цель: быстрый доступ к системным переменным (PATH, USER), подходит для простых скриптов.

Вариант 2: функция os.getenv

Обёртка над os.environ.get().


import os

db_url = os.getenv('DATABASE_URL', 'sqlite:///default.db')

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

Разница только в синтаксисе. Функция os.getenv - это синоним os.environ.get.

Ошибки: аналогичны os.environ. Не загружает .env автоматически.

Вариант 3: использование configparser для INI-файлов

Когда требуется структурированная конфигурация с секциями, удобно использовать configparser.


import configparser

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

# Получение значения из секции DEFAULT
db_host = config['DEFAULT']['Host']
print(db_host)

# Безопасное получение с умолчанием
port = config['DEFAULT'].get('Port', '5432')

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

Проблемы:

  • Файл может отсутствовать - read() вернёт пустой список. Рекомендуется проверять результат config.read().
  • Кодировка: по умолчанию ожидается UTF-8, но можно указать параметр encoding='utf-8'.
  • Не поддерживает переменные окружения напрямую, требуется их смешивание вручную.

Цель: сложные конфигурации с группировкой (например, разные настройки для БД, кэша).

Вариант 4: pydantic-settings с валидацией

Библиотека pydantic-settings (на базе Pydantic) позволяет описывать модель настроек с указанием типов, валидацией и автоматическим чтением из .env.


# pip install pydantic-settings

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = 'MyApp'
    admin_email: str
    secret_key: str

    class Config:
        env_file = '.env'          # откуда загружать
        env_file_encoding = 'utf-8'
        # extra = 'ignore'         # игнорировать лишние переменные

settings = Settings()
print(settings.admin_email)

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

Ошибки:

  • Если обязательная переменная не задана, Pydantic выбросит ValidationError.
  • Требуется установка pydantic и pydantic-settings. Для простых проектов может быть избыточно.

Цель: типизированная, самодокументируемая конфигурация с проверкой на этапе запуска.

Вариант 5: комбинация с argparse для CLI

Иногда нужно, чтобы аргументы командной строки имели приоритет над переменными окружения.


import argparse
import os

parser = argparse.ArgumentParser()
parser.add_argument('--host', default=os.getenv('HOST', 'localhost'))
args = parser.parse_args()

print(f'Host: {args.host}')

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

Цель: гибкие утилиты командной строки, которые можно настраивать и через CLI, и через окружение.

- Using python pip (установка пакетов через pip в python)
- Python interpreter (интерпретатор python)
- Python добавить путь (добавление пути в sys.path в python)

Пример 1: Загрузка окружения в зависимости от режима (development/production)

Использование разных .env файлов для разных сред. Файлы .env.development, .env.production. Загрузка с переменной окружения ENV.

Пример

import os
from dotenv import load_dotenv

env_mode = os.getenv('ENV', 'development')
env_file = f'.env.{env_mode}'

if not load_dotenv(env_file):
    print(f'Предупреждение: файл {env_file} не найден')
    # fallback на .env
    load_dotenv('.env')

# Теперь переменные из соответствующего файла загружены
print(os.getenv('DATABASE_URL'))
# При запуске с ENV=production
# postgresql://prod_user:prod_pass@prod_host/db

Пример 2: Чтение Docker secrets через переменные окружения

Docker монтирует секреты как файлы в /run/secrets/. Можно прочитать их и записать в os.environ для использования приложением.

Пример

import os

def load_docker_secrets(secrets_dir='/run/secrets'):
    if not os.path.isdir(secrets_dir):
        return
    for secret_file in os.listdir(secrets_dir):
        secret_path = os.path.join(secrets_dir, secret_file)
        with open(secret_path, 'r') as f:
            value = f.read().strip()
        os.environ[secret_file.upper()] = value

load_docker_secrets()
# Теперь os.getenv('DB_PASSWORD') вернёт содержимое файла /run/secrets/db_password

Пример 3: Переопределение системных переменных с python-dotenv

По умолчанию dotenv не перезаписывает существующие переменные. Пример принудительной перезаписи.

Пример

# .env
MY_VAR=from_dotenv

import os
os.environ['MY_VAR'] = 'from_system'

from dotenv import load_dotenv
load_dotenv(override=True)  # перезаписать системные значениями из .env

print(os.getenv('MY_VAR'))  # from_dotenv
from_dotenv

Пример 4: Валидация с Pydantic и несколько источников

Модель может читать как из .env, так и из переменных окружения. Показывает, как обрабатывать ошибки валидации.

Пример

from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import ValidationError

class DatabaseSettings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix='DB_', env_file='.env')

    host: str = 'localhost'
    port: int = 5432
    user: str
    password: str

try:
    db_settings = DatabaseSettings()
except ValidationError as e:
    print(f'Ошибка в конфигурации: {e}')
    exit(1)

print(f'Connecting to {db_settings.user}@{db_settings.host}:{db_settings.port}')
# Если переменная DB_USER не задана
# Ошибка в конфигурации: 1 validation error for DatabaseSettings
# user: field required (type=value_error.missing)

Пример 5: Передача переменных окружения в подпроцесс

При запуске внешней программы через subprocess можно передать изменённое окружение.

Пример

import subprocess
import os

# Создаём новое окружение с дополнительными переменными
env = os.environ.copy()
env['MY_ENV_VAR'] = 'custom_value'

# Запускаем скрипт, который выводит переменную
result = subprocess.run(
    ['python', '-c', 'import os; print(os.getenv("MY_ENV_VAR"))'],
    capture_output=True,
    text=True,
    env=env
)
print(result.stdout)  # custom_value
custom_value

Пример 6: Использование os.environ для временной установки переменной в тестах

В модульных тестах часто нужно временно изменить переменную окружения. Для этого применяется контекстный менеджер unittest.mock.patch.dict.

Пример

import os
from unittest.mock import patch

def test_my_function():
    with patch.dict(os.environ, {'MY_VAR': 'test_value'}):
        # внутри блока MY_VAR = test_value
        assert os.getenv('MY_VAR') == 'test_value'
    # после выхода восстанавливается исходное значение
# Тест проходит

переменные окружения в Python - comments

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