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> -
Добавить строку
#!в архив, определяя interpreter как команду для выполнения. Также, в POSIX, сделать архив исполняемым. По умолчанию не записывается строка#!и файл не делается исполняемым.
-
-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 определяет, куда будет записан результирующий архив:
- Если это имя файла или объект, подобный пути, архив будет записан в этот файл.
- Если это объект открытого файла, архив будет записан в этот объект файла, который должен быть открыт для записи в двоичном режиме.
- Если целевой объект отсутствует (или
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) -
Возвращает интерпретатор, указанный в строке
#!в начале архива. Если строка#!отсутствует, возвращает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 поддерживает большинство распространённых форм строки shebang 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 на своей системе. Ключ к этому — объединение всех зависимостей приложения в архив вместе с кодом приложения.
Шаги по созданию автономного архива следующие:
- Создайте ваше приложение в каталоге, как обычно, так что у вас будет каталог
myappсодержащий файл__main__.pyи любой поддерживающий код приложения. -
Установите все зависимости вашего приложения в каталог
myappс помощью pip:$ python -m pip install -r requirements.txt --target myapp
(предполагается, что у вас есть требования к проекту в файле
requirements.txt- если нет, вы можете просто вручную перечислить зависимости в командной строке pip). - Необязательно, удалите каталоги
.dist-infoсозданные pip в каталогеmyapp. Они содержат метаданные для управления пакетами pip, и поскольку вы больше не будете использовать pip, они не требуются — хотя это не причинит вреда, если вы их оставите. -
Упакуйте приложение, используя:
$ 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, это сгенерирует графический исполняемый файл, а без неё — консольный.
Для компиляции исполняемого файла вы можете использовать стандартные инструменты командной строки 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 бита).
Ограничения
Есть некоторые ограничения процесса упаковки вашего приложения в один файл. В большинстве, если не во всех, случаях их можно решить без необходимости вносить крупные изменения в ваше приложение.
- Если ваше приложение зависит от пакета, содержащего расширение C, этот пакет не может быть запущен из zip-файла (это ограничение ОС, поскольку исполняемый код должен присутствовать в файловой системе, чтобы загрузчик ОС смог его загрузить). В этом случае вы можете исключить эту зависимость из zip-файла и либо потребовать от пользователей её установку, либо распространить её вместе с zip-файлом и добавить код в
__main__.pyдля включения каталога с распакованным модулем вsys.path. В этом случае вам нужно будет убедиться, что вы распространяете соответствующие бинарные файлы для ваших целевых архитектур (и, возможно, выбрать правильную версию для добавления вsys.pathво время выполнения, на основе машины пользователя). - Если вы распространяете исполняемый файл Windows, как описано выше, вы должны убедиться, что у пользователей
python3.dllприсутствует в переменной среды PATH (это не стандартное поведение установщика), или вы должны объединить ваше приложение со встроенным дистрибутивом. - Предложенный загрузчик выше использует API встраивания Python. Это означает, что в вашем приложении
sys.executableбудет вашим приложением, а не обычным интерпретатором Python. Ваш код и его зависимости должны быть готовы к этой возможности. Например, если ваше приложение использует модульmultiprocessing, оно должно вызватьmultiprocessing.set_executable(), чтобы указать модулю, где найти стандартный интерпретатор Python.
Формат архива приложения Python в формате zip
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:
- Необязательная строка shebang, содержащая символы
b'#!'за которым следует имя интерпретатора, а затем символ новой строки (b'\n'). Имя интерпретатора может быть любым, приемлемым для обработки «shebang» операционной системой, или загрузчиком Python в Windows. Интерпретатор должен быть закодирован в UTF-8 в Windows и вsys.getfilesystemencoding()в POSIX. - Стандартные данные zip-файла, как генерирует модуль
zipfile. Содержимое zip-файла должно включать файл с именем__main__.py(который должен быть в «корне» zip-файла — то есть он не может находиться в подкаталоге). Данные zip-файла могут быть сжатыми или несжатыми.
Если у архива приложения есть строка shebang, на системах POSIX может быть установлен исполняемый бит, чтобы его можно было непосредственно выполнить.
Нет требования, чтобы инструменты в этом модуле использовались для создания архивов приложений — модуль предназначен для удобства, но архивы в указанном выше формате, созданные любыми средствами, приемлемы для Python.
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/zipapp.html