この記事は Safie Engineers' Blog! Advent Calendar 18日目の記事です。
はじめに
こんにちは!セーフィーでサーバーサイドの開発を担当している石塚です!
セーフィーのサーバーサイドの開発では Python を使って実装することがかなり多いです。Python のプロジェクトでは当然のことながらパッケージ管理ツールを使って、プロジェクトに必要なパッケージを管理しています。普段何気なく使っているパッケージ管理ツールですが、普段は仕組みを意識して使うことがなかったので今年のアドベントカレンダーにちょうど良いかなと思い、今回のテーマとしました。
この記事ではセーフィーの Python プロジェクトでよく使われる uv を取り上げ、uv の紹介と依存関係を解決する仕組みについて書いています。(N番煎じですが…)
uv について
uv とは
uv は、Astral 社によって開発されている、Rust 製の Python パッケージ管理ツールです。
Python のパッケージといえば pip や poetry などもありますが uv はこれらに比べて非常に高速です。

Python の標準的な設定ファイルである pyproject.toml にも対応しているため、一般的な Python プロジェクトで導入可能です。
必要に応じて venv で仮想環境を作成することもできます。
uv の使い方
インストール方法は公式ドキュメントを参照するのがよいと思います。Windows, MacOS, Linux の各 OS に対するインストール方法が書いてあります。インストールはとても簡単です。
Mac や Linux であれば以下のコマンドでインストールできます。
curl -LsSf https://astral.sh/uv/install.sh | sh
Windows の場合でも PowerShell で簡単にインストール可能です。
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
uv は Python 自体のバージョンも管理することができます。
詳しくは https://docs.astral.sh/uv/guides/install-python/#getting-started を参照ください。
基本的な使い方は以下の通りです。
# プロジェクトの作成 uv init <プロジェクト名>
README.md や pyproject.toml などのファイルが作成されます。
# 実行 uv run hello.py
run コマンドで実行することができます。
# package の追加(例: boto3) uv add boto3
add コマンドでパッケージを追加することができます。
ここでなぜパッケージの依存関係が管理できるのかと疑問に思いました。
次の章で uv における依存関係を解決する仕組みについて紹介します。
依存関係の解決方法
uv の公式ページ (https://docs.astral.sh/uv/concepts/resolution/) に解説がありました。
以下で詳しくみていきます。
依存関係とは?
そもそも依存関係とは何でしょうか。普段何気なく使っている用語ですがここで改めて確認してみましょう。
依存関係とはパッケージが動作するために必要としている他のパッケージのことです。一般的にパッケージはバージョン管理されており、依存しているパッケージの許容しているバージョンも決まっています。
例
あるプロジェクトで「パッケージ A」と「パッケージ B」の両方をインストールしたいとします。
- パッケージ A は、パッケージ C に依存しており「パッケージ C の バージョン 1.0 以上」が必要 (
package_C>=1.0.0) - パッケージ B は、パッケージ C に依存しており「パッケージ C の バージョン 2.0 未満」が必要 (
package_C<2.0.0)
この時、パッケージ管理ツールは以下のように考えます。
- A のためには C が 1.0 以上であること
- しかし、B のためには C が 2.0 未満であること
- 結果として C はこの範囲内にあるバージョンの中から最新のものを選ぶ
このようにして依存関係を解決し、互換性を保ったまま適切なバージョンをインストールすることができます。
uv では pyproject.toml に依存関係が定義されています。PEP 508 で指定子の構文が定義されています。各依存関係は「パッケージ名」「バージョン制約」「環境マーカー」で構成されます。(例:boto3>=1.42.3 )
uv が速い理由
uv が速い理由を見てみましょう。
1. Rust による実装
Python 自体は素晴らしい言語ですが、パッケージ管理のような大量の計算(依存関係の解決)やファイル操作が必要な処理においては、コンパイル言語である Rust のパフォーマンスが圧倒的です。uv はこの恩恵を最大限に受けています。
2. グローバルキャッシュとハードリンク
これが uv の最大の特徴かもしれません。従来の pip や venv は、プロジェクトごとにパッケージの実体をコピーしていました。これではディスク容量も食いますし、I/O待ち時間も発生します。
一方 uv は、PC内の共通領域にパッケージを保存(グローバルキャッシュ)し、各プロジェクトの仮想環境にはそのファイルへの「リンク(ハードリンクやリフリンク)」を作成します。
物理的なファイルコピーが発生しないため、インストールが一瞬で完了します。
3. 並列処理
パッケージのダウンロードや解凍を並列に行うことで、ネットワークやディスク I/O の待ち時間を最小限に抑えています。
多くのパッケージのインストールが必要な大規模なプロジェクトほど時間短縮効果が多く得られます。
PubGrub アルゴリズム
詳しくは https://nex3.medium.com/pubgrub-2fb6470504f をお読みください。
Rust で実装された
pubgrub-rsが公開されており、uvはこれを採用することで高速な動作を実現しています。PubGrub は依存関係の解決に失敗した場合には人が理解しやすい形で原因を教えてくれます。
上記の理由から依存関係のインストールはすぐに終わり、体感的にもかなり早くなったことを実感しています。パッケージのバージョンの更新が頻繁に入る場合でも uv sync がすぐに終わるため素早くコーディングに取り掛かることができます!
まとめ
今回は、Python の新しいパッケージ管理ツール uv について、その特徴と裏側の仕組みを紹介しました。
セーフィーの開発環境でも、こうした新しいツールを積極的に検証・導入することで、開発サイクルの高速化を目指しています。
まだ pip install の待ち時間にコーヒーを淹れている方は、ぜひ一度 uv を試してみてください。poetry からの移行でも十分に恩恵は得られると思います!
最後まで読んでいただきありがとうございました!