Рубрики
Без рубрики

Использование переменных среды в Python для конфигурации приложения и секретов

Узнайте, как опытные разработчики используют переменные среды в Python, включая управление … Tagged с Python, Security.

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

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

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

Не знакомы с переменными окружающей среды? Проверьте наш Ultimate Guide для использования переменных среды в Linux и Mac Анкет

Зачем использовать переменные среды для настройки приложений Python?

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

  • Развернуть приложение в любой среде без изменений кода
  • Обеспечивает, чтобы секреты, такие как клавиши API, не протекают в исходный код

Переменные среды имеют дополнительное преимущество абстрагирования вашего приложения, как предоставляются конфигурация и секреты.

Наконец, переменные среды позволяют вашему приложению работать где угодно, будь то для локальной разработки на MacOS, контейнер в капсуле Kubernetes, или такие платформы, как Heroku или Vercel.

Вот несколько примеров использования переменных среды для настройки скрипта или приложения Python:

  • Установить Flask_env переменная среды к “Развитие” Для включения режима отладки для приложения к фляске
  • Предоставить Stripe_api_key переменная среды для сайта электронной коммерции
  • Снабжать Discord_token переменная среды для приложения BOT Discord так что он может присоединиться к серверу
  • Установить специфические для среды переменные базы данных, такие как Db_user и Db_password Таким образом, учетные данные о базе данных не жесткие

Как заполняются переменные среды в Python?

Когда создается процесс Python, доступные переменные среды заполняют Os.environ объект, который действует как словарь питона. Это значит, что:

  • Любые изменения переменной среды, сделанные после создания процесса Python, не будут отражены в процессе Python.
  • Любые изменения переменной среды в Python не влияют на переменные среды в родительском процессе.

Теперь, когда вы знаете, как заполнены переменные среды в Python, давайте посмотрим, как получить к ним доступ.

Как получить переменную среды Python

Переменные среды в Python доступны с использованием ОС Анкет среда объект.

Os.environ Объект кажется как словарь, но отличается, так как значения могут быть только строками, плюс он не предназначен для JSON.

У вас есть несколько вариантов, когда дело доходит до ссылки на Os.environ объект:

# 1. Standard way
import os
# os.environ['VAR_NAME']

# 2. Import just the environ object
from os import environ
# environ['VAR_NAME']

# 3. Rename the `environ` to env object for more concise code
from os import environ as env
# env['VAR_NAME']

Я лично предпочитаю версию 3, поскольку она более лаконичная, но буду придерживаться использования Os.environ для этой статьи.

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

Давайте рассмотрим с некоторыми примерами.

Вариант 1: Требуется без значения по умолчанию

Если ваше приложение будет сбоем, когда переменная среды не установлена, тогда обращайтесь к нему напрямую:

