Питон в коробке – venv в python 3.3

в 17:37, , рубрики: deployment, deployment tools, python, python 3.3, python3, метки: , , ,

Наверняка, большинство из тех, кто разрабатывает или деплоит Python приложения, использует виртуальные окружения. В частности через virtualenv, написанный Ian Bicking.

Идея оказалась так хороша и распостранена, что нечно похожее теперь присутствует в Python 3.3 из коробки в виде модуля venv. Он почти такой же, как virtualenv, только немного лучше.

Как это работает?

Основное отличие venv в том, что он встроен в интерпретатор и может отрабатывать ещё до загрузки системных модулей. Для этого, при определении базовой директории с библиотеками, используется примерно такой алгоритм:

  • в директории с интерпретатором или уровнем выше ищется файл с именем pyvenv.cfg;
  • если файл найден, в нём ищется ключ home, значение которого и будет базовой директорией;
  • в базовой директории идёт поиск системной библиотеки (по спец. маркеру os.py);
  • если что-то пошло не так – всё откатывается к захардкоженному в бинарнике значению.

Вот и вся суть venv, всё остальное уже обёртка над этим.

Как создать?

Всё очень просто, нужно вызвать через ключ -m модуль venv, либо использовать встроенный скрипт pyvenv:

pyenv /path/to/new/venv

Скрипт создаст указанную директорию, вместе со всеми родительскими директориями, если потребуется, и построит виртуальное окружение. Это можно делать и в Windows, только вызов будет чуть более многословным:

c:Python33python -m venv /path/to/new/venv

При создании можно добавлять различные параметры, как, например, включение системных site-packages или использованию symlink вместо копирования интерпретатора.

В отличии от virtualenv новый venv требует чтобы создаваемая директория не существовала, либо была пустой. Вероятно, это сделано, что не допускать конфликтов с существующими файлами.

Как использовать?

До этого момента особых отличий с virtualenv не было, но при использовании собранного окружения всё становится куда веселей.

Можно использовать старый добрый метод активации через bin/activate (Scripts/activate в windows):

cd /path/to/new/venv
. bin/activate
python3 some_script.py

А можно и не использовать, достаточно лишь вызвать интерпретатор из окружения и всё сработает автоматически:

/path/to/new/venv/bin/python3 some_script.py

Это конечно не сработает для скриптов, запускаемых напрямую через #!/usr/bin/env python3, для них всё равно нужно будет, как и раньше, делать активацию. Решение есть – о нём чуть ниже.

Обновление

Если в вашей системе обновилась версия python, то виртуальное окружение иногда тоже нужно обновить.
Всё просто – вызываем venv аналогично созданию окружения, добавив ключ --upgrade:

pyenv --upgrade /path/to/new/venv

Это произойдёт автоматически, если использовать symlink, но если вы хотите кроме изоляции делать фиксацию версии python и библиотек, я бы рекомендовал делать обновление вручную.

Расширение EnvBuilder

Вся работа по созданию окружения падает на класс venv.EnvBuilder, этот класс написан так, чтобы его можно было расширять.

Например, можно при инициализации окружения ставить туда distribute, pip и необходимые начальные зависимости из requirements.txt. Наверное, более сложную логику лучше оставить на совести более предназначенных для этого инструментов, типа buildout или make, но первоначальную настройку можно провести и на уровне EnvBuilder, затем уже передав из него управление нужным скриптам.

При создании окружения используется метод create(self, env_dir), в исходном классе он выглядит так:

    def create(self, env_dir):
        env_dir = os.path.abspath(env_dir)
        context = self.ensure_directories(env_dir)
        self.create_configuration(context)
        self.setup_python(context)
        if not self.upgrade:
            self.setup_scripts(context)
            self.post_setup(context)

Метод описывает суть всего процесса: создание директории (ensure_directories), конфигурации (create_configuration), добавлнию бинарников питона (setup_python) и добавлению скриптов активации (setup_scripts).

В конце вызывается хук post_setup, в который вы можете добавлять свои действия. Видно, что post_setup выполняется только при создании окружения, а при --upgrade он выполняться не будет. Это легко исправить, добавив ещё один хук:

class ImprovedEnvBuilder(venv.EnvBuilder):
    
    def create(self, env_dir):
        """Overwrite create method (add more hooks)"""
        env_dir = path.abspath(env_dir)
        context = self.ensure_directories(env_dir)
        self.create_configuration(context)
        self.setup_python(context)
        if not self.upgrade:
            self.setup_scripts(context)
            self.post_setup(context)
        else:
            self.post_upgrade(context)

    def post_upgrade(self, context):
        pass

В качестве параметров при вызове методов после ensure_directories будет передаваться context — объект, содержащий в виде атрибутов всю необходимую информацию о создаваемом окружении. Почему-то в документации пока эти ключи не описаны, но вы легко сможете понять всё самостоятельно, заглянув в код метода ensure_directories в базовом классе. Приведу самые полезные из атрибутов:

  • context.bin_path — путь к директории с бинарниками и исполняемыми скриптами,
  • context.env_dir — путь к директории с созданным окружением,
  • context.env_exe — путь к бинарнику внтури окружения.

Соответственно, для запуска python скрипта внутри окружения, можно сделать:

import subprocess
import venv
class MyEnvBuilder(venv.EnvBuilder):
    def post_setup(self, context):
        script = '/path/to/some_script.py'
        subprocess.call([context.env_exe, script])

Исполняемые скрипты внутри venv

Вернёмся к проблеме с исполняемыми скриптами внутри виртуального окружения.

В virtualenv для них достаточно было указать интерпретатор через #/usr/bin/env python3 и использовать, не забывая сделать . bin/activate. Если вас такой подход устраивал, то вы можете им продолжать пользоваться и в venv.

Есть и новый путь. Внутри EnvBuilder реализован метод install_scripts(self, context, path), который автоматизирует копирование скриптов и бинарников в создаваемое окружение. В path необходимо передать путь к директории с вложенными поддиректориями «common», «nt», «posix» и т.д. В поддиректории, в свою очередь, положить необходимые скрипты или бинарники. В «common» скрппты для всех платформ, в «nt» – для Windows, «posix» – для Linux, Mac OS X и других posix систем.

Кроме того, для текстовых файлов выполняется постановка значений. Поддерживаемые из коробки:

  • __VENV_DIR__
  • __VENV_NAME__
  • __VENV_BIN_NAME__
  • __VENV_PYTHON__

Пример шаблона запускаемого python скрипта:

#!__VENV_PYTHON__

import sys
import my_module

if __name__ == '__main__':
    sys.exit(my_module.run(sys.argv))

__VENV_PYTHON__ будет заменено на полный путь к интерпретатору python в виртуальном окружении.

После установки такого скрипта через install_scripts, его можно будет запускать, без необходимости активации окружения через bin/activate.

...

Автор: Nikita

Источник

Поделиться

* - обязательные к заполнению поля