22 September 2009

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

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<br>import pkg_resources<br>
pkg_resources.require('lxml==2.2.2')<br>
 <br>from lxml.html import parse, submit_form, tostring<br>
 <br>def highlight(code, language):<br>
    SERVICE = 'http://highlight.hohli.com/'<br>
    doc = parse(SERVICE)<br>
    form = doc.getroot().forms[0 ]<br>
    form.fields['language'] = language<br>
    form.fields['use_font'] = 1 # for habrahabr<br>
    form.fields['code'] = code<br>
    form.action = form.action or form.base_url # hack<br>
    ans = parse(submit_form(form))<br>
    highlited = ans.getroot().cssselect('.source .src')[0 ]<br>
    return ''.join([tostring(x) for x in highlited]<br>
            ).replace('0</font>''0 </font>' # fix habrahabr <br>
            ).replace('blockquote>''code>') # fix format <br>
 <br>if __name__ == "__main__":<br>
    import sys<br>
    def main(*args, **kwargs):<br>
        fh = sys.stdin if args[0 ] == '-' else file(args[0 ])<br>
        language = args[1]<br>
        code = unicode(fh.read())<br>
        print highlight(code, language)<br>
        return 0 <br>
    sys.exit(main(*sys.argv[1:]))<br>
 

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

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


зы: если практическая полезность данного эксперимента вам показалось сомнительной, сделайте:
(testenv) $ deactivate
$ rm -rf /tmp/testenv
и будем считать, что ничего не было. ;)
Tags:pythonvirtualenvpip
Hubs: Python
+26
4.5k 40
Comments 11
Top of the last 24 hours