zipapp — Управление исполняемыми zip-архивами Python
Введено в версии 3.5.
Исходный код: Lib/zipapp.py
Этот модуль предоставляет инструменты для управления созданием zip-архивов, содержащих код Python, которые могут быть непосредственно выполнены интерпретатором Python. Модуль предоставляет как интерфейс командной строки, так и API Python.
28.4.1. Базовый пример
Следующий пример демонстрирует, как использовать интерфейс командной строки для создания исполняемого архива из каталога, содержащего код Python. При запуске архив выполнит функцию main из модуля myapp в архиве.
$ python -m zipapp myapp -m "myapp:main" $ python myapp.pyz <output from myapp>
28.4.2. Интерфейс командной строки
При вызове в качестве программы из командной строки используется следующий формат:
$ 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не может быть указан при копировании архива.
-
--info -
Отобразить интерпретатор, встроенный в архив, для диагностических целей. В этом случае любые другие параметры игнорируются, и SOURCE должен быть архивом, а не каталогом.
-
-h, --help -
Вывести краткое сообщение об использовании и завершить работу.
28.4.3. API Python
Модуль определяет две удобные функции:
-
zipapp.create_archive(source, target=None, interpreter=None, main=None) -
Создать архив приложения из source. Источник может быть любым из следующих:
- Имя каталога или объект
pathlib.Path, ссылающийся на каталог, в этом случае будет создан новый архив приложения из содержимого этого каталога. - Имя существующего файла приложения или объект
pathlib.Path, ссылающийся на такой файл, в этом случае файл копируется в целевой файл (изменяя его для отражения значения, заданного для аргумента interpreter). Имя файла должно включать расширение.pyz, если требуется. - Объект файла, открытого для чтения в формате байтов. Содержимое файла должно быть архивом приложения, и предполагается, что объект файла находится в начале архива.
Аргумент target определяет, куда будет записан результирующий архив:
- Если это имя файла или объект
pathlb.Path, архив будет записан в этот файл. - Если это объект открытого файла, архив будет записан в этот объект файла, который должен быть открыт для записи в формате байтов.
- Если параметр target опущен (или
None), source должен быть каталогом, а target — файлом с тем же именем, что и source, с добавленным расширением.pyz.
Аргумент interpreter задает имя интерпретатора Python, с помощью которого будет выполняться архив. Он записывается как строка “shebang” в начале архива. В системах POSIX это будет интерпретироваться операционной системой, а в Windows — обработчиком запуска Python. Опускание interpreter приводит к тому, что строка shebang не записывается. Если интерпретатор указан, а target — это имя файла, то биты исполняемости целевого файла будут установлены.
Аргумент main задаёт имя вызываемого объекта, который будет использоваться как главная программа для архива. Он может быть указан только если source является каталогом, и source не содержит файла
__main__.py. Аргумент main должен иметь вид “pkg.module:callable”, и архив будет запущен импортом “pkg.module” и выполнением заданного вызываемого объекта без аргументов. Ошибка — опустить main, если source — это каталог, и он не содержит файла__main__.py, так как в противном случае результирующий архив не будет исполняемым.Если для source или target указан объект файла, ответственность за его закрытие лежит на вызывающей стороне после вызова create_archive.
При копировании существующего архива, объекты файлов, передаваемые, нуждаются только в методах
readиreadline, или методеwrite. При создании архива из каталога, если target является объектом файла, он будет передан классуzipfile.ZipFile, и должен предоставить необходимые методы для этого класса. - Имя каталога или объект
-
zipapp.get_interpreter(archive) -
Возвращает интерпретатор, указанный в строке
#!в начале архива. Если строка#!отсутствует, возвращаетNone. Аргумент archive может быть именем файла или объектом файла, открытым для чтения в формате байтов. Предполагается, что он находится в начале архива.
28.4.4. Примеры
Упаковать каталог в архив и запустить его.
$ python -m zipapp myapp $ python myapp.pyz <output from myapp>
То же самое можно сделать с помощью функции create_archive():
>>> import zipapp
>>> zipapp.create_archive('myapp.pyz', 'myapp')
Чтобы сделать приложение непосредственно исполняемым в системах 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())
28.4.5. Указание интерпретатора
Обратите внимание, что если вы указываете интерпретатор, а затем распространяете свой архив приложения, вам необходимо убедиться, что используемый интерпретатор переносимый. Загрузчик 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 написан ваш код.
28.4.6. Создание автономных приложений с помощью 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 при установке.
28.4.6.1. Создание исполняемого файла для 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-битная).
28.4.6.2. Ограничения
Процесс объединения вашего приложения в один файл имеет некоторые ограничения. В большинстве, если не во всех, случаях их можно устранить без необходимости серьёзных изменений в вашем приложении.
- Если ваше приложение зависит от пакета, включающего C-расширение, этот пакет не может быть запущен из zip-файла (это ограничение операционной системы, так как исполняемый код должен быть доступен в файловой системе для загрузки загрузчиком ОС). В этом случае вы можете исключить эту зависимость из zip-файла и либо потребовать, чтобы у ваших пользователей был установлен этот пакет, либо распространить его вместе с zip-файлом и добавить код в
__main__.pyдля включения каталога с разархивированным модулем вsys.path. В этом случае вам необходимо убедиться в распространении соответствующих бинарных файлов для целевых архитектур (и, возможно, выбора правильной версии для добавления вsys.pathво время выполнения, в зависимости от машины пользователя). - Если вы распространяете исполняемый файл Windows, как описано выше, вам либо нужно убедиться, что у ваших пользователей
python3.dllнаходится в переменной среды PATH (что не является стандартным поведением установщика), либо объединить приложение со встроенным дистрибутивом. - Предложенный загрузчик использует API встроенного Python. Это означает, что в вашем приложении
sys.executableбудет вашим приложением, а не стандартным интерпретатором Python. Ваш код и его зависимости должны быть готовы к этому. Например, если ваше приложение использует модульmultiprocessing, ему необходимо вызватьmultiprocessing.set_executable(), чтобы модуль знал, где найти стандартный интерпретатор Python.
28.4.7. Формат архива Python Zip Application
Python с версии 2.6 может запускать zip-файлы, содержащие файл __main__.py. Для запуска интерпретатором Python архиву приложения достаточно быть стандартным zip-файлом, содержащим файл __main__.py, который будет использоваться в качестве точки входа приложения. Как обычно для любого скрипта Python, родительская директория скрипта (в данном случае zip-файл) будет размещена в sys.path, и, таким образом, другие модули могут быть импортированы из zip-файла.
Формат zip-файлов позволяет вставлять произвольные данные в начало zip-файла. Формат zip-приложений использует эту возможность для добавления стандартной POSIX-строки shebang в файл (#!/path/to/interpreter).
Таким образом, формально формат Python zip-приложения:
- Необязательная строка shebang, содержащая символы
b'#!', за которыми следует имя интерпретатора и новая строка (b'\n'). Имя интерпретатора может быть любым, приемлемым для обработки shebang операционной системой или загрузчиком Python в Windows. Интерпретатор должен быть закодирован в UTF-8 в Windows и вsys.getfilesystemencoding()в POSIX. - Стандартные данные 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.6/library/zipapp.html