print(os.environ['HOME']
# >> '/home/dev'

print(os.environ['DOES_NOT_EXIST']
# >> Will raise a KeyError exception

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

Если вместо дефолта Keyerror Исключение поднимается (что не сообщает, почему ваше приложение не началось), вы можете запечатлеть исключение и распечатать полезное сообщение:

import os
import sys

# Ensure all required environment variables are set
try:  
    os.environ['API_KEY']
except KeyError: 
    print('[error]: `API_KEY` environment variable required')
    sys.exit(1)

Вариант 2: Требуется со значением по умолчанию

Вы можете получить значение по умолчанию, возвращаемое, если переменная среды не существует, используя Os.environ.get метод и поставка значения по умолчанию в качестве второго параметра:

# If HOSTNAME doesn't exist, presume local development and return localhost
print(os.environ.get('HOSTNAME', 'localhost')

Если переменная не существует, и вы используете Os.environ.get без значения по умолчанию, Нет возвращается

assert os.environ.get('NO_VAR_EXISTS') == None

Вариант 3: условная логика, если значение существуют

Вам может потребоваться проверить, существует ли переменная среды, но не обязательно заботиться о ее значении. Например, ваше приложение может быть помещено в «режим отладки», если Отладка переменная среды установлена.

Вы можете проверить только наличие переменной среды:

if 'DEBUG' in os.environ:
    print('[info]: app is running in debug mode')

Или проверить, чтобы увидеть, как это соответствует конкретному значению:

if os.environ.get('DEBUG') == 'True':
    print('[info]: app is running in debug mode')

Как установить переменную среды Python

Установка переменной среды в Python – это то же самое, что настройка клавиши на словаре:

os.environ['TESTING'] = 'true'

Что делает Os.environ отличается от стандартного словаря, заключается в том, что разрешены только строковые значения:

os.environ['TESTING'] = True
# >> TypeError: str expected, not bool

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

Например, построение DB_URL Переменная среда при запуске приложения с помощью Db_host , DB_PORT , Db_user , Db_password и Db_name переменные среды:

os.environ['DB_URL'] = 'psql://{user}:{password}@{host}:{port}/{name}'.format(
    user=os.environ['DB_USER'],
    password=os.environ['DB_PASSWORD'],
    host=os.environ['DB_HOST'],
    port=os.environ['DB_PORT'],
    name=os.environ['DB_NAME']
)

Другим примером является установка переменной на значение по умолчанию на основе значения другой переменной:

# Set DEBUG and TESTING to 'True' if ENV is 'development'
if os.environ.get('ENV') == 'development':
    os.environ.setdefault('DEBUG', 'True') # Only set to True if DEBUG not set
    os.environ.setdefault('TESTING', 'True') # Only set to True if TESTING not set

Как удалить переменную среды Python

Если вам нужно удалить переменную среды Python, используйте Os.environ.pop Функция:

os.environ.pop['MY_KEY']

Чтобы расширить наш DB_URL Пример выше, вы можете удалить другой DB_ Префиксированные поля, чтобы убедиться, что единственный способ, которым приложение может подключиться к базе данных, – через DB_URL :

Другим примером является удаление переменной среды, как только она больше не нужна:

auth_api(os.environ['API_KEY']) # Use API_KEY
os.environ.pop('API_KEY') # Delete API_KEY as it's no longer needed

Как перечислить переменные среды Python

Чтобы просмотреть все переменные среды:

print(os.environ)

Вывод этой команды трудно прочитать, хотя он напечатан как один огромный словарь.

Лучший способ – создать удобную функцию, которая преобразует Os.environ к настоящему словару, чтобы мы могли сериализовать его на JSON для симпатичной печати:

import os
import json

def print_env():
    print(json.dumps({**{}, **os.environ}, indent=2))

Почему значения по умолчанию для переменных среды следует избегать

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

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

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

Использование файла .env для переменных среды Python

По мере того, как приложение растет в размерах и сложности, также количество переменных среды.

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

Простое (но нелегкое масштабируемое) решение – использовать .env Файл, чтобы содержать все переменные для конкретной среды.

Тогда вы бы использовали библиотеку Python, такую как Python-Dotenv анализировать .env Подайте и заполняйте Os.environ объект.

Чтобы следовать, создать и активировать новый Виртуальная среда , затем установите Python-Dotenv библиотека:

python3 -m venv ~/.virtualenvs/doppler-tutorial      # 1. Create
source ~/.virtualenvs/doppler-tutorial/bin/activate  # 2. Activate
pip install python-dotenv                            # 3. Install dotenv

Теперь сохраните приведенный ниже файл с именем .env (Обратите внимание, как это тот же синтаксис для установки переменной в оболочке):

API_KEY="357A70FF-BFAA-4C6A-8289-9831DDFB2D3D"
HOSTNAME="0.0.0.0"
PORT="8080"

Затем сохраните следующее в dotenv-test.py :

# Rename `os.environ` to `env` for nicer code
from os import environ as env

from dotenv import load_dotenv
load_dotenv()

print('API_KEY:  {}'.format(env['API_KEY']))
print('HOSTNAME: {}'.format(env['HOSTNAME']))
print('PORT:     {}'.format(env['PORT']))

Затем запустить dotenv-test.py Для проверки переменных среды заполняются:

python3 dotenv-test.py
# >> API_KEY:  357A70FF-BFAA-4C6A-8289-9831DDFB2D3D
# >> HOSTNAME: 0.0.0.0
# >> PORT:     8080

В то время как .env Файлы просты и легко работают в начале, они также вызывают новый набор проблем, таких как:

  • Как сохранить .env Файлы в синхронизации для каждого разработчика в их локальной среде?
  • Если есть отключение из -за неправильной конфигурации, доступ к контейнеру или виртуальной машине непосредственно, чтобы просмотреть содержимое .env может потребоваться для устранения неполадок.
  • Как вы генерируете .env Файл для работы CI/CD, таких как действия GitHub, без совершения .env файл в репозиторий?
  • Если сочетание переменных среды и .env Используется файл, единственным способом определения окончательных значений конфигурации может быть интроспекция приложения.
  • Собирая разработчика, поделившись незашифрованным .env Файл с потенциально конфиденциальными данными в приложении в чате, таком как Slack, может представлять проблемы безопасности.

Это лишь некоторые из причин, по которым мы рекомендуем Отказ от файлов .env И используя что -то вроде Допплеров вместо.

Допплер предоставляет панель управления доступом для управления переменными среды для каждой среды с Легко в использовании CLI для доступа к конфигурации и секретам Это работает для каждого языка, структуры и платформы.

Централизовать конфигурацию приложения с использованием структуры данных Python

Создание конфигурационной структуры данных абстрагирует, как устанавливаются значения конфигурации, какие поля имеют значения по умолчанию (если таковые имеются), и предоставляет один интерфейс для доступа к значениям конфигурации вместо Os.environ быть заваленным по всей вашей кодовой базе.

Ниже приведено достаточно полнофункциональное решение, которое поддерживает:

  • Требуемые поля
  • Дополнительные поля с по умолчанию
  • Проверка типа и тип

Чтобы попробовать это, сохраните этот код в config.py :

import os
from typing import get_type_hints, Union
from dotenv import load_dotenv

load_dotenv()

class AppConfigError(Exception):
    pass

def _parse_bool(val: Union[str, bool]) -> bool:  # pylint: disable=E1136 
    return val if type(val) == bool else val.lower() in ['true', 'yes', '1']

# AppConfig class with required fields, default values, type checking, and typecasting for int and bool values
class AppConfig:
    DEBUG: bool = False
    ENV: str = 'production'
    API_KEY: str
    HOSTNAME: str
    PORT: int

    """
    Map environment variables to class fields according to these rules:
        - Field won't be parsed unless it has a type annotation
        - Field will be skipped if not in all caps
        - Class field and environment variable name are the same
    """
    def __init__(self, env):
        for field in self.__annotations__:
            if not field.isupper():
                continue

            # Raise AppConfigError if required field not supplied
            default_value = getattr(self, field, None)
            if default_value is None and env.get(field) is None:
                raise AppConfigError('The {} field is required'.format(field))

            # Cast env var value to expected type and raise AppConfigError on failure
            try:
                var_type = get_type_hints(AppConfig)[field]
                if var_type == bool:
                    value = _parse_bool(env.get(field, default_value))
                else:
                    value = var_type(env.get(field, default_value))

                self.__setattr__(field, value)
            except ValueError:
                raise AppConfigError('Unable to cast value of "{}" to type "{}" for "{}" field'.format(
                    env[field],
                    var_type,
                    field
                )
            )

    def __repr__(self):
        return str(self.__dict__)

# Expose Config object for app to import
Config = AppConfig(os.environ)

Конфигурация Объект, обнаруженный в config.py затем используется app.py ниже:

from config import Config

print('ENV:      {}'.format(Config.ENV))
print('DEBUG:    {}'.format(Config.DEBUG))
print('API_KEY:  {}'.format(Config.API_KEY))
print('HOSTNAME: {}'.format(Config.HOSTNAME))
print('PORT:     {}'.format(Config.PORT))

Убедитесь, что у вас есть .env Файл все еще сохранен из ранее, затем запустите:

python3 app.py 
# >> ENV:      production
# >> DEBUG:    False
# >> API_KEY:  357A70FF-BFAA-4C6A-8289-9831DDFB2D3D
# >> HOSTNAME: 0.0.0.0
# >> PORT:     8080

Вы можете Просмотреть этот код на GitHub И если вы хотите более полнофункциональное решение для конфигурации Typesafe, то посмотрите отличное Пидантическая библиотека.

Резюме

Потрясающая работа!   Теперь вы знаете, как использовать переменные среды в Python для конфигурации и секретов приложения.

Хотя мы немного предвзяты, мы призываем вас Попробуйте использовать доплеров в качестве источника истины для переменных среды вашей приложения, и это бесплатно, чтобы начать с нашего плана сообщества (неограниченные проекты и секреты).

У нас также есть учебник для построения Случайный генератор GIF Mandalorion Это применяет на практике методы, показанные в этой статье.

Надеюсь, вам понравился пост, и если у вас есть какие -либо вопросы или отзывы, мы хотели бы поболтать с вами в нашем Форум сообщества .

Большое спасибо Stevoisiak , Olivier Pilotte , Джейкоб Каснер и Алекс Холл за их вклад и обзор!

Фотография Хитеш Чоудхари на unsplash Анкет

Оригинал: “https://dev.to/doppler/using-environment-variables-in-python-for-app-configuration-and-secrets-3708”