Python パッケージング #4 uv — 2026 年の標準ワークフロー

読了 5分

ここまで手作業でやってきたことを並べてみます。venv を作る、activate する、pip install する、バージョンを記録する、Python のバージョンは別で管理する。1 つの道具がこの全部を引き受けるとどうなるか、が今回です。uv は 2026 年現在、Python プロジェクト管理の事実上の標準ワークフローになった道具です。 ruff を作った Astral が Rust で開発し、2025 年の 1.0 リリースを経て、月間ダウンロードで既存の道具たちを追い越しました。このブログのテスト自動化シリーズやモダン Python 基礎 #1が最初から uv を前提にしていた理由でもあります。速度(ロックファイルからのインストールで既存ツールの数倍〜十数倍)が有名ですが、実務での価値は速度よりワークフローが 1 つに畳み込まれることにあります。

インストールと最初のプロジェクト — uv init #

uv 自体は Python と無関係な単一の実行ファイルなので、インストールはシンプルです。

インストール
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# または
brew install uv

新しいプロジェクトは init から始めます。

プロジェクト作成
uv init invoice-tool
cd invoice-tool

作られるものは控えめです。#3 で見た pyproject.toml、Python バージョンを固定する .python-version、あとは main.py と README くらいです。.venv はまだありません。必要になった瞬間に uv が作るからです。

add と sync — 宣言と環境を常に一致させます #

依存関係の追加は add です。

依存関係の追加
uv add django
uv add --dev pytest ruff

この 1 行の裏で起きることが、前回までのまとめになっています。pyproject.toml の dependencies(または dependency-groups の dev)に宣言が記録され、依存関係全体が解決されて uv.lock にロックされ.venv がなければ作られたうえでインストールまで終わります。 宣言(#3)、ロック(#5)、環境(#1)がコマンド 1 つで同期されるのです。削除は uv remove django で対称です。

同僚のリポジトリをクローンしたときは sync 1 つです。

環境の再現
git clone .../invoice-tool && cd invoice-tool
uv sync

ロックファイルのとおりに .venv が再現されます。README に「Python X.Y をインストールして、venv を作って、activate して、pip install -r…」といった手順書が要らなくなります。

run — activate の要らない実行 #

#1 で activate の正体が PATH の操作にすぎないことを見ました。uv はその段階ごと省略させてくれます。

実行
uv run python main.py
uv run pytest

uv run はプロジェクトの .venv を見つけて(なければ作り、ロックとずれていれば合わせたうえで)、その環境でコマンドを実行します。「activate を忘れて別の環境にインストールしてしまった」という事故の類型が、構造的に消えます。テストシリーズで uv run pytest を基本の実行方法にしていた背景です。

Python のバージョンまで uv が管理します #

uv の管理範囲はパッケージで終わりません。Python インタープリター自体をダウンロードして管理します。

Python バージョン管理
uv python install 3.13     # インタープリターのインストール
uv python pin 3.13         # プロジェクトに固定 (.python-version に記録)
uv python list             # インストール済み・利用可能なバージョンの一覧

.python-version があれば uv のすべてのコマンドがそのバージョンで動き、ないバージョンなら自動で取ってきます。pyenv が担っていた役割まで吸収した形で、「Python バージョン管理ツール + 仮想環境ツール + パッケージマネージャー」の 3 点セットが 1 つに減ります。システム Python に触らないという #1 の原則も自然に守られます。

uvx と uv tool — グローバルツールの正しい置き場所 #

#1 で「システム Python には何もインストールしない。グローバルなツールには別の方法がある」と先送りした答えがここです。

グローバルツールの実行・インストール
uvx ruff check .            # インストールなしの一回限りの実行
uv tool install ruff        # グローバルツールとしてインストール (分離環境に)

uvx はツールを一時的な分離環境でそのまま実行し、uv tool install はよく使うツールをツールごとの分離環境にインストールして PATH に公開します。どちらでも、プロジェクトの環境もシステム Python も汚れません。自動化 #7 で作った CLI のような自作ツールも、同じ方式でインストールできます。

既存プロジェクトのマイグレーション #

requirements.txt で回っていたプロジェクトの移行は、吸収に近い作業です。

マイグレーション
uv init --bare              # 既存コードに pyproject.toml だけ追加
uv add -r requirements.txt  # 一覧を dependencies に吸収
uv add --dev -r requirements-dev.txt

#2 で見た freeze 型の一覧なら、この機会に直接依存だけを残して整理するのがよいです。推移的依存はロックが管理してくれるので、宣言に残しておく理由がありません。逆に、uv を導入できない環境向けに書き出すこともできます(uv export --format requirements.txt)。また pip のコマンド体系に慣れた手のために uv pip install のような低レベルインターフェースも用意されていますが、プロジェクト管理では add/sync のほうが宣言・ロック・環境を一緒に合わせてくれる正式なやり方です。

まとめ #

今回扱った内容です。

  • uv は仮想環境、パッケージのインストール、ロック、Python バージョン管理を 1 つにまとめた道具で、2026 年現在、新規プロジェクトの事実上の標準です
  • uv init で始め、uv add/uv remove が宣言(pyproject.toml)・ロック(uv.lock)・環境(.venv)を一度に同期します
  • クローン後は uv sync 1 つで環境が再現され、uv run は activate なしでプロジェクトの環境でコマンドを実行します
  • uv python install/pin でインタープリターまで uv が管理します。pyenv なしで .python-version によってチームの Python バージョンが固定されます
  • グローバルツールは uvx(一回限り)と uv tool install(常用)で分離インストールします。システム Python は最後まで触りません
  • 既存プロジェクトは uv add -r requirements.txt で吸収し、freeze 型の一覧は直接依存だけを残して整理します

次回(#5 依存関係のロック)では、add の裏で静かに作られていた uv.lock の中身を直接読みます。 ロックが再現性を保証する仕組み、アップグレードの戦略、そして道具の間をつなぐ標準 pylock.toml(PEP 751)まで扱います。

X