python

«pip -t» — простая альтернатива virtualenv

  • воскресенье, 28 июня 2015 г. в 02:11:05
http://habrahabr.ru/post/261263/

TL;DR


Чтобы просто установить и изолировать зависимости проекта, virtualenv часто оказывается слишком тяжелым решением. Предлагаем простую альтернативу:
  1. добавить ./.pip в переменную окружения PYTHONPATH,
  2. установить пакеты локально с помощью pip install -t .pip,
  3. запускать 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-пакетов.