zipapp — Управление исполняемыми zip-архивами Python

Added in version 3.5.

Источник: Lib/zipapp.py


Этот модуль предоставляет инструменты для управления созданием zip-файлов, содержащих код Python, который может быть executed directly by the Python interpreter. Модуль предоставляет как Интерфейс командной строки, так и API Python.

Базовый пример

В следующем примере показано, как с помощью Интерфейс командной строки можно создать исполняемый архив из каталога, содержащего код Python. При запуске архив выполнит функцию main из модуля myapp в архиве.

$ python -m zipapp myapp -m "myapp:main"
$ python myapp.pyz
<output from myapp>

Интерфейс командной строки

При вызове программы из командной строки используется следующая форма:

$ python -m zipapp source [options]

Если source - это каталог, то будет создан архив из содержимого source. Если source - файл, он должен быть архивом, и он будет скопирован в целевой архив (или будет показано содержимое его shebang-строки, если указана опция –info).

Возможны следующие варианты:

-o <output>, --output=<output>

Запись выходных данных в файл с именем output. Если эта опция не указана, имя выходного файла будет таким же, как и входного source, с добавлением расширения .pyz. Если задано явное имя файла, оно используется как есть (поэтому при необходимости следует включить расширение .pyz).

Имя выходного файла должно быть указано, если источник является архивом (и в этом случае output не должно быть таким же, как source).

-p <interpreter>, --python=<interpreter>

Добавьте в архив строку #!, в которой укажите interpreter в качестве команды для запуска. Кроме того, на POSIX сделайте архив исполняемым. По умолчанию строка #! отсутствует, и файл не является исполняемым.

-m <mainfn>, --main=<mainfn>

Записать в архив файл __main__.py, выполняющий команду mainfn. Аргумент mainfn должен иметь вид «pkg.mod:fn», где «pkg.mod» - пакет/модуль в архиве, а «fn» - вызываемый модуль в данном модуле. Файл __main__.py выполнит этот вызываемый модуль.

При копировании архива нельзя указывать --main.

-c, --compress

Сжимайте файлы методом deflate, уменьшая размер выходного файла. По умолчанию файлы хранятся в архиве в несжатом виде.

--compress не действует при копировании архива.

Added in version 3.7.

--info

Отображение интерпретатора, встроенного в архив, в диагностических целях. В этом случае все остальные опции игнорируются, а SOURCE должен быть архивом, а не каталогом.

-h, --help

Выведите короткое сообщение об использовании и выйдите.

API Python

В модуле определены две удобные функции:

zipapp.create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)

Создайте архив приложения из источника. Источником может быть любой из следующих файлов:

  • Имя каталога или path-like object, ссылающийся на каталог, в этом случае новый архив приложения будет создан из содержимого этого каталога.

  • Имя существующего архивного файла приложения или path-like object, ссылающийся на такой файл, в этом случае файл копируется в целевой файл (изменяя его в соответствии со значением, заданным для аргумента интерпретатор). Имя файла должно включать расширение .pyz, если требуется.

  • Файловый объект, открытый для чтения в режиме байтов. Содержимое файла должно представлять собой архив приложения, и предполагается, что файловый объект расположен в начале архива.

Аргумент target определяет, куда будет записан результирующий архив:

  • Если это имя файла или path-like object, то архив будет записан в этот файл.

  • Если это открытый файловый объект, то архив будет записан в этот файловый объект, который должен быть открыт для записи в режиме байтов.

  • Если значение target опущено (или None), источником должен быть каталог, а целью будет файл с тем же именем, что и источник, с добавлением расширения .pyz.

Аргумент interpreter задает имя интерпретатора Python, с которым будет выполняться архив. Оно записывается в виде строки «shebang» в начале архива. На POSIX она будет интерпретирована ОС, а на Windows ее обработает программа запуска Python. Если опустить интерпретатор, то строка shebang не будет записана. Если указан интерпретатор, а целью является имя файла, будет установлен исполняемый бит целевого файла.

