nikkie-ftnextの日記

イベントレポートや読書メモを発信

Python使いはpip freezeの出力を常にrequirements.txtというファイルに保存する? 文献を調べたところいくつか流派があるようです

はじめに

答えはYesですか? nikkieです。

ふときっかけがあり、Python使いは皆pip freeze > requirements.txtするのか文献調査しました。
わかったことを現在のバックアップとしてまとめます。

目次

きっかけ

脆弱性管理クラウドyamoryを知りました🦎
Pythonプロジェクトにも対応したツールです!

yamoryはPythonプロジェクトをスキャンするとき、requirements.txtという名前のファイルでプロジェクトの依存関係を出力しているのを前提とします。

pip プロジェクトのスキャンを行う際に requirements.txt が必要となります。

上記ドキュメントでは、pipを使っている場合はpip freezeが案内されます。
またPoetryやPipenvの場合は依存ライブラリを出力してからrequirements.txtを作るコマンドが案内されています(ありがたいですね)。

さて、小さい点なのですが、私はどうしても気になってしまいました。
どんなPythonプロジェクトでもpip freezeの出力をrequirements.txtという名前で保存するんでしょうか? 私、気になります!

念のためのおことわり

pip freezeを使って環境を再現する考え方は私も採用しています。
pip freezeを使わない方法をこの記事で取り上げるわけではありません。

pip freezeを用いた環境の再現ですが、出力ファイルの名前には自由度があると考えます1
カンファレンスや書籍でrequirements.txt以外の名前にしている例も見聞きしていました。
こういった文脈を持つ私にとって、(重箱の隅だと思うのですが)requirements.txtを前提にしたyamoryの案内はやや引っかかるものでした2
なお、この記事は、上記の点が引っかかるという理由でyamoryを批評する記事でもないです。

暫定的な結論

文献調査まとめ

  • 多数派と思われるやり方:pip freeze > requirements.txt
  • requirements.txtを開発者が書くやり方もある
    • その場合、環境の再現に使うファイルはpip freeze > requirements.lockで出力
    • requirements.txtにはプロジェクトで直接依存するライブラリを書く
  • pip-toolsを使い、requirements.txt生成するやり方もある
    • 開発者は直接依存するライブラリをrequirements.inに書く

IMO:オススメ

  • Pythonの開発にあまり慣れていないならば(この記事でいろいろ紹介しているが)pip freeze > requirements.txtがオススメ(守破離の守のフェーズ)
  • pip freeze > requirements.txtでやっていく中で、「直接依存するライブラリだけを管理したい」という経験を何回かしたら別のやり方に移行してみては(守破離の破のフェーズ)
  • 私は、個人の開発ではrequirements.lockpip-toolsを(経験値を稼ぐ意味でも)使いたい

以降は文献調査の結果です。お楽しみください!

pip freeze > requirements.txt

これはよく見る方法だと思います。

pipのUser Guideの「Requirements Files」を参照します:

python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt

今回確認したPython書籍では

  • Python実践レシピ3(1.1.3 requirements.txtを作って,複数の環境でバージョンを統一する)
  • Effective Python 第2版』(項目83 隔離された複製可能な依存関係のために仮想環境を使う)

でも紹介されていました。

requirements.txtは手で作る!

今回確認した範囲では、以下のやり方がありました。

  • 手で作ったrequirements.txtをもとにpip freeze
  • pip freezeを勧めない立場(少数派)

pip freeze > requirements.lock

methaneさん(稲田さん。Pythonのコミッター)の記事を参照します:

(1) requirements.txtを記載

Flask

(2) 開発環境でpip install -r requirements.txt
(3) 開発環境と同じ環境を再現する(本番環境やCI環境)

  • pip freeze > requirements.lock
  • pip install -r requirements.lockで再現できる

requirements.lockは、定期的に新しい仮想環境でpip install -r requirements.txtをして更新します。

今回確認したPython書籍では以下で紹介されています。

「稲田さん、渋川さん、rhoboroさんと、Pythonに詳しい方はrequirements.lockを使うのかな」という感想を持ちました。
形から入る(=できる人の真似をする)スタイルの私は自分で開発するときは、pip freeze > requirements.txtよりもこちらの作法を推しています

pip freezeは使わない

エキスパートPythonプログラミング 改訂3版』の筆者の立場は独特でした。

