PipenvでPythonの依存関係の管理は楽になったか?

f:id:vasilyjp:20181022133553p:plain

こんにちは。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以前、私は以下のようなフローで開発していました。

  1. 使用したいバージョンの処理系をインストールする
  2. venv/virtualenvでプロジェクト用の環境を切る
  3. 「使用したいライブラリ」をsetup.pyのinstall_requiresに列挙してpip install -e .でインストールする
  4. テスト時に利用する依存関係はsetup.pyのtest_requires に列挙する
  5. 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テクノロジーズでは、一緒にサービスを作り上げてくれる方を募集中です。 ご興味のある方は、以下のリンクからぜひご応募ください。

www.wantedly.com

カテゴリー