はじめに
1日目、nikkieです。
Pythonの依存管理ツールとして名前だけ聞いていたpip-toolsを触りました。
目次
pip-toolsとは
2つのコマンドからなります。
pip-tools = pip-compile + pip-sync
pip install pip-tools
でインストールできます。
インストールするとコマンド2つとも使えます1
前回のpip-tools
過去記事にちょっとだけ関係しています
requirements.inというファイル
requirements.txtについて文献調査した記事でpip-toolsが登場しています。
直接の依存(例:Flask==2.2.2
)をrequirements.inに書き、pip-compile
。
生成されるrequirements.txtには、直接の依存が依存するライブラリ(他動 transitive の依存)も具体的なバージョン込みで記載されます。
これでpip install -r requirements.txt
しました。
今回は上の記事にないpip-sync
も触っていきます
Ryeが使っている
先日素振りしたRye
https://rye-up.com/guide/sync/ によると
Rye currently uses pip-tools to download and install dependencies.
現在(v1.16.0)ではpip-toolsを使っている、とのことです。
pip-toolsの使い方:2つのコマンドはどう使う?
使い方はリポジトリの図に示されています。
https://github.com/jazzband/pip-tools/blob/7.3.0/img/pip-tools-overview.svg より引用
- まず
pip-compile
。requirements.txtを生成 pip-sync
で環境をrequirements.txtと同期する
pip-compile
requirements.txtを生成します。
pip-toolsを使わない場合、必要なパッケージをpip install
していき最後にpip freeze
して、バージョン込みでパッケージ情報を残すと思います。
それに対してpip-toolsでは必要なパッケージのバージョン解決だけを行って2、先にパッケージ情報のファイル(requirements.txt)ができます
入力するファイルは幅広くサポート
- pyproject.toml
- setup.cfg
- setup.py
- requirements.in
requirements.in を作らなくても、Pythonプロジェクトに置いてあるパッケージのメタデータファイルと一緒に使い出せます。
dependencies(pyproject.toml)やinstall_requires(setup.py)を見てrequirements.txtを生成してくれます
パッケージの依存にはoptional-dependencies3(extras_require)もありますよね。
extraごとのcompileもサポートしています4。
コマンド例:pip-compile --extra dev --output-file dev-requirements.txt
生成されるrequirement.txtはリポジトリにコミット推奨。
後述するpip-syncで見事に環境が再現させられるので、これは納得でした。
https://github.com/jazzband/pip-tools/tree/7.3.0#should-i-commit-requirementsin-and-requirementstxt-to-source-control
If you want a reproducible environment installation available from your source control, then yes, you should commit both requirements.in and requirements.txt to source control.
すでにrequirements.txtをcompileしていて、(全部ではなくて)特定の依存ライブラリのバージョンだけを上げたい場合は、--upgrade-package
を指定。
https://github.com/jazzband/pip-tools/tree/7.3.0#updating-requirements
この引数は複数回指定できます。
全部一度に上げたら一発ではうまくいかないかもしれませんが、1つずつ上げていけるのは便利そうでした
コマンドの引数についてはこちらにまとまっています:
https://pip-tools.readthedocs.io/en/stable/cli/pip-compile/
pip-sync
requirements.txtがある前提で、環境(仮想環境含む)を同期します。
これはPythonを使ったこれまでの開発で体験したことのない衝撃でした。
環境のパッケージをインストール・アンインストールが走って、まじで一致するんですよ!
pip-sync
には複数のrequirements.txtが渡せます
コマンドの引数についてはこちらにまとまっています:
https://pip-tools.readthedocs.io/en/stable/cli/pip-sync/
自作ライブラリに導入して手を動かす
OCRツールをまとめたライブラリを少しだけ開発しました(時期的にお正月ハッカソン?)
pyproject.tomlはこんな感じです5。
[project.optional-dependencies] google = ["google-cloud-vision"] tesseract = ["pytesseract"] dev = [ "taskipy", # 長いので省略します ]
同じ階層にsetup.pyがあります。
. ├── ocroy ├── pyproject.toml ├── setup.py ├── tests └── venv
(雰囲気が伝わるように抜粋して示しました)
新たに仮想環境を切り、pip-toolsを導入して素振りします6。
% python -m venv piptools_env --upgrade-deps % source piptools_env/bin/activate
以降はpiptools_env仮想環境です
% python -V Python 3.11.4 % pip install pip-tools % pip freeze build==1.0.3 click==8.1.7 packaging==23.2 pip-tools==7.3.0 pyproject_hooks==1.0.0
extraごとにcompile
pip-compile
でsetup.pyやpyproject.tomlに基づいてcompileされますが、今回dependenciesの定義がないので空ファイルです。
extraを指定していきます。
% pip-compile --extra google --output-file google-requirements.txt % pip-compile --extra tesseract --output-file tesseract-requirements.txt % pip-compile --extra dev --output-file dev-requirements.txt
仮想環境を同期
生成したrequirements.txt全部と同期させましょう。
% pip-sync *-requirements.txt
追加でインストールではなく、同期します。
ただし、pip-toolsは残ります。
pyproject.tomlのdevのextraにpip-toolsを書いておくのがよさそうな気がしますね7。
自作ライブラリに必要な依存関係はバッチリ管理できるようになったと思います!
宿題事項
- 一部の依存のバージョンを上げて同期するフローは、一定期間使ってみて体験したい
- editable install、どうやるんだろう
- 自作ライブラリの依存管理はバッチリだが、上の手順では自作ライブラリ自体をインストールできていない(追加で
pip install -e .
?) - Ryeによると、requirements.txtに
-e file:.
と書くらしい8 - これは
pip-compile
では書かれないのかな
- 自作ライブラリの依存管理はバッチリだが、上の手順では自作ライブラリ自体をインストールできていない(追加で
- extraが複数あるとき、それぞれのrequirements.txtをどう管理するのがいいんだろう
- ディレクトリを作って、そこに全部置く?
- 今回はdependenciesがなかったが、これがあった場合、全部のrequirements.txtに書かれるのかな
- 重複っぽい印象を受けるけれど、そういうものなのかな
終わりに
pip-toolsを触りました。
自作ライブラリのextraごとに依存ライブラリのバージョンを記載したrequirements.txtを生成し、全部渡して同期しました。
同期の感触はなかなかよいです。
ライブラリを開発するPython使い9にはなかなかいいツールなのではと第一印象はよいです(宿題があるので判断は変わるかもですが)。
ちょっとした素振りでは判断しきれないところもあるので、一定期間試してみて判断したいですね
-
python -m piptools compile
やpython -m piptools sync
のようにも使えます。ref: https://github.com/jazzband/pip-tools/tree/7.3.0#example-usage-for-pip-compile↩ - ドキュメントを読んだ上でやや想像が入っているので、ソースコードを読んでみたら正しくないかもしれません↩
- https://packaging.python.org/ja/latest/guides/writing-pyproject-toml/#dependencies-optional-dependencies↩
- https://github.com/jazzband/pip-tools/tree/7.3.0#requirements-from-pyprojecttoml↩
- https://github.com/ftnext/ocroy/blob/7bc66efa5a870fdfa09a06659096bf2873c60c3d/pyproject.toml#L32-L48↩
-
pyproject.tomlに
tool.setuptools.packages.find
の指定が必要でした。piptools_envディレクトリが増えて、setuptoolsのAutomatic discoveryでパッケージのディレクトリを1つに決められないためという理解です↩ - 書いている例 How to Publish an Open-Source Python Package to PyPI – Real Python↩
- https://github.com/jazzband/pip-tools/issues/1968#issuecomment-1684251342 が見つかりました↩
- ライブラリを開発しない場合は、pip-toolsはtoo muchだと思います。ライブラリを開発するようになって初めて同期するありがたさなど分かるんじゃないでしょうか↩