Python

индекс
250,37

Хабраподсветка или эксперименты в изолированном окружении Python

Протестировать свежую версию любимого фреймоврка. Запустить приложение со специфичным набором библиотек. Установить необходимые библиотеки по списку зависимостей. Как решить все эти задачи не затронув системные файлы? В этой статье речь пойдет об утилитах virutalenv и pip.

Утилита vitrualenv предназначена для создания изолированных окружений Python. Под окружением понимается собственно интерпретатор и набор библиотек к нему. Утилита pip органично дополняет easy_install, делая установку пакетов еще проще. Подробную информацию о всех полезностях, которые virutalenv и pip дают разработчику, вы сможете узнать, посетив странички pypi.python.org/pypi/virtualenv и pypi.python.org/pypi/pip. Я же покажу на примере как все это можно использовать для экспериментов с проектами на языке Python.

Суть данного проекта предельно проста. Эта небольшая консольная программулька с именем hl.py будет раскрашивать исходный код скриптов для вставки в хабратопик. Через командую строку она принимает путь к файлу с исходником и язык, а в ответ печатает нужный html код. Например, вот так она раскрашивает сама себя:
$ ./ht.py ./hl.py python
Traceback (most recent call last):
  File "./hl.py", line 3, in <module>
    pkg_resources.require('lxml==2.2.2')
  File "/usr/lib/python2.6/dist-packages/pkg_resources.py", line 626, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/lib/python2.6/dist-packages/pkg_resources.py", line 524, in resolve
    raise DistributionNotFound(req)  # XXX put more info here
pkg_resources.DistributionNotFound: lxml==2.2.2

Упс, не работает. Сообщение говорит о том, что не установлен дистрибутив lxml версии 2.2.2. Посмотрим, что есть в Ubuntu:
$ apt-cache show python-lxml | grep -E ^Version
Version: 2.1.5-1ubuntu2

Действительно есть что-то похожее, но, определенно, не той версии которая требует эта программулька. А как узнать, что такое lxml? Воспользуемся утилитой yolk (http://pypi.python.org/pypi/yolk).
$ yolk -f summary -M lxml
bash: yolk: команда не найдена
$ apt-cache search yolk
$

Почему разработчики Ubuntu не сделали пакет для такой полезной утилитки?…

Однако, это не проблема. У нас есть возможность сделать собственное окружение Python, не зависящее от причуд создателей дистрибутива операционной системы. Сохраните на диск следующий скрипт под именем vipsetup и сделайте его исполняемым:
#!/bin/sh
# Setup virtual environment for python and pip
# Usage:
#    vipsetup [directory]

ENV_DIR="$1"
VIRTUALENV_BIN='virtualenv'
VIRTUALENV_ARGS='--no-site-packages'

if [ ! -f $ENV_DIR/bin/activate ]; then
    if ! type "$VIRTUALENV_BIN"; then
        if ! type "easy_install"; then
            echo "Error: easy_install executable not found."
            echo "To install easy_install type:"
            echo "  sudo apt-get install python-setuptools"
            exit 1
        fi
        TMPDIR=`mktemp -d -t ve.XXXXXXXXXX` || exit 1
        PYTHONPATH=$TMPDIR easy_install --install-dir=$TMPDIR virtualenv || exit 1
        PYTHONPATH=$TMPDIR $TMPDIR/virtualenv "$@" $VIRTUALENV_ARGS || exit 1
        rm -rf $TMPDIR
    else
        $VIRTUALENV_BIN "$@" $VIRTUALENV_ARGS || exit 1
    fi
fi

if [ ! -e $ENV_DIR/bin/pip ]; then
    $ENV_DIR/bin/easy_install pip || exit 1
fi

echo ""
echo "Installation complete" 
echo "* To activate virtual environment type:"
echo "  . $ENV_DIR/bin/activate"
echo "* To install python package type:"
echo "  $ENV_DIR/bin/pip install <package>"

if [ -z "$PIP_DOWNLOAD_CACHE" ]; then
    echo "* To enable cache for downloads"
    echo "  set environment variable PIP_DOWNLOAD_CACHE with a path"
fi

Создайте новое изолированное окружение, при помощи команд:
$ mkdir -p /tmp/testenv
$ cd /tmp/testenv
$ vipsetup .
$ . /bin/activate
(testenv) $

А теперь установите yolk, используя команду pip:
(testenv) $ pip install yolk
...
(testenv) $ yolk -f summary -M lxml
summary: Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API.

Вот и описание. Установите lxml требуемой версии:
(testenv) $ pip install lxml==2.2.2

Теперь, все готово для запуска нашей программульки:
(testenv) $ ./hl.py ./hl.py python
#!/usr/bin/env python
import pkg_resources
pkg_resources.require('lxml==2.2.2')
 
from lxml.html import parse, submit_form, tostring
 
def highlight(code, language):
    SERVICE = 'http://highlight.hohli.com/'
    doc = parse(SERVICE)
    form = doc.getroot().forms[0 ]
    form.fields['language'] = language
    form.fields['use_font'] = 1 # for habrahabr
    form.fields['code'] = code
    form.action = form.action or form.base_url # hack
    ans = parse(submit_form(form))
    highlited = ans.getroot().cssselect('.source .src')[0 ]
    return ''.join([tostring(x) for x in highlited]
            ).replace('0</font>''0 </font>' # fix habrahabr 
            ).replace('blockquote>''code>') # fix format 
 
if __name__ == "__main__":
    import sys
    def main(*args, **kwargs):
        fh = sys.stdin if args[0 ] == '-' else file(args[0 ])
        language = args[1]
        code = unicode(fh.read())
        print highlight(code, language)
        return 0
    sys.exit(main(*sys.argv[1:]))
 

Вот она, вся такая цветная. Наслаждайтесь. :) И запишите где-нибудь список зависимостей, на память:
(testenv) $ pip freeze | tee requirements.txt
lxml==2.2.2
wsgiref==0.1.2
yolk==0.4.1

