zipapp —実行可能なPython zipアーカイブの管理—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.8/library/zipapp
移動先:案内検索

zipapp —実行可能なPythonzipアーカイブを管理します

バージョン3.5の新機能。


ソースコード: :source: `Lib / zipapp.py`



このモジュールは、Pythonコードを含む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 がファイルの場合、それはアーカイブである必要があり、ターゲットアーカイブにコピーされます(または、–infoオプションが指定されている場合は、そのシバン行の内容が表示されます)。

次のオプションが理解されます。

-o <output>, --output=<output>

output という名前のファイルに出力を書き込みます。 このオプションを指定しない場合、出力ファイル名は入力ソースと同じになり、拡張子.pyzが追加されます。 明示的なファイル名が指定されている場合は、そのまま使用されます(したがって、必要に応じて.pyz拡張子を含める必要があります)。

source がアーカイブの場合は、出力ファイル名を指定する必要があります(その場合、 outputsource と同じであってはなりません)。

-p <interpreter>, --python=<interpreter>
実行するコマンドとしてインタプリタを指定して、アーカイブに#!行を追加します。 また、POSIXで、アーカイブを実行可能にします。 デフォルトでは、#!行を書き込まず、ファイルを実行可能にしません。
-m <mainfn>, --main=<mainfn>

mainfn を実行するアーカイブに__main__.pyファイルを書き込みます。 mainfn 引数の形式は「pkg.mod:fn」である必要があります。「pkg.mod」はアーカイブ内のパッケージ/モジュールであり、「fn」は指定されたモジュール内で呼び出し可能です。 __main__.pyファイルはその呼び出し可能ファイルを実行します。

-アーカイブのコピー時にmain を指定できません。

-c, --compress

deflateメソッドを使用してファイルを圧縮し、出力ファイルのサイズを縮小します。 デフォルトでは、ファイルは非圧縮でアーカイブに保存されます。

-compress は、アーカイブをコピーするときに効果がありません。

バージョン3.7の新機能。

--info
診断のために、アーカイブに埋め込まれているインタープリターを表示します。 この場合、他のオプションはすべて無視され、SOURCEはディレクトリではなくアーカイブである必要があります。
-h, --help
短い使用法メッセージを印刷して終了します。


Python API

このモジュールは、2つの便利な関数を定義します。

zipapp.create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)

ソースからアプリケーションアーカイブを作成します。 ソースは次のいずれかになります。

  • ディレクトリの名前、またはディレクトリを参照するパスのようなオブジェクト。この場合、新しいアプリケーションアーカイブがそのディレクトリのコンテンツから作成されます。

  • 既存のアプリケーションアーカイブファイルの名前、またはそのようなファイルを参照するパスのようなオブジェクト。この場合、ファイルはターゲットにコピーされます(に指定された値を反映するように変更します)。インタプリタ引数)。 必要に応じて、ファイル名に.pyz拡張子を含める必要があります。

  • バイトモードで読み取るために開いているファイルオブジェクト。 ファイルの内容はアプリケーションアーカイブである必要があり、ファイルオブジェクトはアーカイブの先頭に配置されていると想定されます。

target 引数は、結果のアーカイブが書き込まれる場所を決定します。

  • ファイルの名前、またはパスのようなオブジェクトの場合、アーカイブはそのファイルに書き込まれます。

  • 開いているファイルオブジェクトの場合、アーカイブはそのファイルオブジェクトに書き込まれます。これは、バイトモードで書き込むために開いている必要があります。

  • ターゲットを省略した場合(またはNone)、ソースはディレクトリである必要があり、ターゲットはソースと同じ名前のファイルに.pyz拡張子が追加されたものになります。

インタープリター引数は、アーカイブが実行されるPythonインタープリターの名前を指定します。 アーカイブの冒頭に「シバン」の行として書かれています。 POSIXでは、これはOSによって解釈され、WindowsではPythonランチャーによって処理されます。 インタプリタを省略すると、シバン行は書き込まれません。 インタプリタが指定されていて、ターゲットがファイル名の場合、ターゲットファイルの実行可能ビットが設定されます。

