ちょっと前に pex というものを知った。
tl;dr — pex 0.7.0-rc0 documentation
pex って何かというと、公式サイトのドキュメントには以下のようにある。
PEX files are self-contained executable Python virtual environments. More specifically, they are carefully constructed zip files with a #!/usr/bin/env python and special __main__.py that allows you to interact with the PEX runtime.
https://pex.readthedocs.org/en/stable/whatispex.html#tl-dr
簡単に説明すると、.pex というファイルに Python の仮想環境を作って、実行できるもの。
Golang や Java の FatJar みたいなものという印象を受けた*1。
つまり Python でもライブラリ、アプリケーションを一つにまとめて、実行ファイル形式にして、配布できて、実行できる。
Golang をやるようになって、1 つのバイナリで配布できるメリットは凄くあったので、Python でもやりたいなと思っていた。
これを叶えてくれるのが pex みたい。
という訳で色々試してみる。
まずは pex の環境を作る。
$ mkvirtualenv pex-py34 --python=/opt/local/python-3.4/bin/python3 $ pip install pex $ pex Python 3.4.3 (default, May 25 2015, 18:46:46) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>>
無事に入った。
サンプルとしてドキュメントにもある Sphinx を 1 つのバイナリにしてみる。
$ pex -v sphinx sphinx_rtd_theme -e sphinx:main -o sphinx.pex pytz 2015.4 pex :: Resolving distributions :: Packaging MarkupSafe docutils 0.12 Babel 2.0 Jinja2 2.8 sphinx-rtd-theme 0.1.8 snowballstemmer 1.2.0 six 1.9.0 MarkupSafe 0.23 Pygments 2.0.2 alabaster 0.7.6 Sphinx 1.3.1 pex: Building pex: 9286.8ms pex: Resolving distributions: 7982.2ms pex: Packaging alabaster: 514.7ms pex: Packaging snowballstemmer: 445.4ms pex: Packaging Babel: 3715.0ms pex: Packaging MarkupSafe: 889.9ms Saving PEX file to sphinx.pex
以下引数の説明。
-v は verbose で標準出力のログをだす。
sphinx sphinx_rtd_theme は Sphinx のパッケージを指定する。ここで指定すると自動的にダウンロードしてパッケージ化してくれる模様。
-e ではアプリケーションのエントリーポイントを指定する。ここでは sphinx:main がそれに当たる。
-o は出力するバイナリファイルを指定する。
つまり -o で出力されたバイナリのエントリーポイントに -e で指定したものが実行されるようになる。
出力結果を見てみる。
$ ls -la -rwxr-xr-x 1 heavenshell admin 7783967 9 8 01:21 sphinx.pex
ここで一旦 virtualenv を抜けて(環境が無い場所で実行してみるため)、実行をしてみる。
$ deactivate $ ./sphinx.pex --version Sphinx (sphinx-build) 1.3.1
無事に Sphinx が 1 バイナリになった。
pex は依存するパッケージを指定して、そのエントリーポイントを指定することで実行ファイルになってくれる。
次に PyPi などにあがっていない自分のアプリケーションを pex 化してみる。
ここでは Flask を使った簡単なアプリケーションを pex 化してみる。
. ├── pexsample │ ├── __init__.py │ └── app.py └── setup.py
pexsample というパッケージに app.py がある。
#!/usr/bin/env python # -*- coding: utf-8 -*- from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'hello' def main(): app.run() if __name__ == '__main__': main()
普通に起動したら以下の用になる。
$ python pexsample/app.py * Running on http://127.0.0.1:5000/
これを pex 化して、以下のように起動できるようにする。
$ ./app.pex * Running on http://127.0.0.1:5000/
自分のアプリケーションは当然 PyPi にあがっていないので、ローカルでビルドできるようにする。
そのために setup.py が必要になる。
#!/usr/bin/env python # -*- coding: utf-8 -*- from setuptools import setup, find_packages setup( name='pexsample', version='1.0', description='A simple PEX example', packages=find_packages(), package_dir={'': '.'} )
$ pex -v flask . -e pexsample.app:main -o app.pex Werkzeug 0.10.4 :: Resolving distributions :: Packaging MarkupSafe itsdangerous 0.24 MarkupSafe 0.23 Flask 0.10.1 pexsample 1.1 Jinja2 2.8 pex: Building pex: 3536.3ms pex: Resolving distributions: 3121.4ms pex: Packaging Flask: 776.0ms pex: Packaging pexsample: 345.8ms pex: Packaging itsdangerous: 368.0ms pex: Packaging MarkupSafe: 510.1ms Saving PEX file to app.pex
引数に flask と カレントディレクトリ(アプリケーション、つまり setup.py がある場所)を指定する*2。
$ ls -la drwxr-xr-x 4 heavenshell admin 136 9 8 01:29 pexsample -rw-r--r-- 1 heavenshell admin 388 9 8 01:33 setup.py drwxr-xr-x 7 heavenshell admin 238 9 8 01:34 pexsample.egg-info -rwxr-xr-x 1 heavenshell admin 976648 9 8 01:34 app.pex $ deactivate $ ./app.pex * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Flask を使ったアプリケーションが 1 バイナリになった*3。
つまりこれをデプロイするだけで Flask がそのまま動く。
ただバイナリをそのまま持って行けば、Python の無い環境でも動くかは未確認*4。
Python の C 拡張系はどうなんだろう。
一応オプションには --platform というのがあって Linux とか Mac とか選べるようだけど。
アーキテクチャを同じものなら、動くのかもしれない。
まとめ
pex を使えば 1 バイナリになる。
自分のアプリケーションを pex 化する際に最初上手く出来なくて色々ハマったけど、色々やってみたら出来た。
もっとも今回の例みたいな Flask のみを動かすのはほぼないと思うけど…。
単純に Web アプリケーションを動かしたいだけなら、フロントに Nginx だったり、アプリケーションサーバが必要だったりで、pex を使うメリットはあまりなさそう。
じゃあどういう所で有効そうというと、例えばお客さんの本番環境に Python で作ったツール*5を入れたいけど、Python 自体はともかく pip だのライブラリ等を入れての動かせる環境作るのが難しいというシナリオにハマりそう。