Аргумент main задает имя вызываемого файла, который будет использоваться в качестве основной программы архива. Он может быть указан только в том случае, если источником является каталог, и источник еще не содержит __main__.py файла. Аргумент main должен иметь вид «pkg.module:callable», и архив будет запущен путем импорта «pkg.module» и выполнения указанного callable без аргументов. Ошибкой будет опустить main, если источник является каталогом и не содержит __main__.py файла, так как в противном случае полученный архив не будет исполняемым.

Необязательный аргумент filter задает функцию обратного вызова, которой передается объект Path, представляющий путь к добавляемому файлу (относительно исходного каталога). Она должна возвращать True, если файл должен быть добавлен.

Необязательный аргумент compressed определяет, будут ли файлы сжаты. Если установлено значение True, файлы в архиве сжимаются методом deflate; в противном случае файлы хранятся без сжатия. Этот аргумент не влияет на копирование существующего архива.

Если для source или target указан файловый объект, то после вызова create_archive вызывающая сторона обязана закрыть его.

При копировании существующего архива файловым объектам передаются только методы read и readline, либо write. При создании архива из каталога, если целью является файловый объект, он будет передан классу zipfile.ZipFile и должен предоставить методы, необходимые этому классу.

Изменено в версии 3.7: Добавлены параметры filter и compressed.

zipapp.get_interpreter(archive)

Возвращает интерпретатор, указанный в строке #! в начале архива. Если строка #! отсутствует, возвращается None. Аргумент archive может быть именем файла или файлоподобного объекта, открытого для чтения в режиме байтов. Предполагается, что он находится в начале архива.

Примеры

Упакуйте каталог в архив и запустите его.

$ python -m zipapp myapp
$ python myapp.pyz
<output from myapp>

То же самое можно сделать с помощью функции create_archive():

>>> import zipapp
>>> zipapp.create_archive('myapp', 'myapp.pyz')

Чтобы сделать приложение непосредственно исполняемым на POSIX, укажите используемый интерпретатор.

$ python -m zipapp myapp -p "/usr/bin/env python"
$ ./myapp.pyz
<output from myapp>

Чтобы заменить строку shebang в существующем архиве, создайте измененный архив с помощью функции create_archive():

>>> import zipapp
>>> zipapp.create_archive('old_archive.pyz', 'new_archive.pyz', '/usr/bin/python3')

Чтобы обновить файл на месте, выполните замену в памяти с помощью объекта BytesIO, а затем перезапишите источник. Обратите внимание, что при перезаписи файла на месте существует риск того, что ошибка приведет к потере исходного файла. Этот код не защищает от таких ошибок, но в производственном коде это должно быть сделано. Кроме того, этот метод будет работать только в том случае, если архив помещается в памяти:

>>> import zipapp
>>> import io
>>> temp = io.BytesIO()
>>> zipapp.create_archive('myapp.pyz', temp, '/usr/bin/python2')
>>> with open('myapp.pyz', 'wb') as f:
>>>     f.write(temp.getvalue())

Определение интерпретатора

Обратите внимание, что если вы указываете интерпретатор, а затем распространяете архив приложения, вам нужно убедиться, что используемый интерпретатор переносимый. Программа запуска Python для Windows поддерживает большинство распространенных форм строки POSIX #!, но есть и другие вопросы, которые необходимо учитывать:

  • Если вы используете команду «/usr/bin/env python» (или другие формы команды «python», например «/usr/bin/python»), вам нужно учитывать, что у ваших пользователей по умолчанию может быть либо Python 2, либо Python 3, и писать свой код для работы под обеими версиями.

  • Если вы используете явную версию, например «/usr/bin/env python3», ваше приложение не будет работать для пользователей, у которых нет этой версии. (Это может быть то, что вам нужно, если вы не сделали свой код совместимым с Python 2).

  • Нет возможности сказать «python X.Y или более поздней версии», поэтому будьте осторожны с использованием точной версии, например «/usr/bin/env python3.4», так как вам придется изменить строку shebang для пользователей Python 3.5, например.

