Spec-Zone.ru › Python 3.7

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

В версии 3.5.

Исходный код: Lib/zipapp.py

Этот модуль предоставляет инструменты для управления созданием zip-архивов, содержащих Python-код, который можно исполнять непосредственно интерпретатором Python. Модуль предоставляет как командную строку, так и Python-API.

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

Следующий пример демонстрирует, как можно использовать командную строку для создания исполняемого архива из каталога, содержащего 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).

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

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

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

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

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

--main не может быть указан при копировании архива.

-c, --compress

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

--compress не имеет эффекта при копировании архива.

В версии 3.7.

--info

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

-h, --help

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

Python-API

Модуль определяет две удобные функции:

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

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

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

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

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

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

Аргумент main задает имя вызываемого объекта, который будет использоваться в качестве основной программы для архива. Он может быть указан только в том случае, если источник — каталог, и источник еще не содержит файл __main__.py. Аргумент main должен иметь вид «pkg.module:callable», и архив будет запущен путем импорта «pkg.module» и выполнения заданного вызываемого объекта без аргументов. Опускание 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)

Возвращает интерпретатор, указанный в строке shebang в начале архива. Если строки shebang нет, возвращает 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. Необязательно, удалите каталоги .dist-info созданные pip в каталоге myapp. Они содержат метаданные для управления пакетами pip, и так как вы не будете использовать pip дальше, они не требуются — хотя это не повредит, если вы их оставите.
  4. Упакуйте приложение с помощью:

    $ python -m zipapp -p "interpreter" myapp
    

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

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

Создание исполняемого файла для Windows

В Windows регистрация расширения .pyz необязательна, и, более того, есть места, которые не распознают зарегистрированные расширения «прозрачно» (простейший пример — subprocess.run(['myapp']) не найдёт ваше приложение — вам нужно явно указать расширение).

Поэтому в Windows часто предпочтительнее создавать исполняемый файл из zipapp. Это относительно легко, хотя для этого требуется компилятор C. Основной подход основан на том, что zip-файлы могут иметь произвольные данные в начале, а файлы exe Windows — произвольные данные в конце. Таким образом, создавая подходящий загрузчик и добавляя файл .pyz в конец, вы получаете исполняемый файл, который запускает ваше приложение.

Подходящий загрузчик может быть таким простым:

#define Py_LIMITED_API 1
#include "Python.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#ifdef WINDOWS
int WINAPI wWinMain(
    HINSTANCE hInstance,      /* handle to current instance */
    HINSTANCE hPrevInstance,  /* handle to previous instance */
    LPWSTR lpCmdLine,         /* pointer to command line */
    int nCmdShow              /* show state of window */
)
#else
int wmain()
#endif
{
    wchar_t **myargv = _alloca((__argc + 1) * sizeof(wchar_t*));
    myargv[0] = __wargv[0];
    memcpy(myargv + 1, __wargv, __argc * sizeof(wchar_t *));
    return Py_Main(__argc+1, myargv);
}

Если вы определите препроцессорную макрос WINDOWS, это сгенерирует GUI-приложение, а без неё — консольное.

Для компиляции исполняемого файла можно использовать стандартные инструменты MSVC командной строки, или воспользоваться тем фактом, что distutils умеет компилировать исходный код Python:

>>> from distutils.ccompiler import new_compiler
>>> import distutils.sysconfig
>>> import sys
>>> import os
>>> from pathlib import Path

>>> def compile(src):
>>>     src = Path(src)
>>>     cc = new_compiler()
>>>     exe = src.stem
>>>     cc.add_include_dir(distutils.sysconfig.get_python_inc())
>>>     cc.add_library_dir(os.path.join(sys.base_exec_prefix, 'libs'))
>>>     # First the CLI executable
>>>     objs = cc.compile([str(src)])
>>>     cc.link_executable(objs, exe)
>>>     # Now the GUI executable
>>>     cc.define_macro('WINDOWS')
>>>     objs = cc.compile([str(src)])
>>>     cc.link_executable(objs, exe + 'w')

>>> if __name__ == "__main__":
>>>     compile("zastub.c")

Получившийся загрузчик использует «ограниченный ABI», поэтому он будет работать без изменений с любой версией Python 3.x. Единственное, что требуется — наличие Python (python3.dll) в переменной среды PATH пользователя.

Для полностью автономного распространения можно распространять загрузчик с приложенным приложением, объединённым с «встроенным» дистрибутивом Python. Он будет работать на любом ПК с соответствующей архитектурой (32-битной или 64-битной).

Ограничения

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

  1. Если ваше приложение зависит от пакета, включающего C-расширение, этот пакет нельзя запустить из zip-файла (это ограничение ОС, так как исполняемый код должен быть в файловой системе, чтобы его загрузил загрузчик ОС). В этом случае вы можете исключить эту зависимость из zip-файла и либо потребовать от пользователей её установки, либо распространять её вместе с zip-файлом и добавить код в __main__.py для включения каталога с разархивированным модулем в sys.path. В этом случае вам необходимо убедиться в распространении соответствующих библиотек для ваших целевых архитектур (и, возможно, выбрать правильную версию, которую нужно добавить в sys.path во время выполнения, в зависимости от машины пользователя).
  2. Если вы распространяете исполняемый файл Windows, как описано выше, вам нужно убедиться, что у ваших пользователей python3.dll есть в переменной среды PATH (это не поведение по умолчанию установщика), или вы должны распространять своё приложение с встроенным дистрибутивом.
  3. Предлагаемый загрузчик выше использует API встраивания Python. Это означает, что в вашем приложении sys.executable будет вашим приложением, а не обычным интерпретатором Python. Ваш код и его зависимости должны быть готовы к такой возможности. Например, если ваше приложение использует модуль multiprocessing, ему нужно вызвать multiprocessing.set_executable(), чтобы модуль знал, где найти стандартный интерпретатор Python.

Формат архива Python Zip Application

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

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

Формально, формат zip-приложения Python:

  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.

© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.7/library/zipapp.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API