pip freezeコマンドを使うと、現在の環境のすべてのパッケージを表示できますが、暗黙的に使うべきではありません。(Kindle の位置No.1199-1200)

全て読み通せてはいないのですが、pip freezeの出力を使って環境の再現は行わないという主張みたい(?)です(完全に同じ環境を再現しなくてもいいということなのかな?)。

requirements.inという方法もある(pip-toolsを使う)

いつか素振りしなきゃと積んでいた情報をこのたび評価しました。
pip-toolsを使います。
requirements.in手で作り、それをpip-compileしてrequirements.txt生成するようです。

上記のブログの概要を箇条書きで紹介します。

  • Pythonプロジェクトの依存(dependency)には、directtransitive5がある
  • 例:Flaskを使うPythonプロジェクト
  • pip freeze > requirements.txtする
  • さて、Dependabotを使って、依存ライブラリを最新化したいとする
    • Dependabotはrequirements.txtを見て、direct dependencyにもtransitive dependencyにもプルリクエストを作る
    • transitive dependencyのバージョンを上げた際にインストールでエラーが発生(その時点でdirect dependencyが未対応)
    • Dependabotはdirect dependencyだけを見てほしい
  • そこで、pip-tools
    • 開発者はrequirements.inにdirect dependencyだけを書く
    • pip-compileで、transitive dependencyも加えたrequirements.txtを生成
    • requirements.inをバージョン管理し、requirements.txtはバージョン管理しない
    • Dependabotはrequirements.inを見るので解決した(と理解した)

実際のファイルの内容などは、ぜひ上記ブログをご確認ください。

終わりに

Pythonプロジェクトの環境の再現方法の細かい点「pip freezeの出力を保存するファイル名」について、どんなやり方があるか見てきました。

思うに、Pythonのライブラリの管理方法って、簡単かつ正しい方法6がないんじゃないですかね(そういう意味で混乱と言えるかもしれません)7
簡単な方法はpip freeze > requirements.txtだと思います。
ただそれだと「direct dependencyだけを管理したい」というニーズはまかなえず、正しい方法としては伸びしろがあるんじゃないでしょうか。
正しい方法としてここで紹介したいくつかの方法が提案されているのだと思いますが、これらはpip freeze > requirements.txtほど簡単ではないので、それが普及のネックとなっているのかもしれません。

もしかするとnikkie的にはPoetryやPipenvを試すタイミングなのかもしれないですね。
ツールがファイルも管理してくれるので、「環境を再現する役割のファイルをどんな名前にするか」の追究自体が不要になりそうです。

P.S. yamoryへのリクエス

requirements.txtに開発者がdirect dependencyだけを書くケースがあるので、スキャン時にrequirements.txt以外のファイルを指定できるとめちゃめちゃありがたいです!
脆弱性をスキャンする目的ではdirect dependencyだけを書いたrequirements.txtでは不足しており、transitive dependencyも含めたrequirements.lockが目的に適うと考えています。 (requirements.txtをバージョン管理している前提であれば、yamoryでスキャンするときだけpip freeze > requirements.txtrequirements.lockと同じ内容にして対処できるかもしれません)

pip-tools方式は、direct dependencyだけを管理したい、かつ、yamory用にrequirements.txtも生成したいを満たせそうですね。


  1. PoetryやPipenvのように出力ファイルがツール側で管理されていたら、この記事を書くきっかけの疑問は生まれなかったでしょう
  2. この点は引っかかりましたが、ツールとしてはかなり有用そうという印象です
  3. Python実践レシピ』は1.1.4でpipの-cオプションを紹介していました。-cオプションは「世の中の殆どの Python 製アプリの開発では使う必要がないでしょう」という稲田さんのQiita記事(このエントリでも紹介)に納得した私は、取り上げたかった理由が何かあるんだと思いますが、試験の教科書として多くの人が読む本ではむしろ取り上げない方がよかったのでは、と思いました
  4. アーカイブもあります
  5. ウィズダム英和辞典を引くと「他動(詞)の」とありました。Oxford Learner's Dictionariesに「used with a direct object」とあり、directと一緒に使われるがしっくり来ました(該当する訳語は浮かばないのですが)
  6. このブログでたびたび登場する「正しい使い方を簡単に、誤った使い方を困難に」の話題です
  7. (宿題)今回の調査で知ったPEP 621は簡単かつ正しい方法の提案になるかもしれず、どこかで確認したいな〜と思います