はじめに
Ryeで始めるPythonプロジェクトを執筆してから半年近く経ちました。
(よければ、まずRyeの記事から読んでいただけると嬉しいです)
この半年でuvが大きく成長し、Ryeと同等かそれ以上のプロジェクトおよびパッケージ管理ができるようになりました。そのため、今度はuvを使って開発を進めたい方向けの記事も上げてみようと思いました。
本稿はRyeの記事同様、ハンズオン形式で誰でも気軽にプロジェクトを作れるようになっています。また、Ryeとの違いについても簡単にですが、言及します。
ハンズオン環境
- Ubuntu / MacOS
- uv : 0.4.24
0. uvとは?
Rustで書かれた非常に高速なPythonパッケージとプロジェクト管理ツールです。
以前に、Ryeの記事を執筆した際には、「パッケージインストーラー(pip等)の代替」ような位置づけでuvを利用していました。実際にRyeでプロジェクト作成する際は、pipかuvでパッケージインストーラーを選ぶ形になっていました。
現在のuvはというと、下記のような機能を代替できます。
- pipenv, poetry, virtualenvなど(パッケージ、プロジェクト管理ツール)の代替
- pipの代替(10倍から100倍速い)
- pyenv(バージョン管理ツール)の代替
Rye(0.41.0)とuv(0.4.24)を簡単に機能面で比較してみると、下記のような感じになります。
項目 | uv | Rye |
---|---|---|
パッケージ管理 | pip に依存せず独自管理 | pip(uvも選択可)を利用して管理 |
プロジェクト管理 |
uv init でプロジェクト作成 |
rye init でプロジェクト作成 |
pip との互換性 | uv内のpip互換で依存関係を管理可能 | pip(uv)と連携して依存関係を管理 |
依存関係の解決 | 独自の依存関係解決システム | pip(uv)を通じて依存関係を解決 |
バージョン管理 |
uv python で管理 |
Toolchain で管理 |
バージョン固定 | pinでバージョン固定可能 | pinでバージョン固定可能 |
依存ファイル |
pyproject.toml に記載 |
pyproject.toml に記載 |
スクリプト実行 |
uv run コマンドで実行 |
rye run コマンドで実行 |
ロックファイル |
uv.lock で依存関係を固定 |
rye.lock で依存関係を固定 |
大きく差異はないですが、現状、Ryeからuvを使うのであればuvだけで良いでしょう。もし、pipを今後も利用したい場合は、Ryeを選ぶとよいでしょう。
注意点
ちなみに、uvからpipを操作することも出来ますが、「ベースとなっているツールのインターフェイスや動作を正確に実装しているわけではない」らしいので、Ryeを使う方が無難です。
(参考 : https://docs.astral.sh/uv/getting-started/features/#the-pip-interface)
# 例
uv pip install
uv pip list
1.インストール編
基本的には、公式の誘導に従います。
(Windows へのインストールは本稿では扱わないので公式見てね)
1.1. ダウンロードする
まず、curl コマンドを実行しましょう。適したバイナリファイルが落ちてきます。
curl -LsSf https://astral.sh/uv/install.sh | sh
pipからも落とすこともできますが、pip に依存する形を好まないので割愛します。Rye と異なり、 $HOME/.cargo/env ($HOME/.cargo/env.fish)
に PATH が通っていれば良いようです。
# sh, bash, zsh
source $HOME/.cargo/env
# fish
source $HOME/.cargo/env.fish
1.2. Shell の補完スクリプトを入れる
rye と同様に shell 補完スクリプトが提供されているため、ついでに入れましょう。
# bash
echo 'eval "$(uv generate-shell-completion bash)"' >> ~/.bashrc
# zsh
echo 'eval "$(uv generate-shell-completion zsh)"' >> ~/.zshrc
# fish
echo 'uv generate-shell-completion fish | source' >> ~/.config/fish/config.fish
1.3. uv 自体をアップデートする
最後に uv 自体をアップデートしましょう。
uv self update
2. Python管理編
uvでは、pyenvのように Python のバージョン管理をすることが可能です。
uvはRye同様、必要に応じて自動的にPythonのバージョンを取得するため、Pythonをインストールする必要はないですが、pyenvのように手動でインストールしたり、バージョン確認することが下記のように出来ます。
uv python install 3.13
一度に複数のバージョンをインストールすることも可能です。
(リビジョンまで指定可能です)
uv python install 3.11.7 3.12 3.13
# 結果
Searching for Python versions matching: Python 3.11.7
Searching for Python versions matching: Python 3.12
Searching for Python versions matching: Python 3.13
Installed 3 versions in 7.05s
+ cpython-3.11.7-macos-x86_64-none
+ cpython-3.12.7-macos-x86_64-none
+ cpython-3.13.0-macos-x86_64-none
インストールした Python は下記のように確認可能です。
uv python list
3. 新規プロジェクト作成編
3.1. プロジェクトを作成する
uv initを用いてプロジェクトを作成していきます。
uv init uv-sample-project
cd uv-sample-project
既にプロジェクトのフォルダがある場合は、下記でOKです。
cd uv-sample-project
uv init
そうすると、以下のようなフォルダ構成が出来上がります。
.
├── .python-version
├── README.md
├── hello.py
└── pyproject.toml
生成されたpyproject.toml
は下記のような感じになります。
[project]
name = "uv-sample-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []
Ryeとの比較
Ryeの方が下記のように、デフォルトで色々と生成してくれます。簡単に動かすという観点ではuvよりも優れているかもしれません。ただし、プロジェクトを作り込んでいく場合は、uv位シンプルでいいかなと思います。
# フォルダ構成
.
├── .git
├── .gitignore
├── .python-version
├── README.md
├── pyproject.toml
└── src
└── my_project
└── __init__.py
# pyproject.toml
[project]
name = "my-project"
version = "0.1.0"
description = "Add your description here"
authors = [
{ name = "kissy24", email = "hogehoge@fuga.com" }
]
dependencies = []
readme = "README.md"
requires-python = ">= 3.8"
[project.scripts]
hello = "my_project:hello"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.rye]
managed = true
dev-dependencies = []
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["src/my_project"]
2.2. hello worldする
まず何かパッケージを入れたいですね。とりあえずRuffを入れてみましょうか。
uv add ruff
ライブラリを追加したら必ずuv sync
しましょう…と言いたいのですが、uvはuv add
でuv.lock
と.venv
が爆速で更新される上にuv run
を起動する前に、uv.lock
とpyproject.toml
が最新であること、uv.lock
と.venv
が最新であることを爆速で確認してくれます。そのため、Ryeと異なりsyncの出番はほぼありません。(コマンドとしては存在します)
>>> uv run hello.py
Hello from uv-sample-project!
uvで無事プロジェクトを作ることができました。Ryeより考えることは少ないですね。
2.3. パッケージを開発環境にだけ入れる
おっと、Ruffは開発でしか必要の無いライブラリでしたね。
こういうときはいつも通り、devパッケージとしてインストールしましょう。
# 一旦ruffを削除します
uv remove ruff
# devパッケージとしていれます
uv add --dev ruff
# 実行用途のみで使う場合
uv sync --no-dev
はい、ちょっと待ってください。さっき「syncの出番はほぼありませんといったそばからuv sync
使ってません?」と思ったそこのあなた、センスがいいです。
uvではdevelop環境で利用するtoolをuv tool
(uvx
)というコマンド別管理することが出来ます。
(.venv
にRuffを含まず、Ruffが利用できます。保存先はuv tool dir
で確認できます)
# 一旦devパッケージからruffを削除します
uv remove --dev ruff
# toolとしてruffをいれます
uv tool install ruff
では、実際に動くのか見てみましょうか。
>>> uv tool run ruff check hello.py
All checks passed!
コマンドによるRuffのチェックが出来ました。ちなみにパッケージを削除・更新する場合は下記のコマンドより可能です。
# 更新
uv tool upgrade ruff
# 削除
uv tool uninstall ruff
更に面白いのは、ruffをインストールしなくてもuvx
コマンドでツール実行ができます。
(ちなみに uvx
は uv tool run
のエイリアスです)
>>> uvx ruff check hello.py
All checks passed!
開発時のパッケージの持ち方は、下記のように棲み分けすると良さそうですね。
- 開発時の環境に制約をかけたい場合は
uv add --dev
- ローカルにパッケージを持っておきたい場合は
uv tool install
- 特に制約のない場合は
uvx
3.3. おまけ : uvxでPytestを実行する
Ruffと同じ方法でPytestも出来るのか実際にやってみましょう。
まず、ソースコードを下記のように変更します。
def hello() -> str:
return "Hello from uv-sample-project!"
def main():
print(hello())
if __name__ == "__main__":
main()
次にテストコードを作成しましょう。(hello.py
と同階層でOKです)
import hello
def test_hello():
assert hello.hello() == "Hello from uv-sample-project!"
これで、準備ができたのでPytestしてみましょう。
>>> uvx pytest
platform linux -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0
rootdir: /home/hoge/workspace/uv-sample-project
configfile: pyproject.toml
collected 1 item
test_hello.py .
無事に動作しましたね。
次に、pytest-cov
が使えるか見ていきましょうか。
>>> uvx pytest --cov -v
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --cov
inifile: /home/hoge/workspace/uv-sample-project/pyproject.toml
rootdir: /home/hoge/workspace/uv-sample-project
pytest-cov
の--cov
オプションを認識しないので駄目みたいですね。uv tool install
が可能かも見てみましょうか。
>>> uv tool install pytest-cov
Resolved 6 packages in 392ms
Installed 6 packages in 2ms
+ coverage==7.6.4
+ iniconfig==2.0.0
+ packaging==24.1
+ pluggy==1.5.0
+ pytest==8.3.3
+ pytest-cov==5.0.0
No executables are provided by `pytest-cov`
>>> uv tool list
No tools installed
どうやら、pytest-cov
が提供する実行可能ファイルはないため、依存関係含めてインストール出来ていないようですね。
この辺りはまだこれからといった感じになりそうですね。pytest-cov
を利用するには従来通りの方法で実行します。
>>> uv add --dev pytest-cov
Resolved 8 packages in 80ms
Prepared 6 packages in 114ms
Installed 6 packages in 7ms
+ coverage==7.6.4
+ iniconfig==2.0.0
+ packaging==24.1
+ pluggy==1.5.0
+ pytest==8.3.3
+ pytest-cov==5.0.0
>>> uv run pytest --cov -v
platform linux -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0 -- /home/hoge/workspace/uv-sample-project/.venv/bin/python3
cachedir: .pytest_cache
rootdir: /home/hoge/workspace/uv-sample-project
configfile: pyproject.toml
plugins: cov-5.0.0
collected 1 item
test_hello.py::test_hello PASSED [100%]
---------- coverage: platform linux, python 3.13.0-final-0 -----------
Name Stmts Miss Cover
-----------------------------------
hello.py 6 2 67%
test_hello.py 3 0 100%
-----------------------------------
TOTAL 9 2 78%
ここまでが、ハンズオンになります。下記に、ハンズオンのコードを置いてますのでよければ確認してみてください。
おわりに
いかがでしたでしょうか?個人的にuvを触っていて思ったのは、VSCodeのPython補完とuvxの相性が良いと感じました。例えば、VSCode Ruffであれば、
Ruff v0.4.5以降、RuffにはRustで書かれた組み込み言語サーバーが搭載されています: ⚡ ruff server ⚡ このサーバーはRuff v0.5.3で安定版としてマークされ、利用可能であれば拡張機能で自動的に使用されます。 参照してください: Rustベースの言語サーバーを有効にする
(参考 : https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff)
となっており、Ruffに限らず開発者ツールはプロジェクト外(エディタや統合管理ツール)で管理されるような流れを感じます。おそらく、今後のPythonプロジェクトはRustやGoのように開発者がlinterやformatterを気にしなくても、デファクト・スタンダードのようなものが勝手についてくるようになるのではと感じています。
uvは、コードに向き合う時間を増やしてくれるような可能性を感じさせてくれました。Rye、uv問わずこの手の統合管理ツールがより盛り上がってくることを願っています。