zipapp — Управление исполняемыми zip-архивами Python¶
Добавлено в версии 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 - это файл, то это должен быть архив, и он будет скопирован в целевой архив (или будет отображено содержимое его промежуточной строки, если указан параметр –info).
Возможны следующие варианты:
- -o <output>, --output=<output>¶
Запишите выходные данные в файл с именем output. Если этот параметр не указан, имя выходного файла будет таким же, как и имя входного файла source, с добавлением расширения
.pyz. Если указано явное имя файла, оно используется как есть (поэтому при необходимости следует указать расширение.pyz).Имя выходного файла должно быть указано, если источником является архив (и в этом случае выходные данные не должны совпадать с источником).
- -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¶
Отобразите интерпретатор, встроенный в архив, для диагностических целей. В этом случае любые другие параметры игнорируются, и источником должен быть архив, а не каталог.
-
-h, --help¶
Распечатайте краткое сообщение об использовании и завершите работу.
API Python¶
Модуль определяет две удобные функции:
- zipapp.create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)¶
Создайте архив приложения из источника. Источником может быть любой из следующих файлов:
Имя каталога или path-like object, указывающее на каталог, и в этом случае из содержимого этого каталога будет создан новый архив приложения.
Имя существующего файла архива приложения или path-like object, ссылающегося на такой файл, и в этом случае файл копируется в целевой файл (изменяя его, чтобы отразить значение, указанное в аргументе interpreter). При необходимости имя файла должно содержать расширение
.pyz.Файловый объект, открытый для чтения в байтовом режиме. Содержимое файла должно быть архивом приложения, и предполагается, что файловый объект находится в начале архива.
Аргумент target определяет, куда будет записан результирующий архив:
Если это имя файла или path-like object, то архив будет записан в этот файл.
Если это открытый файловый объект, то архив будет записан в этот файловый объект, который должен быть открыт для записи в байтовом режиме.
Если цель не указана (или
None), источником должен быть каталог, а целью будет файл с тем же именем, что и у источника, с добавленным расширением.pyz.
Аргумент interpreter указывает имя интерпретатора Python, с помощью которого будет выполнен архив. Оно записывается в виде строки «shebang» в начале архива. В POSIX это будет интерпретировано операционной системой, а в Windows это будет обработано программой запуска Python. Если не использовать interpreter, строка shebang не будет записана. Если указан интерпретатор, а целью является имя файла, то будет задан исполняемый бит целевого файла.
Аргумент main указывает имя вызываемого объекта, который будет использоваться в качестве основной программы для архива. Его можно указать только в том случае, если источником является каталог, и этот источник еще не содержит
__main__.pyфайла. Аргумент main должен иметь вид «pkg.module:вызываемый», и архив будет запущен путем импорта «pkg.module» и выполнения данного вызываемого файла без аргументов. Пропуск main является ошибкой, если исходный файл является каталогом и не содержит__main__.pyфайла, так как в противном случае результирующий архив не был бы исполняемым.Необязательный аргумент filter указывает функцию обратного вызова, которой передается объект Path, представляющий путь к добавляемому файлу (относительно исходного каталога). Он должен возвращать
True, если файл должен быть добавлен.Необязательный аргумент compressed определяет, будут ли файлы сжаты. Если задано значение
True, файлы в архиве сжимаются с помощью метода deflate; в противном случае файлы сохраняются в несжатом виде. Этот аргумент не действует при копировании существующего архива.Если файловый объект указан для source или target, то вызывающий объект обязан закрыть его после вызова create_archive.
При копировании существующего архива для предоставленных файловых объектов требуются только методы
readиreadlineилиwrite. При создании архива из каталога, если целью является файловый объект, он будет передан классуzipfile.ZipFileи должен предоставить методы, необходимые этому классу.Изменено в версии 3.7: Добавлены параметры filter и compressed.
Примеры¶
Упакуйте каталог в архив и запустите его.
$ 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 в своей системе. Ключом к достижению этой цели является объединение всех зависимостей приложения в архив вместе с кодом приложения.
Для создания отдельного архива необходимо выполнить следующие действия:
Создайте свое приложение в каталоге, как обычно, чтобы у вас был каталог
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 часто предпочтительнее создавать исполняемый файл из приложения zip. Это относительно просто, хотя и требует использования компилятора C. Базовый подход основан на том факте, что zip-файлы могут содержать произвольные данные, а исполняемые файлы 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(что не является стандартным поведением установщика), либо вам следует связать свое приложение со встроенным дистрибутивом.Предложенный выше лаунчер использует Python embedding API. Это означает, что в вашем приложении
sys.executableбудет вашим приложением, а не обычным интерпретатором Python. Ваш код и его зависимости должны быть готовы к такой возможности. Например, если ваше приложение использует модульmultiprocessing, ему нужно будет вызватьmultiprocessing.set_executable(), чтобы сообщить модулю, где найти стандартный интерпретатор Python.
Формат архива приложения 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).
Таким образом, формально формат zip-приложения Python является:
Необязательная промежуточная строка, содержащая символы
b'#!', за которыми следует имя интерпретатора, а затем символ новой строки (b'\n'). Имя интерпретатора может быть любым, приемлемым для обработки в операционной системе «shebang» или в программе запуска Python в Windows. Интерпретатор должен быть закодирован в UTF-8 в Windows и вsys.getfilesystemencoding()в POSIX.Стандартные данные zip-файла, сгенерированные модулем
zipfile. Содержимое zip-файла должно включать файл с именем__main__.py(который должен находиться в «корневом каталоге» zip-файла, т.е. он не может находиться в подкаталоге). Данные zip-файла могут быть сжаты или распакованы.
Если в архиве приложения есть строка shebang, в системах POSIX может быть установлен бит исполняемого файла, позволяющий выполнять его напрямую.
Нет требования, чтобы инструменты в этом модуле использовались для создания архивов приложений - модуль удобен, но архивы в вышеуказанном формате, созданные любыми способами, приемлемы для Python.