Обычно используется «/usr/bin/env python2» или «/usr/bin/env python3», в зависимости от того, для какого языка написан ваш код - Python 2 или 3.

Создание автономных приложений с помощью zipapp

С помощью модуля zipapp можно создавать самодостаточные программы на Python, которые можно распространять среди конечных пользователей, которым достаточно установить в своей системе подходящую версию Python. Ключевым моментом в этом случае является упаковка всех зависимостей приложения в архив вместе с его кодом.

Чтобы создать автономный архив, выполните следующие действия:

  1. Создайте свое приложение в каталоге, как обычно, чтобы у вас был каталог myapp, содержащий файл __main__.py и весь вспомогательный код приложения.

  2. Установите все зависимости вашего приложения в каталог myapp, используя pip:

    $ python -m pip install -r requirements.txt --target myapp
    

    (это предполагает, что требования к проекту содержатся в файле requirements.txt - если это не так, вы можете просто перечислить зависимости вручную в командной строке pip).

  3. Упакуйте приложение с помощью:

    $ python -m zipapp -p "interpreter" myapp
    

В результате получится отдельный исполняемый файл, который можно запустить на любой машине, где есть соответствующий интерпретатор. Подробности см. в разделе Определение интерпретатора. Он может быть отправлен пользователям в виде одного файла.

На Unix файл myapp.pyz является исполняемым в исходном виде. Вы можете переименовать файл, удалив расширение .pyz, если предпочитаете «простое» имя команды. В Windows файл myapp.pyz[w] является исполняемым благодаря тому, что интерпретатор Python при установке регистрирует расширения файлов .pyz и .pyzw.

Оговорки

Если ваше приложение зависит от пакета, включающего расширение C, этот пакет не может быть запущен из zip-файла (это ограничение ОС, поскольку исполняемый код должен присутствовать в файловой системе, чтобы загрузчик ОС мог его загрузить). В этом случае вы можете исключить эту зависимость из zip-файла и либо потребовать от пользователей ее установки, либо отправить ее вместе с zip-файлом и добавить в __main__.py код для включения каталога с распакованным модулем в sys.path. В этом случае вам нужно будет убедиться, что вы поставляете соответствующие двоичные файлы для вашей целевой архитектуры (и, возможно, выбрать правильную версию для добавления в sys.path во время выполнения, основываясь на машине пользователя).

Формат архива приложения Python Zip

Начиная с версии 2.6, Python может выполнять zip-файлы, содержащие __main__.py файл. Для того чтобы выполняться Python, архив приложения должен быть просто стандартным zip-файлом, содержащим __main__.py файл, который будет запущен как точка входа для приложения. Как обычно для любого скрипта Python, родитель скрипта (в данном случае zip-файл) будет помещен в sys.path, и таким образом дальнейшие модули могут быть импортированы из zip-файла.

Формат zip-файлов позволяет добавлять в zip-файл произвольные данные. Формат приложения zip использует эту возможность для добавления в файл стандартной строки POSIX «shebang» (#!/path/to/interpreter).

Формально формат приложения Python zip выглядит так:

  1. Необязательная строка shebang, содержащая символы b'#!', за которыми следует имя интерпретатора, а затем символ новой строки (b'\n'). Имя интерпретатора может быть любым, приемлемым для обработки «shebang» в ОС или для запуска Python в Windows. Интерпретатор должен быть закодирован в UTF-8 в Windows и в sys.getfilesystemencoding() в POSIX.

  2. Стандартные данные zip-файла, сгенерированные модулем zipfile. Содержимое zip-файла должно включать файл с именем __main__.py (который должен находиться в «корне» zip-файла - т.е. не может быть в подкаталоге). Данные zip-файла могут быть сжатыми или несжатыми.

Если архив приложения содержит строку shebang, то в POSIX-системах для него может быть установлен бит исполняемости, позволяющий выполнять его напрямую.

Нет никаких требований, чтобы инструменты из этого модуля использовались для создания архивов приложений - модуль является удобным, но архивы в указанном выше формате, созданные любыми средствами, приемлемы для Python.