TL;DR
Чтобы просто установить и изолировать зависимости проекта, virtualenv часто оказывается слишком тяжелым решением. Предлагаем простую альтернативу:
- добавить
./.pip
в переменную окруженияPYTHONPATH
, - установить пакеты локально с помощью
pip install -t .pip
, - запускать
python
из папки проекта.
Какую версию желаете сегодня?
Установка зависимостей — обязательный шаг практически для любого приложения на Питоне. Каждое приложение зависит от разных библиотек, и чтобы гарантировать его правильное поведение, лучше устанавливать в точности правильные версии библиотек.
Обычная практика — включать в проект файл
requirements.txt
. В этом файле перечислены все библиотеки, от которых зависит проект, с номерами версий. Если файл на месте, установить зависимости просто:$ pip install -r requirements.txt
Пока все хорошо. Плохо станет, если у вас два и больше проектов с конфликтующими зависимостями. Допустим, проект A работает с библиотекой X только версии 0.1, а проект B — 0.2. По умолчанию pip устанавливает библиотеки глобально в папку интерпретатора Питона. Это означает, что команда
$ pip install X==0.2
установит библиотеку X версии 0.2 для всех проектов и перезапишет версию 0.1, если та уже установлена. Переключение между A и B означает глобальную переустановку X каждый раз, что долго и неудобно.Остров под солнцем
Популярное решение этой частой проблемы — виртуальные окружения. Фреймворк virtualenv создает изолированные Питон-окружения. Зависимости каждого проекта отделяются друг от друга. Так или иначе, некоторым пользователям virtualenv кажется слишком сложным. Поэтому существуют пакеты типа virtualenvwrapper и autoenv, которые расширяют функциональность virtualenv, чтобы им было проще пользоваться. Другие решения:
Anaconda environments
и pyvenv
из стандартной библиотеки Питона (3.3+).Все это отличные инструменты, но нам всегда казалось, что они предлагают довольно тяжелые и сложные решения простой, в общем-то, задачи.
В мире Яваскрипта npm и Bower предлагают простое, надежное и мощное управление пакетами, которого как будто не хватает в Питоне. Их ключ к успеху в том, что они скачивают правильную версию правильной библиотеки и кладут ее в специальную папку внутри проекта. Скачанные библиотеки относятся только к конкретному проекту. Это автоматически избавляет от проблем, о которых говорилось выше.
Чистая магия
Оказывается, есть простой способ повторить подход npm и Bower в Питоне:
- добавить
./.pip
вPYTHONPATH
, - использовать
pip
с параметром-t .pip
для локальной установки пакетов.
Затем просто запускаете код из папки проекта. Забудьте про
source env/bin/activate
и deactivate
!Фокус работает, потому что
./.pip
— относительный путь. В результате, когда запускаете python
из ~/dev/project_a
, папка ~/dev/project_a/.pip
включается в список папок библиотек для этого экземпляра Питона. Запускаете python
в ~/dev/project_b
— включается ~/dev/project_b/.pip
. Прием работает на всех популярных платформах: Линуксе, Маке и Виндоузе.Название папки
.pip
, конечно, может быть любым. Кому-то больше понравится pip_components
или libs
. Тем не менее, .pip
быстро печатать, а точка в начале делает папку скрытой в Линуксе и Маке.Шаг 1: Установить PYTHONPATH
- MAC/LINUX
Эта команда установит PYTHONPATH навсегда для стандартных терминальных сессий:
$ echo 'export PYTHONPATH="./.pip:$PYTHONPATH"' >> ~/.bash_profile
После этого перезапустите терминал или исполните$ source .bash_profile
, чтобы PYTHONPATH загрузилась в активной сессии. В зависимости от платформы вам, возможно, надо будет заменить~/.bash_profile
на~/.bashrc
.
- WINDOWS
Окройте Панель упраления и перейдите в System and Security > System > Change Settings > Advanced > Environment Variables (у меня английская версия, поэтому не перевожу названия разделов, чтобы случайно не запутать читателей. — прим. пер.). Добавьте или отредактируйте переменную PYTHONPATH, чтобы ее значение стало таким:.\.pip
или.\.pip;(...other paths...)
. Можно установить переменную как для пользователя, так и для всей системы.
- TEMPORARY PYTHONPATH
Если предпочитаете устанавливать PYTHONPATH только на время сессии, запустите$ export PYTHONPATH=./.pip
в Маке и Линуксе или> set PYTHONPATH=.\.pip
в Виндовсе.
В Маке и Линуксе можно даже установить PYTHONPATH только на время Питон-сессии:$ PYTHONPATH=./.pip python main.py
.
Шаг 2: Установить пакеты через pip -t
Теперь, когда PYTHONPATH установлена, осталось только установить пакеты в правильное место с помощью pip. Для этого используем ключ
-t
или --target
:$ cd project_a
project_a$ pip install requests==2.7.0 -t .pip
project_a$ python
>>> import requests
>>> requests.__version__
'2.7.0'
Теперь то же самое в другом проекте с другой версией:
$ cd project_b
project_b$ pip install requests==2.6.0 -t .pip
project_b$ python
>>> import requests
>>> requests.__version__
'2.6.0'
С файлом requirements.txt все работает так же:
$ pip install -r requirements.txt -t .pip
Потенциальные засады
Разные интерпретаторы Питона
Можно легко запустить программу другим интерпретатором Питона:
$ /path/to/python main.py
Тем не менее, есть проблема с переключением между Питоном 2 и 3, если используются пакеты, которые компилируют исходный код во время установки через
2to3
. У таких пакетов нет единой кодовой базы, при установке код генерируется соответственно активной версии Питона.В этом случае создайте папку типа
.pip3
и добавьте ее в начало PYTHONPATH, когда запускаете код Питоном 3.easy_install
Если у вас есть пакеты, установленные глобально через
easy_install
, столкнетесь с проблемой: easy_install дописывает путь к таким пакетам в начало sys.path
, поэтому у них приоритет над пакетами из .pip
.Решение — избавиться от пакетов, установленных глобально через easy_install.
Чтобы проверить, что ничто не мешает использовать .pip, запустите
import sys;sys.path
в Питоне. Если перед ./.pip
будут другие пути, возможно, придется сначала почистить систему от глобальных easy_install-пакетов.
Комментарии (12)
ZyXI
28.06.2015 01:51+1Вот не нужно так делать:
export PYTHONPATH="./.pip:$PYTHONPATH"
. Данное действие, скорее всего, добавит текущий каталог в PYTHONPATH, но, возможно и нет. Зависит от того, пуста ли переменная PYTHONPATH. Если вам нужно добавить что?то в переменные типа *PATH, то используйте${VARNAME:+:}
вместо двоеточия, чтобы не получить в результате пустой элемент, который часто имеет значение вроде «текущий каталог» или «каталог(и) по?умолчанию».
KhodeN
28.06.2015 01:55+1Мне хватает pyenv c штатным плагином virtualenv.
Позволяет хранить на машине сколько угодно версий python, встраивается в командный шелл, автоматически включает окружение и переходе в папку и выключает при выходе из неё.
Сpip -t
одна сложность — надо не забывать писать параметр. Лучше сделать алиас уже с параметром.
gbezyuk
28.06.2015 06:47+4>> Чтобы просто установить и изолировать зависимости проекта, virtualenv часто оказывается слишком тяжелым решением.
серьёзно? можно реальный пример?moigagoo Автор
28.06.2015 06:58-7«Тяжесть» решения — субъективная величина. Мне virtualenv через несколько лет использования действительно стал казаться слишком тяжелым для моих задач. Неохота стало делать activate и deactivate. Virtualenvwrapper мне не нравится, да и в Павершелле не работает, а я сейчас почти всегда в нем. Я перестал им пользоваться этими инструментами и начал ставить пакеты глобально. Да, это плохая практика, но мне не мешало. В моем мире virtualenv — слишком тяжелый инструмент.
Теперь, когда узнал о таком простом приеме, начал использовать его — и не пожалел.
Если в вашем мире нет примеров, когда virtualenv — это слишком тяжело, пользуйтесь virtualenv ради бога. Никто ж не запрещает. Значит, в вашем мире это идеальный инструмент.
MrFrizzy
28.06.2015 10:59+1Еще такую штуку рекомендую: nix
kmmbvnr
28.06.2015 12:44+1ээ, и оно с питон пакетами как-то дружит?
MrFrizzy
28.06.2015 18:42Присоединяюсь к предыдущему комментарию.
По теме: вот ссылки с первой страницы гугла по запросу «nix packet manager python»:
Getting started with Nix package manager
RhodeCode and Nix Package Manager
Nixos Container Example: Python Web Developmentkmmbvnr
29.06.2015 03:39>> The following environment can be used in a nixos-container for using traditional python package managers (pip, buildout etc.) in an impure way within a container
Т.е. тот же virtual env, те же не атомарные апдейты, только надо еще затратить усилия, чтоб заставить это работать. Но зачем?
aikikode
28.06.2015 21:08+1Интересное решение, но насколько я понимаю, в отличие от того же virtualenv, оно будет работать только в корне проекта.
rhamdeew
Довольно элегантное решение. В Node.js оно радует. Посмотрим обрадует ли в этом случае)