Переменные окружения для Python проектов
При разработки web-приложения или бота мы часто имеем дело с какой-либо секретной информацией, различными токенами и паролями (API-ключами, секретами веб-форм). "Хардкодить" эту информацию, а тем более сохранять в публично доступной системе контроля версий это очень плохая идея.
# Плохая практика. Не делай так.
API_KEY = 'very_secret_password'
Конфигурационные файлы
Самый простой путь решения данной проблемы, это создание отдельного конфигурационного файла со всей чувствительной информацией и добавление его в .gitignore
. Минус такого подхода в том, что в гит нужно держать ещё и шаблон конфигурационного файла и не забывать его периодически обновлять.
# Уже лучше.
from config import API_KEY
app = Flask(__name__)
app.config['API_KEY'] = API_KEY
Переменные окружения
Более продвинутый подход, это использование переменных окружения. Переменные окружения это именованные переменные, содержащие текстовую информацию, которую могут использовать запускаемые программы. Например, чтобы запустить flask-приложение, вначале нужно указать в переменной окружения FLASK_APP
имя нашего приложения:
$ export FLASK_APP=hello.py
$ flask run
* Running on http://127.0.0.1:5000/
С помощью переменных окружения можно получать различные параметры приложение и секретные ключи:
import os
app.config['API_KEY'] = os.environ.get('API_KEY')
Библиотека python-dotenv
Чтобы не задавать каждый раз вручную переменные окружения при новом запуске терминала, можно воспользоваться пакетом python-dotenv. Он позволяет загружать переменные окружения из файла .env
в корневом каталоге приложения.
Устанавливаем пакет:
pip install python-dotenv
Теперь можно создать файл .env со всеми переменными среды, которые необходимы вашему приложению. Важно, добавьте .env
-файл в .gitignore
, не храните его в системе контроля версий.
import os
from dotenv import load_dotenv
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
if os.path.exists(dotenv_path):
load_dotenv(dotenv_path)
Этот .env-файл можно использовать для всех переменных конфигурации, но его нельзя использовать для переменных среды FLASK_APP
и FLASK_DEBUG
, так как они необходимы уже в процессе начальной загрузки приложения.
Утилита direnv
Переменные среды могут быть автоматически загружены при входе в папку с проектом, это особенно удобно при работе с несколькими проектами одновременно. Сделать это позволяет утилита direnv. Direnv — это менеджер переменных среды для терминала, поддерживает bash, zsh, tcsh и др. оболочки. Позволяет автоматически загружать и выгружать переменные среды в зависимости от вашего текущего каталога. Это позволяет иметь переменные среды, специфичные для каждого проекта. Перед каждым приглашением проверяется наличие файла .envrc
в текущем и родительском каталогах. Если файл существует, он загружается в подшаблон bash, и все экспортированные переменные затем захватываются direnv, а затем становятся доступными для оболочки.
Установка direnv
sudo apt-get install direnv
Далее необходимо в нести изменения для настройки нашей оболочки, для bash необходимо в конец файла ~/.bashrc
добавить следующее и перезапустить консоль:
eval "$(direnv hook bash)"
Создадим новую папку для проекта:
$ mkdir ~/my-project
$ cd ~/my-project
Покажем, что переменная окружения FLASK_APP не загружена:
$ echo $FLASK_APP
nope
Запишем переменные окружения в файл .envrc
:
$ echo export FLASK_APP=hello.py > .envrc
.envrc is not allowed
Для обеспечения безопасности, после создания или изменения файла .envrc
, нужно выполнить подтверждение с помощью команды direnv allow:
$ direnv allow .
direnv: reloading
direnv: loading .envrc
direnv export: +FLASK_APP
Покажем, что переменная окружения загружена:
$ echo $FLASK_APP
hello.py
При выхода из папки с проектом переменные окружения выгружаются
$ cd ..
direnv: unloading
и становятся снова не заданными
$ echo $FLASK_APP
nope
Работа с виртуальным окружением в direnv
Кроме загрузки переменных окружения, утилита direnv позволяет также работать с виртуальным окружением для Python.
Виртуальное окружение позволяет использовать для отдельные проектов разные версии интерпретатора python и пакетов библиотек. Существует несколько способов создания виртуального окружения для python, здесь мы рассмотрим модуль venv, для другие варианты описаны в документации к direnv.
Чтобы использовать venv для автоматического создания и активирования виртуального окружения, необходимо добавить в файл ~/.config/direnv/direnvrc
следующий код (см. документацию).
Создание виртуального окружения
Если в файл .envrc
добавить строчку
layout python-venv
то при переходе в папку будет direnv создаст виртуальное окружение в папке direnv, например .direnv/python-venv-3.7.3
.
Чтобы создать виртуальное окружение с другим путем, например в более привычной папке venv, надо задать переменную VIRTUAL_ENV
:
export VIRTUAL_ENV=.venv
Таким же способом можно подключать уже созданное виртуальное окружение.
Работа с разными версиями Python
Для установки отличной от системной версии python, нужно использовать команду:
layout python-venv python3.6
Создаем строку приглашения bash (PS1)
В отличие от ручной активации виртуального окружения, в нашем случае строка приглашения bash (PS1) не будет изменена (обычно она выглядит как (venv) user@comp:~$
). Чтобы вернуть показ активации виртуального окружения в консоли нужно в файл ~/.bashrc
добавить следующий код:
show_virtual_env() {
if [[ -n "$VIRTUAL_ENV" && -n "$DIRENV_DIR" ]]; then
echo "($(basename $VIRTUAL_ENV))"
fi
}
export -f show_virtual_env
PS1='$(show_virtual_env)'$PS1
Пример файла настройки файла .envrc
Вот так может выглядеть файл .envrc
настроенный для разработки flask-приложения:
export VIRTUAL_ENV=venv
layout python-venv
export FLASK_APP=app.py
export FLASK_DEBUG=1
Это позволяет автоматически активировать виртуальное окружение и загрузить переменные окружения при входе в папку с проектом.
Комментарии (11)
mrBuG
23.10.2019 13:58Есть ещё один вариант — управлять виртуальным окружением через pipenv. Оно уже из коробки поддерживает .env файлы и выставляет оттуда переменные окружения при выполнении команд pipenv shell / pipenv run. Остается лишь обратиться к ним из проекта любым удобным способом.
kostyaten
23.10.2019 14:05Вопрос, чем плохо иметь в /etc/environment
export KEY=«Ключ»
И в питоне получать os.environ.get('KEY') ???
AndreyPhys Автор
23.10.2019 14:19Хранить переменные окружения в
/etc/environment
плохо тем, что это требует наличия прав суперпользователя и при таком подходе трудно разделять переменные окружения для разных проектов.
Кроме того, иногда нужно задавать переменные окружения до запуска питона.
eternal_why
23.10.2019 20:50Мне единственному кажется странной идея использовать библиотеки для загрузки в окружение нежелательных к сохранению в файлы переменных из, опаньки, сохранённых файлов?.. :)
baldr
25.10.2019 17:30Да, действительно, наблюдается некоторая непоследовательность:
Хранить параметры в файле плохо, поэтому давайте будем хранить в переменных окружения, которые запишем в файл. И для этого нам еще нужны библиотеки сторонние!
Вдобавок, если нам потребуется, например, группировать переменные, то, простите — только плоская структура:
AWS_ACCESS_KEY/AWS_SECRET_KEY — это еще просто.
Как вы, например, зададите параметры подключения к базам для Django (settings.DATABASES)?
Я лично использую YAML-файлики, которые перезаписывают все дефолтные переменные. Файлы храню в /usr/local/etc — но это уже предпочтения.
Очень не рекомендую хранить в папке с проектом ничего локально-зависимого. Пример: для отладки я очень часто синхронизирую код в папке с проектом на удаленный сервер с помощью rsync (lsyncd). Если там лежит какой-нибудь local_settings.yaml для моей локальной системы — он тоже будет отправлен на сервер, хотя там будет нужен свой. Ну а сколько историй о том, как кто-то по ошибке закоммитил локальный конфиг…webdevium
25.10.2019 19:08+2Историй много таких, конечно. Потому и додумались, что в проекте есть только .env.example (любое другое имя), как набор возможных параметров и примеры их наполнения. А вот нужный "локальный" .env обязательно в .gitignore. Естественно, этот файл никогда не попадет на сервер, пока вы не начнете использовать для деплоя НЕ git.
Но даже для Вашего случая с rsync есть отличное решение:
rsync --filter=":- .gitignore" ...
gnomeby
Насколько я помню апач при запуске с запароленным сертификатом спрашивает пароль интерактивно прямо во время старта. То есть даже в некоторых местах переменным окружения не доверяют.