main 引数は、アーカイブのメインプログラムとして使用される呼び出し可能オブジェクトの名前を指定します。 ソースがディレクトリであり、ソースに__main__.pyファイルがまだ含まれていない場合にのみ指定できます。 main 引数は「pkg.module:callable」の形式である必要があり、アーカイブは「pkg.module」をインポートし、引数なしで指定されたcallableを実行することによって実行されます。 ソースがディレクトリであり、__main__.pyファイルが含まれていない場合、 main を省略するとエラーになります。そうしないと、結果のアーカイブが実行できなくなります。

オプションの 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>

既存のアーカイブのシバン行を置き換えるには、 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())

通訳者の指定

インタプリタを指定してからアプリケーションアーカイブを配布する場合は、使用するインタプリタが移植可能であることを確認する必要があることに注意してください。 Windows用のPythonランチャーは、POSIX #!行の最も一般的な形式をサポートしていますが、考慮すべき他の問題があります。

  • 「/ usr / bin / envpython」(または「/ usr / bin / python」などの他の形式の「python」コマンド)を使用する場合は、ユーザーがPython2またはPython3のいずれかを使用している可能性があることを考慮する必要があります。デフォルトとして、両方のバージョンで動作するようにコードを記述します。
  • 「/ usr / bin / envpython3」などの明示的なバージョンを使用する場合、そのバージョンを持たないユーザーに対してアプリケーションは機能しません。 (これは、コードをPython 2と互換性のあるものにしていない場合に必要なものかもしれません)。
  • 「pythonXY以降」と言う方法はありません。たとえば、Python 3.5のユーザーのシバン行を変更する必要があるため、「/ usr / bin /envpython3.4」のような正確なバージョンを使用するように注意してください。 。

通常、コードがPython 2または3のどちらで記述されているかに応じて、「/ usr / bin / envpython2」または「/ usr / bin / envpython3」を使用する必要があります。


zipappを使用したスタンドアロンアプリケーションの作成

zipapp モジュールを使用すると、自己完結型のPythonプログラムを作成できます。このプログラムは、適切なバージョンのPythonをシステムにインストールするだけでよいエンドユーザーに配布できます。 これを行うための鍵は、アプリケーションのすべての依存関係をアプリケーションコードとともにアーカイブにバンドルすることです。

スタンドアロンアーカイブを作成する手順は次のとおりです。

  1. 通常どおりディレクトリにアプリケーションを作成します。これにより、__main__.pyファイルとサポートするアプリケーションコードを含むmyappディレクトリが作成されます。

  2. pipを使用して、アプリケーションのすべての依存関係をmyappディレクトリにインストールします。

    $ python -m pip install -r requirements.txt --target myapp

    (これは、プロジェクト要件がrequirements.txtファイルにあることを前提としています。そうでない場合は、pipコマンドラインで依存関係を手動で一覧表示できます)。

  3. 必要に応じて、myappディレクトリ内のpipによって作成された.dist-infoディレクトリを削除します。 これらは、パッケージを管理するためのpipのメタデータを保持します。また、pipをこれ以上使用しないため、必要ありません。ただし、そのままにしておいても害はありません。

  4. 以下を使用してアプリケーションをパッケージ化します。

    $ python -m zipapp -p "interpreter" myapp

これにより、スタンドアロンの実行可能ファイルが生成されます。この実行可能ファイルは、適切なインタープリターが使用可能な任意のマシンで実行できます。 詳細については、インタープリターの指定を参照してください。 単一のファイルとしてユーザーに出荷できます。

Unixでは、myapp.pyzファイルはそのまま実行可能です。 「プレーン」コマンド名が必要な場合は、ファイルの名前を変更して.pyz拡張子を削除できます。 Windowsでは、Pythonインタープリターがインストール時に.pyzおよび.pyzwファイル拡張子を登録するため、myapp.pyz[w]ファイルは実行可能です。

Windowsを実行可能にする

Windowsでは、.pyz拡張機能の登録はオプションであり、さらに、登録された拡張機能を「透過的に」認識しない特定の場所があります(最も簡単な例は、subprocess.run(['myapp'])があなたのアプリケーション-拡張子を明示的に指定する必要があります)。

