zipapp
--- 実行可能な Python zip 書庫を管理する¶
バージョン 3.5 で追加.
ソースコード: Lib/zipapp.py
このモジュールは Python コードを含む zip ファイルの作成を行うツールを提供します。 zip ファイルは Python インタープリタで直接実行することが出来ます。 このモジュールは コマンドラインインターフェイス と Python API の両方を提供します。
基本的な例¶
実行可能なアーカイブを Python コードを含むディレクトリから作成する為に コマンドラインインターフェイス をどのように利用することができるかを以下に例示します。アーカイブは実行時にアーカイブ内の myapp
モジュールから main
関数を実行します。
$ python -m zipapp myapp -m "myapp:main"
$ python myapp.pyz
<output from myapp>
コマンドラインインターフェイス¶
コマンドラインからプログラムとして呼び出す場合は、次の形式を使用います:
$ python -m zipapp source [options]
source がディレクトリである場合、 source ディレクトリの内容からアーカイブを作成します。 source がファイルである場合、 source ファイル自身をアーカイブ化し、保存先アーカイブへコピーします。(または --info オプションが指定されている場合はファイルのシェバン行が表示されます。)
以下のオプションが解釈されます:
-
-o
<output>
,
--output
=<output>
¶ 出力を output に指定した名前のファイルへ書込みます。このオプションが指定されていない場合、出力先ファイル名は入力元 source と同じになり、
.pyz
拡張子が付与されます。 ファイル名が明示的に指定されている場合は、指定されたファイル名を使用します。(必要であれば.pyz
拡張子が含まれます。)source がアーカイブである場合は、出力先ファイル名を必ず指定しなければなりません。 (source がアーカイブである場合は output を必ず source とは別の名前にしてください。)
-
-p
<interpreter>
,
--python
=<interpreter>
¶ 実行コマンドとしての interpreter を指定する
#!
行を書庫に追加します。 また、POSIX では書庫を実行可能にします。 デフォルトでは#!
行を書かず、ファイルを実行可能にはしません。
-
-m
<mainfn>
,
--main
=<mainfn>
¶ mainfn を実行するアーカイブへ
__main__.py
ファイルを書込んでください。 mainfn 引数は "pkg.mod:fn" の形式で指定します。 "pkg.mod" の場所はアーカイブ内の package/module です。 "fn" は指定した module から呼出すことのできる関数です。__main__.py
ファイルが module から呼出すことのできる関数を実行します。書庫をコピーする際、
--main
を指定することは出来ません。
-
-c
,
--compress
¶
Compress files with the deflate method, reducing the size of the output file. By default, files are stored uncompressed in the archive.
--compress
has no effect when copying an archive.バージョン 3.7 で追加.
-
--info
¶
診断するために書庫に埋め込まれたインタープリタを表示します。 この場合、他の全てのオプションは無視され、SOURCE はディレクトリではなく書庫でなければなりません。
-
-h
,
--help
¶
簡単な使用法を表示して終了します。
Python API¶
このモジュールは 2 つの簡便関数を定義しています:
-
zipapp.
create_archive
(source, target=None, interpreter=None, main=None, filter=None, compressed=False)¶ source からアプリケーション書庫を作成します。 ソースは以下のいずれかです:
ディレクトリ名、または 新しいアプリケーションアーカイブがディレクトリのコンテンツから作成される場合に path-like object オブジェクトが参照するディレクトリ。
既存のアプリケーションアーカイブファイルの名前、 または(interpreter 引数に指定した値を反映し、修正する)アーカイブへ ファイルがコピーされる場合に path-like object オブジェクトが参照するファイル。
バイトモードの読込みで開くファイルオブジェクト。 ファイルの内容がアプリケーションアーカイブとなり、 ファイルオブジェクトがアーカイブの起点となります。
target 引数は作成される書庫が書き込まれる場所を決めます:
ファイル名、または path-like object オブジェクトを指定した場合、アーカイブは指定したファイルへ書込まれます。
開いているファイルオブジェクトを指定した場合、 アーカイブはそのファイルオブジェクトへ書込みを行ないます。 ファイルオブジェクトは必ずバイトモードの書込みで開いてください。
target を指定しないか
None
を渡した場合、 source は必ずディレクトリでなければならず、target は source のファイル名に.pyz
拡張子を付与したファイル名となります。
interpreter 引数はアーカイブが実行時に使用する Python インタープリタの名前を指定します。 インタープリタ名は "シェバン" 行としてアーカイブの起点に書込まれます。 POSIX では OS によってシェバンが解釈され、 Windows では Python ランチャーによって扱われます。 シェバン行が書込まれていない場合は interpreter の結果を無視します。 interpreter が指定されており、 target がファイル名である場合、 target ファイルの実行可能ビットが設定されます。
main 引数はアーカイブのメインプログラムとして使用する callable の名前を指定します。 main 引数は source がディレクトリであり、 source が既に
__main__.py
ファイルを保持していない場合に限り、指定することができます。 main 引数は "pkg.module:callable" の形式を取り、 アーカイブは "pkg.module" をインポートして実行され、 指定した callable を引数なしで実行します。 source がディレクトリであり、__main__.py
が含まれていない場合、 main は無視すべきエラーとなり、 作成されたアーカイブには実行可能ビットが設定されません。The optional filter argument specifies a callback function that is passed a Path object representing the path to the file being added (relative to the source directory). It should return
True
if the file is to be added.The optional compressed argument determines whether files are compressed. If set to
True
, files in the archive are compressed with the deflate method; otherwise, files are stored uncompressed. This argument has no effect when copying an existing archive.source または target へファイルオブジェクトを指定した場合、 caller が create_archive の呼出し後にオブジェクトを閉じます。
既存のアーカイブをコピーする際、ファイルオブジェクトは
read
,readline
,write
メソッドのみを提供します。 アーカイブをディレクトリから作成する際、 target がファイルオブジェクトである場合は、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>
シバン行を既存の書庫で置換するには、 create_archive()
function: を使用して変更された書庫を作成します:
>>> 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())
インタープリタの指定¶
Note that if you specify an interpreter and then distribute your application
archive, you need to ensure that the interpreter used is portable. The Python
launcher for Windows supports most common forms of POSIX #!
line, but there
are other issues to consider:
If you use "/usr/bin/env python" (or other forms of the "python" command, such as "/usr/bin/python"), you need to consider that your users may have either Python 2 or Python 3 as their default, and write your code to work under both versions.
If you use an explicit version, for example "/usr/bin/env python3" your application will not work for users who do not have that version. (This may be what you want if you have not made your code Python 2 compatible).
There is no way to say "python X.Y or later", so be careful of using an exact version like "/usr/bin/env python3.4" as you will need to change your shebang line for users of Python 3.5, for example.
Typically, you should use an "/usr/bin/env python2" or "/usr/bin/env python3", depending on whether your code is written for Python 2 or 3.
Creating Standalone Applications with zipapp¶
Using the zipapp
module, it is possible to create self-contained Python
programs, which can be distributed to end users who only need to have a
suitable version of Python installed on their system. The key to doing this
is to bundle all of the application's dependencies into the archive, along
with the application code.
The steps to create a standalone archive are as follows:
Create your application in a directory as normal, so you have a
myapp
directory containing a__main__.py
file, and any supporting application code.Install all of your application's dependencies into the
myapp
directory, using pip:$ python -m pip install -r requirements.txt --target myapp
(this assumes you have your project requirements in a
requirements.txt
file - if not, you can just list the dependencies manually on the pip command line).Optionally, delete the
.dist-info
directories created by pip in themyapp
directory. These hold metadata for pip to manage the packages, and as you won't be making any further use of pip they aren't required - although it won't do any harm if you leave them.Package the application using:
$ python -m zipapp -p "interpreter" myapp
This will produce a standalone executable, which can be run on any machine with the appropriate interpreter available. See インタープリタの指定 for details. It can be shipped to users as a single file.
On Unix, the myapp.pyz
file is executable as it stands. You can rename the
file to remove the .pyz
extension if you prefer a "plain" command name. On
Windows, the myapp.pyz[w]
file is executable by virtue of the fact that
the Python interpreter registers the .pyz
and .pyzw
file extensions
when installed.
Making a Windows executable¶
On Windows, registration of the .pyz
extension is optional, and
furthermore, there are certain places that don't recognise registered
extensions "transparently" (the simplest example is that
subprocess.run(['myapp'])
won't find your application - you need to
explicitly specify the extension).
On Windows, therefore, it is often preferable to create an executable from the
zipapp. This is relatively easy, although it does require a C compiler. The
basic approach relies on the fact that zipfiles can have arbitrary data
prepended, and Windows exe files can have arbitrary data appended. So by
creating a suitable launcher and tacking the .pyz
file onto the end of it,
you end up with a single-file executable that runs your application.
A suitable launcher can be as simple as the following:
#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);
}
If you define the WINDOWS
preprocessor symbol, this will generate a
GUI executable, and without it, a console executable.
To compile the executable, you can either just use the standard MSVC command line tools, or you can take advantage of the fact that distutils knows how to compile Python source:
>>> 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")
The resulting launcher uses the "Limited ABI", so it will run unchanged with
any version of Python 3.x. All it needs is for Python (python3.dll
) to be
on the user's PATH
.
For a fully standalone distribution, you can distribute the launcher with your application appended, bundled with the Python "embedded" distribution. This will run on any PC with the appropriate architecture (32 bit or 64 bit).
Caveats¶
There are some limitations to the process of bundling your application into a single file. In most, if not all, cases they can be addressed without needing major changes to your application.
If your application depends on a package that includes a C extension, that package cannot be run from a zip file (this is an OS limitation, as executable code must be present in the filesystem for the OS loader to load it). In this case, you can exclude that dependency from the zipfile, and either require your users to have it installed, or ship it alongside your zipfile and add code to your
__main__.py
to include the directory containing the unzipped module insys.path
. In this case, you will need to make sure to ship appropriate binaries for your target architecture(s) (and potentially pick the correct version to add tosys.path
at runtime, based on the user's machine).If you are shipping a Windows executable as described above, you either need to ensure that your users have
python3.dll
on their PATH (which is not the default behaviour of the installer) or you should bundle your application with the embedded distribution.The suggested launcher above uses the Python embedding API. This means that in your application,
sys.executable
will be your application, and not a conventional Python interpreter. Your code and its dependencies need to be prepared for this possibility. For example, if your application uses themultiprocessing
module, it will need to callmultiprocessing.set_executable()
to let the module know where to find the standard Python interpreter.
The Python Zip Application Archive Format¶
Python has been able to execute zip files which contain a __main__.py
file
since version 2.6. In order to be executed by Python, an application archive
simply has to be a standard zip file containing a __main__.py
file which
will be run as the entry point for the application. As usual for any Python
script, the parent of the script (in this case the zip file) will be placed on
sys.path
and thus further modules can be imported from the zip file.
The zip file format allows arbitrary data to be prepended to a zip file. The
zip application format uses this ability to prepend a standard POSIX "shebang"
line to the file (#!/path/to/interpreter
).
Formally, the Python zip application format is therefore:
An optional shebang line, containing the characters
b'#!'
followed by an interpreter name, and then a newline (b'\n'
) character. The interpreter name can be anything acceptable to the OS "shebang" processing, or the Python launcher on Windows. The interpreter should be encoded in UTF-8 on Windows, and insys.getfilesystemencoding()
on POSIX.Standard zipfile data, as generated by the
zipfile
module. The zipfile content must include a file called__main__.py
(which must be in the "root" of the zipfile - i.e., it cannot be in a subdirectory). The zipfile data can be compressed or uncompressed.
If an application archive has a shebang line, it may have the executable bit set on POSIX systems, to allow it to be executed directly.
There is no requirement that the tools in this module are used to create application archives - the module is a convenience, but archives in the above format created by any means are acceptable to Python.