uv とは
Python 用のツール uv は、超高速なプロジェクト&パッケージマネージャです。
パッケージマネージャと説明されていることが多いですが、どちらかというと Python プロジェクトを総合的に管理するツールです(単なる pip
の置き換えではないです)。
uv
は Rust で実装されており、従来の pip
コマンドを使うよりも数十倍高速だと言われています。
提供元の Astral 社 は、Python 用の高速なツールチェインを作成することをミッションとしており、Python 用の超高速 Linter & Formatter である Ruff を提供している会社でもあります。
uv
の特徴
- Rust で実装されていてとにかく高速
- インストールが簡単で使い方も簡単
- もちろんマルチプラットフォーム対応 (macOS/Linux/Windows)
- モダンな
pyproject.toml
ベースのプロジェクト管理が可能uv
があれば、Poetry
やRye
は必要ありません。uv
はRye
の後継という位置づけとのこと。
python
コマンド自体のバージョン切り替えが可能uv
があれば、pyenv
やpy
は必要ありません。
- 意識せずに仮想環境を扱える
python
コマンドの代わりにuv run
を使うだけで仮想環境 (.venv
) を作って入って依存解決して実行して抜けてくれます。python -m venv .venv
で仮想環境を作ったり、source .venv/bin/activate
で仮想環境に入ったりする必要はありません。
- 様々な CI 環境での実行が考慮されている
- 例えば GitHub Actions 用の
astral-sh/setup-uv
アクションが公式に用意されていて安心です。
- 例えば GitHub Actions 用の
- その他の特徴は、公式ページを参照
高速かつ全部入りで使いやすいので、2024 年時点の Python プロジェクト管理ツールとしては最も期待が持てそうです。
特に、venv
などの仮想環境を意識せずに Python プログラムを 1 コマンドで実行できるのはとても快適です。
若干 Rust の cargo
コマンドを意識しすぎな感じもありますが、Astral が Rust ラブなのでしょうがないです(別に悪いことではないです)。
uv のインストール
uv
のインストール方法は公式ドキュメントに書かれている通りですが、基本的にはどの環境でも 1 行のコマンドを実行するだけです。
事前に Python がインストールされている必要はありません。
インストール
$ # macOS/Linux の場合
$ curl -LsSf https://astral.sh/uv/install.sh | sh
$ # macOS (Homebrew) の場合
$ brew install uv
$ # Windows の場合
$ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
uv
コマンドが実行できるようになれば成功です。
$ uv version
uv 0.4.8 (956cadd1a 2024-09-09)
uv
コマンド(および uvx
コマンド)は ~/.cargo/bin
以下にインストールされます。
Rust 製のコマンドラインツールはこのディレクトリにインストールされることが決まっています。
1 ファイル(uvx
を入れると 2 ファイル)がインストールされるだけなので、とてもシンプルです。
アンインストールは ~/.cargo/bin/uv
を削除するだけです。バージョンアップ
インストール済みの uv
コマンドをバージョン更新するには次のようにします。
コマンド自身にアップデートの仕組みが入っていると楽ですね。
$ uv self update
シェルの補完機能を有効化
bash/zsh/Powershell などのコマンドラインで入力補完機能 (shell autocompletion) を有効化しておくと便利です。
Python プロジェクトを作成する (uv init)
pyproject.toml
ベースの Python プロジェクトを作成するには、uv init <アプリ名>
コマンドを使用します。
$ uv init myapp
Initialized project `myapp` at `/Users/maku/myapp`
myapp
ディレクトリが作成され、次のようなファイルが自動生成されます。
myapp/
- .python-version # uv run コマンド(後述)で使う python のバージョン
- README.md # 空っぽの README
- hello.py # Hello World プログラム
- pyproject.toml # プロジェクトの config ファイル
生成される pyproject.toml
の内容は次のような感じになっています。
[project]
name = "myapp"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
プロジェクト内で Python スクリプトを実行するときは、基本的に python
コマンドの代わりに uv run
コマンドを使用します。
これにより、自動的にプロジェクト用の仮想環境 (.venv
) に入り、依存関係を解決し、uv
管轄の python
コマンドでスクリプトを実行してくれます。
$ uv run hello.py
Using Python 3.12.0 interpreter at: /Users/maku/.pyenv/versions/3.12.0/bin/python3.12
Creating virtualenv at: .venv
Hello from myapp!
プロジェクト内で最初に uv run
コマンドを実行したときは、上記のように自動的に仮想環境用のディレクトリ (.venv
) が作成されます。
このメッセージを抑制したいときは -q
オプションを指定してください。
プログラムの実行が完了すると、自動的に仮想環境から抜けた状態に戻ります。
つまり、uv run
を使うと、仮想環境内で実行しているということすら意識しなくて済むようになります。
uv run
は cargo run
に相当するコマンドであると考えると分かりやすいです。
最初は python
コマンドで実行しないことに違和感があるかもしれませんが、uv run
を使うことで多くの恩恵を得られます。依存パッケージの管理 (uv add/remove)
uv
管理下の Python プロジェクトで外部パッケージへの依存を追加するときは、pip install/uninstall
の代わりに uv add/remove
を使用します。
ここでは、例として toml
パッケージをインストールしてみます。
$ uv add toml
これにより、仮想環境 (.venv
) に toml
パッケージがインストールされ、pyproject.toml
の dependencies
に依存情報が追記されます。
dependencies = [
"toml>=0.10.2",
]
既存の hello.py
ファイルを編集して、toml
パッケージを使うコードに書き換えます。
次の例では、pyproject.toml
ファイルの内容をパースして辞書オブジェクト (dict
) として取得しています。
import toml
def main():
# Load the configuration file and convert it into a dict
config = toml.load("pyproject.toml")
print(config)
if __name__ == "__main__":
main()
実行してみます。
$ uv run hello.py
{'project': {'name': 'myapp', 'version': '0.1.0', 'description': 'Add your description here', 'readme': 'README.md', 'requires-python': '>=3.12', 'dependencies': ['toml>=0.10.2']}}
うまく動きました! ٩(๑❛ᴗ❛๑)۶ わーぃ
ちなみに、他の PC 上でこのプログラムを実行したいときは、プロジェクトのディレクトリに移動して、おもむろに uv run hello.py
とするだけで実行できます。
その裏では、uv が (1) 仮想環境の生成、(2) 仮想環境に入る、(3) 依存パッケージのインストール、(4) プログラムの実行、(5) 仮想環境を抜ける、ということを自動でやってくれています。
とっても楽ですね!
(応用)Git にどのファイルをコミットするか?
下記のファイルにはプロジェクトの実行環境の情報が含まれているので Git にコミットします。
.python-version
… python コマンド自体のバージョンpyproject.toml
… プロジェクトの情報(依存パッケージなど)uv.lock
… 依存パッケージのより詳細なバージョン情報
一方、仮想環境ディレクトリ (.venv
) はコミットしないように、.gitignore
に次のように記述しておきます。
# Ignore Python virtual environment directory
.venv/
(応用)uv.lock ファイルとは?
uv.lock
ファイルには、pyproject.toml
の dependencies
で表現しきれない 詳細なパッケージ依存情報 が記述されています (Node.js プロジェクトにおける、packages-lock.json
や yaml.lock
のようなものです)。
例えば、pyproject.toml
に toml>=0.10.2
と記述されている場合、toml
パッケージのバージョンは 0.10.2
以上であれば何でもよいということを示していますが、それだけでは実行環境の再現性がなくなってしまうので、uv.lock
に実際に使用する具体的なバージョンが記述されます。
version = 1
requires-python = ">=3.12"
[[package]]
name = "myapp"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "toml" },
]
[package.metadata]
requires-dist = [{ name = "toml", specifier = ">=0.10.2" }]
[[package]]
name = "toml"
version = "0.10.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 },
]
uv.lock
ファイルは、uv add
コマンドで依存パッケージを追加したときに、pyproject.toml
と一緒に更新されます。
uv run
によるプログラム実行時に更新されることもありますが、それは uv.lock
に記述されたバージョンが、pyproject.toml
で指定されたバージョン条件を満たしていない場合に限られます(プログラムを実行するたびにパッケージが更新されると大変なので)。
例えば、uv.lock
にバージョン 0.10.2
と書かれていて、pyproject.toml
に >=0.10.1
と書かれている場合は、0.10.2 >= 0.10.1
で条件を満たすので uv.lock
は更新されないし、新しいパッケージがインストールされることもありません。
明示的に最新のパッケージをインストールして uv.lock
ファイルを更新するには、次のように uv lock
コマンドを使用します。
$ uv lock --upgrade # 全パッケージを更新する場合
$ uv lock --upgrade-package toml # パッケージを指定して更新する場合
これにより、最新のパッケージを使用できるようになりますが、これはあくまで uv.lock
ファイルの更新なので、pyproject.toml
で指定している >=0.10.1
などの情報は更新されないことに注意してください。
pyproject.toml
側の dependencies
情報を更新したいときは、uv add toml
(あるいは明示的に uv add toml>=0.10.2
)などのコマンドを実行してください。
uv.lock
ファイルを作成しています。(応用)uv で Ruff などのツールをインストールする
Python 用の高速 Linter & Formatter である Ruff も uv
でインストールできます。
Ruff は開発時に使用するツールなので、uv add --dev
で dev 環境用の依存パッケージとしてインストールします。
$ uv add --dev ruff
pyproject.toml
の dev-dependencies
に次のように追記されます。
[project]
# ...プロジェクトの設定...
[tool.uv]
dev-dependencies = [
"ruff>=0.6.5",
]
git clone
した直後はこれらの依存パッケージはインストールされていません。
dev
環境用の依存パッケージをまとめてインストールするには、uv sync --dev
コマンドを使用します(uv run
や uv sync
では dev 環境用の依存パッケージはインストールされません)。
これは、GitHub Actions などの CI 環境上でテストツールをインストールするときにも使用します。インストールしたコマンドを実行するには、uv run
コマンドを使用します。
$ uv run ruff check # Lint チェック
$ uv run ruff format --check # フォーマットチェック
$ uv run ruff format # 自動フォーマット
エディタとして Visual Studio Code を使っているのであれば、Ruff 拡張を入れてしまうのが手っ取り早かったりします。
(応用)GitHub Actions のワークフローに組み込む
uv
によるパッケージのインストールは高速なので、GitHub Actions などの CI 環境でも uv
を使うとよいです。
- 本家ドキュメント: Using uv in GitHub Actions
- astral-sh/setup-uv アクション: Python setup uv · Actions · GitHub Marketplace
基本的な流れは次のようになります。
astral-sh/setup-uv
アクションを使ってuv
をインストールuv python install
でpython
をインストールuv sync --dev
で dev 環境用の依存パッケージをインストールuv run pytest tests
など任意のテストツールや Lint を実行