したがって、Windowsでは、zipappから実行可能ファイルを作成することが望ましい場合がよくあります。 これは比較的簡単ですが、Cコンパイラが必要です。 基本的なアプローチは、zipファイルに任意のデータを追加でき、Windowsexeファイルに任意のデータを追加できるという事実に依存しています。 したがって、適切なランチャーを作成し、その最後に.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プリプロセッサシンボルを定義すると、GUI実行可能ファイルが生成され、それがない場合はコンソール実行可能ファイルが生成されます。

実行可能ファイルをコンパイルするには、標準の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")

結果のランチャーは「LimitedABI」を使用するため、Python3.xのどのバージョンでも変更されずに実行されます。 必要なのは、Python(python3.dll)がユーザーのPATH上にあることだけです。

完全にスタンドアロンのディストリビューションの場合、Pythonの「埋め込み」ディストリビューションにバンドルされた、アプリケーションが追加されたランチャーをディストリビューションできます。 これは、適切なアーキテクチャ(32ビットまたは64ビット)を備えたすべてのPCで実行されます。


警告

アプリケーションを単一のファイルにバンドルするプロセスには、いくつかの制限があります。 すべてではないにしても、ほとんどの場合、アプリケーションに大きな変更を加えることなく対処できます。

  1. アプリケーションがC拡張子を含むパッケージに依存している場合、そのパッケージをzipファイルから実行することはできません(OSローダーがロードするにはファイルシステムに実行可能コードが存在する必要があるため、これはOSの制限です)。 この場合、その依存関係をzipファイルから除外し、ユーザーにインストールを要求するか、zipファイルと一緒に出荷して__main__.pyにコードを追加し、解凍したモジュールを含むディレクトリを[ X240X] 。 この場合、ターゲットアーキテクチャに適したバイナリを出荷する必要があります(ユーザーのマシンに基づいて、実行時にsys.pathに追加する正しいバージョンを選択する可能性があります)。
  2. 上記のようにWindows実行可能ファイルを出荷する場合は、ユーザーのPATHにpython3.dllが含まれていることを確認するか(インストーラーのデフォルトの動作ではありません)、アプリケーションを組み込みディストリビューションにバンドルする必要があります。 。
  3. 上記の推奨ランチャーは、Python埋め込みAPIを使用します。 つまり、アプリケーションでは、sys.executableがアプリケーションになり、は従来のPythonインタープリターではありません。 コードとその依存関係は、この可能性に備えて準備する必要があります。 たとえば、アプリケーションで multiprocessing モジュールを使用する場合は、 multiprocessing.set_executable()を呼び出して、標準のPythonインタープリターの場所をモジュールに通知する必要があります。


PythonZipアプリケーションアーカイブ形式

Pythonは、バージョン2.6以降、__main__.pyファイルを含むzipファイルを実行できるようになりました。 Pythonで実行するには、アプリケーションアーカイブは、アプリケーションのエントリポイントとして実行される__main__.pyファイルを含む標準のzipファイルである必要があります。 Pythonスクリプトの場合と同様に、スクリプトの親(この場合はzipファイル)は sys.path に配置されるため、zipファイルからさらにモジュールをインポートできます。

zipファイル形式では、任意のデータをzipファイルの前に追加できます。 zipアプリケーション形式では、この機能を使用して、標準のPOSIX「シバン」行をファイル(#!/path/to/interpreter)の前に追加します。

したがって、正式には、Pythonzipアプリケーションの形式は次のとおりです。

  1. オプションのシバン行。文字b'#!'、インタプリタ名、改行(b'\n')文字が続きます。 インタープリター名は、OSの「シバン」処理またはWindowsのPythonランチャーに受け入れられるものであれば何でもかまいません。 インタプリタは、WindowsではUTF-8で、POSIXでは sys.getfilesystemencoding()でエンコードする必要があります。
  2. zipfile モジュールによって生成された標準のzipfileデータ。 zipファイルの内容 must には、__main__.pyというファイルが含まれている必要があります(zipファイルの「ルート」にある必要があります。つまり、サブディレクトリに含めることはできません)。 zipfileデータは圧縮または非圧縮にすることができます。

アプリケーションアーカイブにシバン行がある場合、POSIXシステムで実行可能ビットが設定されている可能性があり、直接実行できるようになっています。

このモジュールのツールを使用してアプリケーションアーカイブを作成する必要はありません。このモジュールは便利ですが、Pythonでは、任意の方法で作成された上記の形式のアーカイブを使用できます。