pexでPythonの単一実行ファイルを作る使い方

公開日:
目次

CI でビルドした Python のツールを、本番サーバーに scp で1ファイル投げるだけで動かしたい。そういうとき自分は pex を使っています。PyInstaller でも近いことはできますが、サーバー側にどうせ Python が入っているなら pex のほうが軽くて速い、という場面が結構あります。

この記事では pex とは何かから、.pex ファイルの作り方・実行方法、そして PyInstaller との使い分けまでを、コマンド付きでまとめます。

pexとは何か

pex は Python EXecutable の略で、アプリと依存パッケージをまとめて1つの実行可能ファイル(.pex)に固める PEP 441 ベースのツールです。

中身を一言で言うと、virtualenv をまるごと固めた自己完結 zip です。.pex ファイルは実体としては zip アーカイブで、先頭に shebang が付いていて、そのまま ./app.pex と叩くと中の Python コードが動きます。pip で入れる依存もこの中に同梱されるので、配布先で pip install を走らせる必要がありません。

ここで大事なのは、pex は実行先に Python インタプリタがある前提だということです。zip の中に入っているのはあなたのコードとサードパーティ製パッケージであって、Python 本体は入っていません。後述する PyInstaller との一番の違いがここです。

インストール

pip で入ります。

pip install pex

これで pex コマンドが使えるようになります。

.pexファイルを作る

最小の例から見ていきます。requests を同梱した .pex を作るなら、こうです。

pex requests -o app.pex

-o(--output-file)が出力先のファイル名です。これを付けないと pex はその場限りの環境を作って即実行するだけでファイルが残らないので、配布物を作りたいなら -o は必須だと思っておくとよいです。

実際のアプリは依存が複数あるので、requirements.txt でまとめて渡すのが普通です。

requirements.txtを使う場合
pex -r requirements.txt -o app.pex

-r(--requirements)は pip と同じく requirements ファイルを読みます。

ここまでだと「依存は入ったけど、起動したとき何が実行されるのか」が決まっていません。エントリーポイントの指定方法は2つあります。

ひとつは -e(--entry-point)で、package:function の形でモジュール内の関数を直接指します。自作パッケージ myappmain 関数を起点にするなら、こうです。

エントリーポイントを関数で指定
pex -r requirements.txt -e myapp.cli:main -o app.pex

もうひとつは -c(--console-script)で、依存パッケージが pyproject.toml などで定義済みのコンソールスクリプト名を起点にします。たとえば ansible を同梱してその ansible コマンドを起点にするなら、こうなります。

コンソールスクリプトを起点にする
pex ansible -c ansible -o ansible.pex

-c は「すでに console_scripts として登録されている名前」を指定するもの、-e は「自分で起点の関数を名指しする」もの、という住み分けです。自作 CLI なら pyproject.toml に entry point を書いて -c で呼んでもいいし、書かずに -e で関数を直接指してもいい。自分は手早く済ませたいときは -e を使うことが多いです。

作った.pexの使い方

できた .pex の使い方は単純で、実行権限が付いた状態で出力されるので、そのまま叩けます。

./app.pex

引数もそのまま後ろに渡せます。

./app.pex --help

明示的に Python を指定して動かすこともできます。

python app.pex

配布は、この1ファイルを scp なりアーティファクトなりで送り込むだけです。展開もインストールも要りません。

PyInstallerとの違いと使い分け

PyInstaller を知っているなら、ここが一番知りたいところでしょう。両者の決定的な違いは Python インタプリタを同梱するかどうか です。

  • pex: あなたのコードと依存パッケージだけを固める。実行先に Python が必要。
  • PyInstaller: Python インタプリタごと丸ごと同梱する。実行先に Python が無くても動く。

つまり、配布先にどんな Python があるかで選び分けます。サーバーや CI のように Python が入っている(しかも自分でバージョンを管理できる)環境に配るなら、pex のほうが軽くてビルドも速いです。一方、Python が入っているか分からない一般ユーザーの PC に単体のアプリとして配るなら、インタプリタごと持っていける PyInstaller が向いています。

注意点

pex を使う前に踏まえておきたい制約があります。

.pex ファイルは、ビルドしたときと 同じ OS・同じ CPU アーキテクチャ・同じ Python インタプリタ を持つ環境でしか動きません。これは、依存にネイティブ拡張(C 拡張を含むパッケージ)があると、その wheel がプラットフォーム固有になるためです。Linux でビルドした .pex を macOS に持っていっても、そういう依存があると動かないことがあります。配布先と同じ環境(同じコンテナイメージなど)でビルドするのが安全です。

それと、pex は Unix 系(Linux / macOS)を主戦場にしたツールで、Windows では扱いづらいです。shebang で実行する仕組みが Windows ネイティブと噛み合わないためで、Windows に配るなら素直に PyInstaller を検討したほうが楽です。


サーバーや CI に Python があるなら、pex は「依存ごと1ファイルにして投げるだけ」という配布のだるさを地味に減らしてくれます。自分は Linux のコンテナ内でビルドして同じイメージで動かす、という運用に落ち着きました。Windows 配布や Python の無い環境が絡むなら、最初から PyInstaller を選んだほうが回り道になりません。

参考