こんにちは。ZOZO研究所 福岡の光瀬です。Pythonを書かれている皆様は、普段どのように開発をすすめていますか? pipとvenv/virtualenvによるこれまでのデファクトの組み合わせだけではなく、最近は Pipenv を使用している開発者も増えてきたのではないでしょうか。 日々の検証や開発を効率よく進めるにあたって、依存関係を適切かつ楽に管理するのはとても重要だと感じていて、ここ半年ほどPipenvを利用しています。 今回は、その中でsetup.pyやrequirements.txtそしてPipfileの住み分け・運用について考えたことをまとめてみました。
TL;DR
Pipenvが使えることで、確かに楽になった部分はあるのかなと思っています。 一方で、既存のツールとの兼ね合いがまだ微妙な部分もあります。 その上で、以下の運用がベターなのかなと考えました。
- Pipenvのみで完結させようとしない(setup.pyの
install_requires
を併用する) - setup.pyの
install_requires
には、直接の依存関係をゆるく記述しpipenv install -e
でPipenv管理下に置く - 開発・テストに必要な依存関係をPipfileの
dev-packages
へ記述する - 依存関係のロックが必要であればrequirements.txtではなくPipfile.lockを使用する
Pipenvとは何か
Pipenvは、一言でいえば pipとvirtualenvをラップして、依存関係を Pipfile およびPipfile.lockで管理するツール です。 いわゆるRubyにおけるbundlerやNode.jsにおけるnpmあるいはyarnのような立ち位置ですね。 今やごく当たり前になっているであろう開発フローをPythonにおいても実現できます。 例えば、以下に列挙する機能が実装されています。
- Pipfileによる依存関係の管理
- Pipfileによる開発用の依存関係の管理
- Pipfile.lockによる依存関係のロック
- virtualenv環境の自動構築
- Pipfileで指定された
python_version
と現在のバージョンとのチェック - Pipfileで指定された
python_version
の処理系の自動インストール(pyenvが利用可能な時のみ) - ..。など
より詳しくは公式ドキュメントをどうぞ。なお、Pipenvには日本語ドキュメントもあります。また、作者のKenneth Reitz氏のPyCon 2018におけるスライド もオススメです。
Pipenv以前の話
pipおよびvenv/virtualenvによる開発フロー
Pythonを書かれている皆様は、普段どのように開発をすすめられていますでしょうか? Pipenv以前、私は以下のようなフローで開発していました。
- 使用したいバージョンの処理系をインストールする
- venv/virtualenvでプロジェクト用の環境を切る
- 「使用したいライブラリ」をsetup.pyの
install_requires
に列挙してpip install -e .
でインストールする - テスト時に利用する依存関係はsetup.pyの
test_requires
に列挙する pip freeze
でrequirements.txtを吐きだしておく(ライブラリではない、依存関係の終端であるプリケーションの場合)
一見、特に問題がないように見えますが、開発時に必要な依存関係の管理方法が明確ではない のが1つの問題なのかなと思います。
例えば、静的型チェッカーであるmypyを使って、編集しながら型チェックをしていきたい場合があります。
この場合、mypyはどこに依存関係として記述するのが適当でしょうか?
実際にアプリケーション・ライブラリが利用される際には、通常不要なツールです。
そのため、少なくともinstall_requires
に書くのは適当でなさそうです。
一方、テスト時にのみ使用できれば良いということではないのでtest_requires
に書くのも違うように思えます。
開発時に必要な依存関係の管理に対するワークアラウンド
開発時に必要な依存関係の管理については、明確な答えはないものの、いくつかのワークアラウンドで対処されてきたようです。
- setup.pyの
extras_require
を利用してオプショナルな依存関係として扱う - requirements.txtに加えて、開発用に別途requirements-dev.txtを用意して
pip install -r requirements-dev.txt
する
いずれの場合も、pip freeze
した際に開発用の依存関係がrequirements.txtへ混入します。
requirements.txtを依存関係のロックとして扱いたい場合に、余計なパッケージが含まれるのはあまり嬉しくありません。
Pipenvが解決した問題
開発・テスト用の依存関係の管理
Pipfileには、開発・テスト用の依存関係が記述・記録される専用のdev-packages
というテーブルがあります。
コマンドラインからは、pipenv install --dev {package_name}
でインストール・Pipfileへ記録できます。
Pipfile.lockには開発用の依存関係も記録されますが、pipenv install
時に--dev
を付与した場合のみインストールされます。
pipやvirtualenvなど各種ツールの隠蔽
環境構築について、pipやvirtualenvそしてpyenvを直接さわらずとも環境がつくれるようになっています。 既存ツールをラップしながら、他の言語で提供されているツールと同等の機能を提供している点は、地味ながらもひとつの利点です。 少なくとも優れたパッケージ管理ツールを備えた他の言語のユーザーがPythonを触る際の驚きは減るはずです。
Pipenvが解決できていない問題
依存関係上の終端になるようなアプリケーションであれば、setup.pyを用意せずPipenvのみで開発を進められるように思います。 ただし、pipはPipfileを読まないため、ライブラリの場合には、Pipenvのみでは完結しません。 直接pipからインストールされるライブラリが大半であると思われる現状、ライブラリの開発においてはsetup.pyを書かざるをえなくなっています。
結局どのように運用するのが楽なのか
Pipenvの公式の見解が Pipfile vs Setup.py に書かれています。 他のコードから参照される「ライブラリ」であればsetup.pyでまず管理すべきで、どこからも参照されない「アプリケーション」であればPipfileで依存関係を管理するのが良いという話ですね。 この公式の見解を踏まえつつ、ライブラリ・アプリケーションの区別をせずに同じフローへ落とすのが楽なのかなと考えました。
- pipからインストール可能にするため、
install_requires
を使用する - 不要になったパッケージを排除しやすくするため、
pipenv install -e
でプロジェクトのパッケージをインストールし、依存関係はPipenv管理下に置く - 開発・テスト時にのみ必要なパッケージはsetup.pyに入っている必要はないので、Pipfileに書く
依存関係のロックが必要でない場合は、pipenv install
をする際に--skip-lock
するのが適当かと考えています。
もちろん、ライブラリの開発の場合は、setup.pyだけで最低限管理できます。
とはいえほとんどの場合、開発時にのみ使用するツールが入ってきますし、どのみちvenv/virtualenv環境もつくります。
そのため、初めからPipenv管理下に置いてしまった方が楽な印象です。
まとめ:Pipenvは依存関係の管理を楽にしたのか
依存関係の管理については、方法にブレが減ったという意味では楽になったと感じます。 加えて、pipとvirtualenvのラッパーとして振る舞うので、特に意識しなくとも他プロジェクトの環境と分離して管理できるのも良い点です。 一方で、pipはPipfileおよびPipfile.lockを参照できないため、「便利ではあるけど既存ツールを完全に置き換えたものではない」というのが現状のように思います。 基本はsetup.pyを書くという前提で、Pipfileを併用していこうと考えています。
ZOZOテクノロジーズでは、一緒にサービスを作り上げてくれる方を募集中です。 ご興味のある方は、以下のリンクからぜひご応募ください。