zipapp — Управление исполняемыми zip-архивами Python
Введено в версии 3.5.
Исходный код: Lib/zipapp.py
Этот модуль предоставляет инструменты для управления созданием zip-архивов, содержащих код Python, которые могут быть выполнены непосредственно интерпретатором Python. Модуль предоставляет как интерфейс командной строки, так и 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, если требуется).Имя файла вывода необходимо указать, если 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 определяет, куда будет записан результирующий архив:
- Если это имя файла или объект-путь, архив будет записан в этот файл.
- Если это открытый объект файла, архив будет записан в этот объект файла, который должен быть открыт для записи в двоичном режиме.
- Если 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, так как в противном случае результирующий архив не будет исполняемым.Необязательный аргумент filter задает функцию обратного вызова, которая получает объект Path, представляющий путь к файлу, добавляемому (относительно каталога source). Она должна вернуть
True, если файл должен быть добавлен.Необязательный аргумент compressed определяет, сжимаются ли файлы. Если установлено
True, файлы в архиве сжимаются методом deflate, в противном случае файлы хранятся без сжатия. Этот аргумент не имеет эффекта при копировании существующего архива.Если для source или target указан объект файла, то его закрытие является обязанностью вызывающего кода после вызова create_archive.
При копировании существующего архива, объекты файлов, передаваемые только, должны иметь методы
readиreadline, или методwrite. При создании архива из каталога, если target — объект файла, он будет передан классу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).Упакуйте приложение с помощью:
$ 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-строки «шебанга» в начало файла (#!/path/to/interpreter).
Более формально, формат zip-приложения Python:
- Необязательная строка шебанга, содержащая символы
b'#!', за которыми следует имя интерпретатора, а затем символ новой строки (b'\n'). Имя интерпретатора может быть любым допустимым для обработки «шебанга» ОС или загрузчика Python в Windows. Интерпретатор должен быть закодирован в UTF-8 в Windows и вsys.getfilesystemencoding()в POSIX. - Стандартные данные zip-файла, созданные с помощью модуля
zipfile. Содержимое zip-файла должно содержать файл с именем__main__.py(который должен находиться в «корне» zip-файла — то есть он не может находиться в подкаталоге). Данные zip-файла могут быть сжаты или не сжаты.
Если в архиве приложения есть строка шебанга, на системах POSIX может быть установлен бит «исполняемый», чтобы его можно было запустить напрямую.
Нет требования, чтобы инструменты в этом модуле использовались для создания архивов приложений — этот модуль является удобством, но архивы в указанном выше формате, созданные любым способом, приемлемы для Python.
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/zipapp.html