🐍
uv tool install でインストールしたツールは独立したvenvで実行される
便利さと実装方法に感動したので書く。
uvとは
Pythonのバージョン+依存ライブラリ管理ツール。もはや説明不要かもしれない。Rustで書かれているので「Pythonの管理ツールをPythonで動かす場合、そのツールを動かすPythonのバージョンはいくつになるの?」みたいなニワトリ卵問題は発生しない。
最強なので使ったことの無い人は本当に使ってみてほしい。pyenv
やpipenv
やpoetry
に苦しめられた経験のある人は特に。
uv tool installとは
Pythonで書かれたツールをインストールするためのコマンド。公式ドキュメントに
in which case their dependencies are installed in a temporary virtual environment isolated from the current project.
とあるように、ツールごとに独立したvenv
が作られて管理される。
自作ツールを作った時の記事でも書いたんですが、私はpip
でCLIツールを入れてシステムのPythonで依存ライブラリ地獄が始まるのが好きではないので、この機能は非常に助かります。
試してみる
pip
のテストといえばお馴染みの牛を呼んでみる。
> uv tool install pycowsay
Resolved 1 package in 346ms
Installed 1 package in 14ms
+ pycowsay==0.0.0.2
Installed 1 executable: pycowsay
> source ~/.zshrc
> pycowsay "Hello World"
-----------
< Hello World >
-----------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ちなみに
> pip3 list
Package Version
---------- -------
altgraph 0.17.2
crcmod 1.7
future 0.18.2
macholib 1.15.2
pip 24.3.1
setuptools 58.0.4
six 1.15.0
wheel 0.37.0
> uv run pip list
Package Version
---------- -------
pip 24.3.1
setuptools 75.6.0
と表示されるので、確かに他のPython環境にはpycowsay
はインストールされていないことがわかる。すごい。
どうやって実現しているのか
uv tool run pycowsay
みたいな感じで実行するのであれば独立した環境で実行されるのもわかるんですが、PATH
が通って直接実行した場合も別環境で実行されるのが不思議でならなかったので、仕組みを調べてみることに。
> cat $(which pycowsay)
#!/Users/hakusai/.local/share/uv/tools/pycowsay/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from pycowsay.main import main
if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(main())
注目すべきはshebang
#!/Users/hakusai/.local/share/uv/tools/pycowsay/bin/python
なるほど、ツールごとにvenv
を作った上で、そのPythonのパスをshebang
で指定することによって、個別の環境で実行されるようにしているらしい。
uvのソースでいうとこのへん。
ツールごとに使うPythonのパスを判定して、shebang
として書き込んでいることがわかる。
ちなみに
ソースのコメントにもある通り、この実行ファイルの書き込み処理はpipを元にしているらしい。これってpipが書いてくれてたんだ。知らなかった。
Discussion