Хабраподсветка или эксперименты в изолированном окружении 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
    и будем считать, что ничего не было. ;)
    Поделиться публикацией

    Похожие публикации

    Комментарии 11

      +2
      хорошо написали)
        0
        Мне кажется, что появление Virtual Env, Pip, yolk вызвано в большой степени тем, что штатный питоновский Easy Install — сильно ограниченная вещь. Питону не хватает нормального менеджера пакетов, такого, как, например, Gem в Ruby, в котором можно и указать нужную версию библиотеки для использования, и обновить все библиотеки одной командой.
          0
          Ситуация с Gem тоже не однозначна, поскольку пакетный менеджер, имеющий собственные представления о зависимостях и путях инсталляции, неизбежно вступает в конфликт с пакетными менеджерами операционных систем (тем же apt). В последнее время появилось много аналогичной критики и в адрес setuptools, пытающихся вобрать в себя функционал инсталятора, пакетного менеджера и менеджера зависимостей. Плюс setuptools долгое время находится практически в заброшенном состоянии. Сейчас активно ведется переработка pypi.python.org/pypi/distribute (соответствующий фрок virtualenv pypi.python.org/pypi/virtualenv-distribute), но до завершения еще долго. Virutalenv, pip и yolk это классический unixway подхода к решению проблемы.
          0
          подсветку кода на питоне удобнее сделать с помощью pygments. а в requirements.txt запихнуть lxml который зависит от нескольких сишных библиотек — совсем не правильно.
            +1
            хабра требует специфичное 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
              mechanize не парсер, а браузер. он работает поверх BeautifulSoup. я не говорил что lxml плохой, я сказал что пихать его в список установки для pip не самамя лучшая идея. кроме BeautifulSoup есть html5lib и elementtidy. скорее всего они справятся.
                0
                пихать его в список установки для pip не самамя лучшая идея

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

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

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

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

            Самое читаемое