Если потребуется, быстренько восстановите при помощи команды:
(testenv) $ pip install -r requirements.txt


зы: если практическая полезность данного эксперимента вам показалось сомнительной, сделайте:
(testenv) $ deactivate
$ rm -rf /tmp/testenv
и будем считать, что ничего не было. ;)
+26
22 сентября 2009, 23:50
30

комментарии (11)

+2
kmike #
хорошо написали)
0
iCrowley #
Мне кажется, что появление Virtual Env, Pip, yolk вызвано в большой степени тем, что штатный питоновский Easy Install — сильно ограниченная вещь. Питону не хватает нормального менеджера пакетов, такого, как, например, Gem в Ruby, в котором можно и указать нужную версию библиотеки для использования, и обновить все библиотеки одной командой.
0
funca #
Ситуация с Gem тоже не однозначна, поскольку пакетный менеджер, имеющий собственные представления о зависимостях и путях инсталляции, неизбежно вступает в конфликт с пакетными менеджерами операционных систем (тем же apt). В последнее время появилось много аналогичной критики и в адрес setuptools, пытающихся вобрать в себя функционал инсталятора, пакетного менеджера и менеджера зависимостей. Плюс setuptools долгое время находится практически в заброшенном состоянии. Сейчас активно ведется переработка pypi.python.org/pypi/distribute (соответствующий фрок virtualenv pypi.python.org/pypi/virtualenv-distribute), но до завершения еще долго. Virutalenv, pip и yolk это классический unixway подхода к решению проблемы.
0
barbuza #
подсветку кода на питоне удобнее сделать с помощью pygments. а в requirements.txt запихнуть lxml который зависит от нескольких сишных библиотек — совсем не правильно.
+1
funca #
хабра требует специфичное html форматирование с использованием тегов <font>, а pygments генерирует <style class=«el» >. в общем, сходу не разобрался. да и задача была другая. познакомившись с virtualenv и pip я потратил определенное время, в поисках удобного решения для быстрого создания изолированных окружений и кеширования загружаемых исходников. в итоге появился этот скрипт на shell.

что касается lxml. перепробовал несколько библиотек. wwwsearch.sourceforge.net/mechanize/ на использованном сервисе не распознает инпут «use_font», соответственно глючат и другие библиотеки, использующие mechanize (http://pypi.python.org/pypi/zc.testbrowser, например). у pypi.python.org/pypi/BeautifulSoup/ вроде отдельный парсер, но вылезла та же самая проблема. lxml.html оказалось первым, кто смог правильно распарсить страницу.
0
barbuza #
mechanize не парсер, а браузер. он работает поверх BeautifulSoup. я не говорил что lxml плохой, я сказал что пихать его в список установки для pip не самамя лучшая идея. кроме BeautifulSoup есть html5lib и elementtidy. скорее всего они справятся.
0
funca #
пихать его в список установки для pip не самамя лучшая идея

почему?
+1
barbuza #
такой файл делают обычно для того, чтобы потом написать

./venv/bin/pip install -E venv -r ./build/pipreq.txt

и получить готовое к работе окружение, со всеми зависимостями для проекта. но есть библиотеки — mysqldb, psycopg2, PIL, lxml — которые для сборки требуют либо дополнительных библиотек, либо правильно настроенного окружения, либо правки исходников. поэтому обычно эти библиотеки ставятся не в virtualenv, а system-wide.
0
apatrushev #
добавлю только что обычно в system-wide ставят пакеты, которые требуют gcc для установки из pypi, т.к. gcc есть не всегда.
0
funca #
По прежнему не понимаю. Но теперь уже две вещи. :)
1. Зачем указывать ключ -E venv? Мне попадалось много статей, где ключ указан. Но показалось, если пакет устанавливается в то же самое виртуальное окружение, где находится и pip, то ключ не обязателен. Необходимые настройки подключаются благодаря кастомному интерпретатору ./bin/python, который размещен там же.
2. Нужно было установить lxml system-wide от root, создав *.deb пакет, но зачем? Я считаю, что это сложнее и вносит неразбериху в apt. Поэтому для экспериментов использую виртуальные окружения. У меня еще не было проблем ни с lxml, ни с mysqldb, установленными таким способом. Где ждать неприятностей?
0
barbuza #
по первому пункту — да, ключ необязательный.
по второму — не везде debian и не у всех установлены libxml2 и libxslt

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