本記事は、PyCon Kyushu 2024 KAGOSHIMA 発表の補足記事です
目次
Ryeとpip-tools
作者のmitsuhiko氏からAstral社1に引き継がれたRye。
Rust製のPythonパッケージ管理ツール「uv」を使ってみよう | gihyo.jp
執筆時点の最新版は0.34.0。
https://github.com/astral-sh/rye/releases/tag/0.34.0
pip-toolsよりもuv2が優先して使われるようになったようです。
https://github.com/astral-sh/rye/blob/0.34.0/docs/guide/sync.md#syncing-and-locking
It currently defaults to
uv
as it offers significantly better performance, but will offer you the option to usepip-tools
instead.
数ヶ月前(v0.16.0)にpip-toolsを使ったRyeのLockやSyncの実装を見ていました。
その中で魔法のようなことをやっていることが分かってきました(ちょうぜつ...!)
rye sync
でプロジェクトにできる仮想環境にはpip
はない(開発者には触らせないらしい)- その仮想環境にRyeはrequirements.txtに沿ってインストールできる
この魔法の仕組みについて理解したことを綴ります。
なお、uvが優先されていますが、rye config --set-bool behavior.use-uv=false
3でpip-toolsを使うように設定できます。
関連エントリ
この2本の内容を前提にします。伏線だったのですよ...
pipのない仮想環境にpip-syncする仕組み
ソースコードを読んで、方法を理解するのを目的とします。
短いrequirements.txtを用意しました。
kojo-fan-art==0.1.1
pipのない仮想環境(プロジェクト)
Ryeはvirtualenvに--no-seed
を指定して、プロジェクトにpipのない仮想環境を作ります。
https://github.com/astral-sh/rye/blob/0.34.0/rye/src/sync.rs#L369
理解するための実験としては、2つの仮想環境を用意します。
% python -V Python 3.11.8 % python -m venv .venv/no_pip --without-pip % python -m venv .venv/piptools --upgrade-deps
. └── .venv/ ├── piptools/ # pipのある仮想環境。この後pip-toolsも入れる └── no_pip/ # pipのない仮想環境
% .venv/piptools/bin/python -m pip list <省略しますができます> % .venv/no_pip/bin/python -m pip list /.../.venv/no_pip/bin/python: No module named pip
プロジェクト外にあるRyeの仮想環境
いくつかありますが、今回は2つ紹介します
(macOSでrye pin 3.12
しています)
~/.rye/self/lib/python3.12/site-packages/
- Rye自身の仮想環境のようです
- virtualenvはここにありました
- Ruffなども入っています
- pip-toolsはありません
~/.rye/pip-tools/cpython@3.12/lib/python3.12/site-packages/
- pip-toolsはこちらでした
- (ちなみにuvは
~/.rye/uv/0.1.44/uv
に置かれているようです)
Ryeに合わせて、pip-toolsの入った仮想環境を作ります
% .venv/piptools/bin/python -m pip install pip-tools
シンボリックリンク+PYTHONPATHでpip-syncがpipを見つけられるようにしている
以前の記事で確認したように、PYTHONPATH環境変数を使えばpipのない仮想環境にもパッケージをインストールできます。
pipのない仮想環境でpip-sync
できる一要素はPYTHONPATH
です。
ですが、他にも要素があります。
pip-toolsの仮想環境から一時ディレクトリにpipだけシンボリックリンクしています。
https://github.com/astral-sh/rye/blob/0.34.0/rye/src/sync.rs#L270-L274
これを再現すると
% mkdir temp # 一時ディレクトリの代わりに作成 % ln -s $PWD/.venv/piptools/lib/python3.11/site-packages/pip $PWD/temp/pip % ls temp pip
このとき
% PYTHONPATH=$PWD/temp .venv/no_pip/bin/python -m pip list
何も表示されません。
ですが、これで正常動作なんです
% echo $? 0 % ls .venv/no_pip/lib/python3.11/site-packages # 空ディレクトリ
ここからpip-sync
できちゃうんですね!
% .venv/piptools/bin/pip-sync --python-executable .venv/no_pip/bin/python Traceback (most recent call last): File "<string>", line 1, in <module> ModuleNotFoundError: No module named 'pip' subprocess.CalledProcessError: Command '('.venv/no_pip/bin/python', '-c', 'import pip;print(pip.__version__)')' returned non-zero exit status 1. % PYTHONPATH=$PWD/temp .venv/piptools/bin/pip-sync --python-executable .venv/no_pip/bin/python
PYTHONPATHでpipの実体のソースコードが見つかるので、「No module named 'pip'」とはならずpip-sync
できたと理解しました。
% PYTHONPATH=$PWD/temp .venv/no_pip/bin/python -m pip list Package Version ------------ ------- kojo-fan-art 0.1.1 % ls .venv/no_pip/lib/python3.11/site-packages kojo_fan_art-0.1.1.dist-info the_solitary_castle_in_the_mirror
実際のコマンドは、pip-sync
の--pip-args
に--no-deps
を渡しています。
https://github.com/astral-sh/rye/blob/0.34.0/rye/src/sync.rs#L277-L284
--pip-args PIP_ARGS Arbitrary pip arguments to pass directly to pip install/upgrade commands
pip install --no-deps
https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-no-deps
site-packages下のpipだけシンボリックリンクを作る意図がピンとこなかったのですが、
PYTHONPATH=$PWD/temp .venv/no_pip/bin/python -m pip list
の出力が空だったのを見て、「pipのない仮想環境で最小限でpip-syncを動かせている」と分からされました(技術力で殴られた)。
lock (pip-compile
)側は見られていませんが、余計な依存をlockファイルに書かないようにするための工夫とも言えそうです。
比較のため.venv/piptools
のsite-packagesを渡すと、めちゃめちゃ依存が表示されます(pip-sync
の前にやりました)
% PYTHONPATH=$PWD/.venv/piptools/lib/python3.11/site-packages .venv/no_pip/bin/python -m pip list Package Version --------------- ------- build 1.2.1 click 8.1.7 packaging 24.0 pip 24.0 pip-tools 7.4.1 pyproject_hooks 1.1.0 setuptools 70.0.0 wheel 0.43.0
終わりに
ソースコードを見て理解したRyeの魔法「pipのない仮想環境にpip-sync
」です
- プロジェクトの仮想環境にはpipはない(開発者は触れない)
- プロジェクトの仮想環境とは別に、pip-toolsの仮想環境がある
- pip-toolsの仮想環境のうちpipのコードだけを参照できるようにして、pipのない仮想環境での
pip-sync
を実現している
uvがデフォルトとなり、この魔法は今後あまり日の目を見ないのかもしれませんが、このアイデア・技術力はめちゃめちゃすごいと思いました。