Pythonで個人開発を続けていると、プロジェクトごとに仮想環境を作り、ライブラリをインストールし、requirements.txtを更新する作業が必要になります。
従来は、次のような操作が一般的でした。
python -m venv .venv
.venv\Scripts\activate
pip install pandas
pip freeze > requirements.txt
python main.py
小規模なプロジェクトが1つだけなら、この方法でも大きな問題はありません。しかし、プロジェクト数が増え、生成AIがコードを変更するようになると、次の状態が食い違いやすくなります。
- コードが実際に必要としているライブラリ
.venvにインストールされているライブラリrequirements.txtに記録されているライブラリ- 開発者が意図しているバージョン範囲
この問題を解決する有力な選択肢が、Pythonパッケージ・プロジェクト管理ツールのuvです。
この記事では、uvの役割、基本構造、Windowsへの導入、日常的な操作、既存プロジェクトからの移行、生成AIと組み合わせた依存関係管理まで順を追って解説します。
uvとは
uvは、Python開発で分散していた複数の役割をまとめて扱うツールです。
- Python本体の管理
- 仮想環境の作成
- パッケージの追加・削除
- 直接依存関係の記録
- 間接依存関係の解決
- パッケージバージョンの固定
- 仮想環境の同期
- Pythonプログラムやテストの実行
requirements.txtなどへのエクスポート
単にpipを高速化しただけのツールではありません。pip、venv、依存関係リゾルバー、ロックファイル、Pythonバージョン管理などを、1つのプロジェクト管理フローに統合するものと考えると分かりやすいでしょう。
uvはAstralによって開発され、Windows、macOS、Linuxで利用できます。詳細はuv公式ドキュメントで確認できます。
従来方式で起きる問題
.venvの状態が設計仕様になってしまう
たとえば、次のコマンドでpandasをインストールしたとします。
pip install pandas
その後にpip freezeを実行すると、pandasだけでなく、pandasが内部で利用するライブラリも出力されます。
numpy==2.x.x
pandas==2.x.x
python-dateutil==2.x.x
pytz==2026.x
six==1.x.x
tzdata==2026.x
自分が直接選んだのはpandasだけですが、pip freezeの結果だけを見ると、すべてを直接選定したように見えます。
また、以前に試しただけのライブラリが.venvに残っていれば、現在のコードで使っていなくてもrequirements.txtへ記録されます。
pipの公式ドキュメントでも、pip freezeは現在インストールされているものを報告する機能であり、依存関係を計算してロックファイルを作る機能ではないと説明されています。
直接依存と間接依存を分離しにくい
依存関係には、次の2種類があります。
| 種類 | 意味 | 例 |
|---|---|---|
| 直接依存 | プロジェクトが直接利用するライブラリ | pandas、openpyxl |
| 間接依存 | 直接依存ライブラリが内部で必要とするもの | numpy、six、tzdata |
人間が管理したいのは主に直接依存です。間接依存の組み合わせとバージョンは、依存関係管理ツールに解決させるのが合理的です。
uvによる依存関係管理の構造
uvでは、依存関係を次の4層に分けます。
| 層 | ファイル・場所 | 役割 |
| プロジェクト仕様 | pyproject.toml | 直接必要な依存関係を宣言する |
| 解決結果 | uv.lock | 間接依存を含む正確なバージョンを固定する |
| 実行環境 | .venv | 実際にパッケージをインストールする |
| 互換出力 | requirements.txt | 外部サービスなどが要求するときだけ生成する |
flowchart TD
A["pyproject.toml<br>直接依存の仕様"] --> B["uv resolver<br>依存関係を計算"]
B --> C["uv.lock<br>バージョン固定"]
C --> D[".venv<br>実行環境"]
C --> E["requirements.txt<br>必要時のみ出力"]重要なのは、.venvを依存関係の正解にしないことです。
pyproject.tomlを設計仕様、uv.lockを依存関係の解決結果、.venvを再生成可能な実行環境として扱います。
pyproject.tomlとは
pyproject.tomlは、Pythonプロジェクトの標準的な設定ファイルです。uv専用形式ではありません。
例を示します。
[project]
name = "weight-calculator"
version = "0.1.0"
description = "Material weight calculation tool"
requires-python = ">=3.12"
dependencies = [
"pydantic>=2.0",
"openpyxl>=3.1",
]
[dependency-groups]
dev = [
"pytest",
"ruff",
]
この例では、プログラムの実行に直接必要なのは次の2つです。
pydantic
openpyxl
開発時だけ必要なのは次の2つです。
pytest
ruff
pydanticやopenpyxlが内部で必要とするライブラリを、人間がすべて記述する必要はありません。uvが解決してuv.lockに記録します。
pyproject.tomlの依存関係記述については、Python Packaging User Guideで標準仕様を確認できます。
uv.lockとは
uv.lockは、依存関係を解決した結果を記録するロックファイルです。
たとえばpandasを追加すると、pandas本体だけでなく、関連する間接依存も含めて具体的なバージョンの組み合わせが決定されます。
概念的には次のような依存ツリーです。
weight-calculator
├── openpyxl
│ └── et-xmlfile
└── pandas
├── numpy
├── python-dateutil
│ └── six
└── tzdata
pyproject.tomlが「何を必要とするか」という仕様であるのに対し、uv.lockは「どのバージョンの組み合わせで成立させるか」という解決結果です。
uv.lockは原則として手作業で編集しません。uvのコマンドを通して更新し、アプリケーション開発ではGitの管理対象にします。
Windowsにuvをインストールする
Windowsでは、wingetを使うと簡単です。
winget install --id=astral-sh.uv -e
インストール後にバージョンを確認します。
uv --version
winget経由で更新する場合は、次のコマンドを使用します。
winget upgrade --id=astral-sh.uv -e
公式のPowerShellインストーラー、pipx、Scoopなどを使う方法もあります。
新しいPythonプロジェクトを作る
重量計算ツールを例に、新規プロジェクトを作成します。
mkdir weight-calculator
cd weight-calculator
uv init
初期化すると、おおむね次のようなファイルが作成されます。
weight-calculator/
├─ .gitignore
├─ .python-version
├─ README.md
├─ pyproject.toml
└─ main.py
最初にuv add、uv run、uv syncなどを実行すると、.venvとuv.lockも作成されます。
weight-calculator/
├─ .venv/
├─ .gitignore
├─ .python-version
├─ README.md
├─ main.py
├─ pyproject.toml
└─ uv.lock
Pythonバージョンを指定する
たとえばPython 3.12系を使う場合は、次のように指定します。
uv python pin 3.12
.python-versionには次のように記録されます。
3.12
Python 3.12がPCにない場合は、明示的にインストールできます。
uv python install 3.12
uvはプロジェクトが要求するPythonを探索し、必要に応じて管理下のPythonを取得できます。
ライブラリを追加する
通常の実行時依存を追加します。
uv add pandas
uv add openpyxl
複数のライブラリをまとめて追加することもできます。
uv add pandas openpyxl pydantic
許容するバージョン範囲も指定できます。
uv add "pandas>=2,<3"
uv addを実行すると、原則として次の処理が連動します。
pyproject.tomlへ直接依存を追加する- 間接依存を含めて互換性を計算する
uv.lockへ解決結果を記録する.venvを更新する
開発用ライブラリを追加する
pytest、Ruff、型チェックツールなどは、実行時依存と分離できます。
uv add --dev pytest ruff
pyproject.tomlでは、開発用依存として管理されます。
[dependency-groups]
dev = [
"pytest",
"ruff",
]
これにより、本番実行に必要なライブラリと、開発・テストにだけ必要なライブラリを区別できます。
プログラムを実行する
uvでは、仮想環境をactivateせずにプログラムを実行できます。
uv run python main.py
テストも同様です。
uv run pytest
Ruffによる静的解析は次のように実行します。
uv run ruff check .
uv runは実行前に、pyproject.toml、uv.lock、プロジェクト環境の状態を確認し、必要に応じて同期します。
従来どおり仮想環境をactivateすることもできます。
uv sync
.venv\Scripts\activate
python main.py
ただし、複数の個人プロジェクトを扱う場合は、uv runに統一した方が別プロジェクトの仮想環境を誤って使用しにくくなります。
ライブラリを削除する
不要になった直接依存は、次のコマンドで削除します。
uv remove pandas
この操作により、pyproject.toml、uv.lock、.venvが連動して更新されます。
ただし、コードから本当に削除可能かどうかはテストで確認します。
uv remove pandas
uv run pytest
パッケージを更新する
すべてのパッケージについて、新しい互換バージョンへの更新を試みる場合は次を実行します。
uv lock --upgrade
特定のパッケージだけ更新する場合は次です。
uv lock --upgrade-package pandas
特定バージョンへ更新する場合は、次のように指定できます。
uv lock --upgrade-package "pandas==2.3.2"
更新後は、環境を同期してテストします。
uv sync
uv run pytest
依存関係の固定バージョンは、コードを変更しなくても更新できます。したがって、コード変更と定期的な依存更新は別のトリガーとして扱います。
依存ツリーを確認する
次のコマンドで依存関係を表示できます。
uv tree
これにより、自分が直接追加したパッケージと、それらが内部で要求するパッケージの関係を確認できます。
仮想環境を同期する
uv.lockに基づいて.venvを同期する場合は、次を実行します。
uv sync
Gitからプロジェクトを取得した場合も、原則として次の操作で環境を再構築できます。
git clone <repository-url>
cd <project-name>
uv sync
uv run pytest
.venvそのものをGitへ登録する必要はありません。
Gitへ登録するものは次のとおりです。
pyproject.toml
uv.lock
.python-version
ソースコード
テストコード
Gitへ登録しないものは次のとおりです。
.venv/
__pycache__/
requirements.txtは必要なくなるのか
uvを使うプロジェクトでは、通常、pyproject.tomlとuv.lockが依存関係管理の中心になります。
uvだけで開発・実行・配布できる環境なら、requirements.txtは必須ではありません。
一方、外部の実行環境やレンタルサーバーがrequirements.txtを要求することがあります。その場合は、uv.lockから自動生成します。
uv export --format requirements.txt --output-file requirements.txt
このrequirements.txtは手作業で編集せず、生成物として扱います。
flowchart LR
A["pyproject.toml"] --> B["uv lock"]
B --> C["uv.lock"]
C --> D["uv export"]
D --> E["requirements.txt"]uv公式ドキュメントでも、uv.lockとrequirements.txtを二重に管理するのではなく、必要な用途がある場合にエクスポートする方法が案内されています。
依存関係を更新するタイミング
依存関係の更新は、生成AIがコードを変更したときだけに限られません。
| トリガー | 直接依存 | 固定バージョン | 主な処理 |
| 新しいライブラリを使用 | 変わる | 変わる | uv add |
| ライブラリの使用を廃止 | 変わる | 変わる | uv remove |
| 特定パッケージを更新 | 通常は変わらない | 変わる | uv lock --upgrade-package |
| 全依存を定期更新 | 通常は変わらない | 変わる | uv lock --upgrade |
| Pythonバージョンを変更 | 場合による | 変わり得る | 再ロックとテスト |
| OSや実行環境を変更 | 場合による | 変わり得る | 再ロックとテスト |
依存関係が変化していないコード変更で、毎回ファイルを書き換える必要はありません。重要なのは、依存関係の変更イベントを確実に捕捉することです。
コード変更と依存変更を同じ単位で扱う
生成AIが次のコードを追加したとします。
import pandas as pd
この場合、コードだけを変更して作業を終了してはいけません。同じ作業の中で次を実行します。
uv add pandas
uv run pytest
コード、依存宣言、ロックファイル、実行環境、テストを1つの変更単位として扱います。
flowchart TD
A["コード変更"] --> B{"新しい依存あり?"}
B -- あり --> C["uv add / uv remove"]
B -- なし --> E["テスト"]
C --> D["pyproject・lock・環境更新"]
D --> E
E --> F{"成功?"}
F -- はい --> G["変更確定"]
F -- いいえ --> H["修正または差し戻し"]生成AIが依存更新を忘れた場合
生成AIに指示を書いても、uv addを忘れる可能性はあります。そこで、AIの注意力ではなく機械的な検証で防止します。
pyproject.tomlとuv.lockから環境を同期する- テストを実行する
ModuleNotFoundErrorなどを検出する- 不足している依存関係を追加する
- 再びテストする
- 必要なら
requirements.txtを再生成する
つまり、AIが必ず正しく判断することを前提にせず、依存関係管理ツールとテストによって結果を検証します。
生成AIに設定しておきたいルール
リポジトリのAI向け指示ファイルに、次のようなルールを記載できます。
## Python dependency management
- Python dependencies are managed with uv.
- Do not run `pip install` directly.
- Add runtime dependencies with `uv add`.
- Add development dependencies with `uv add --dev`.
- Remove dependencies with `uv remove`.
- Do not manually edit `uv.lock`.
- Generate `requirements.txt` only with `uv export`.
- Run tests with `uv run pytest` after dependency changes.
- Commit `pyproject.toml` and `uv.lock`.
- Do not commit `.venv`.
これにより、生成AIがコードだけを変更して依存関係を放置する可能性を減らせます。
既存プロジェクトをuvへ移行する
既存プロジェクトが次の構成だとします。
existing-project/
├─ .venv/
├─ main.py
└─ requirements.txt
まず、Gitなどで移行前の状態を保存します。そのうえでプロジェクト直下を初期化します。
uv init
既存のrequirements.txtを取り込む場合は、次を実行できます。
uv add -r requirements.txt
簡易移行
既存環境をできるだけ維持して、まずuv管理へ移す方法です。
uv init
uv add -r requirements.txt
uv run pytest
この方法は移行しやすい反面、requirements.txtがpip freezeで作られていると、間接依存まで直接依存として登録される可能性があります。
依存関係を整理して移行する
より正確に移行する場合は、次の流れを生成AIやスクリプトに実行させます。
- Pythonファイルからimport文を抽出する
- Python標準ライブラリを除外する
- import名とインストール済みパッケージを対応づける
- 直接依存候補を作る
uv addで登録する- 新しい環境を構築する
- テストを実行する
- 不足している依存を追加する
- 不要候補を削除して再テストする
ただし、import名と配布パッケージ名は必ずしも一致しません。
| import文 | インストールするパッケージ |
import cv2 | opencv-python |
from PIL import Image | Pillow |
import yaml | PyYAML |
import sklearn | scikit-learn |
そのため、静的解析だけで確定するのではなく、環境を再構築してテストする工程が必要です。
複数プロジェクトの管理
複数のPythonアプリを管理する場合、最初はプロジェクトごとにpyproject.tomlとuv.lockを持たせるのが分かりやすい構成です。
projects/
├─ autopost/
│ ├─ pyproject.toml
│ ├─ uv.lock
│ └─ .venv/
├─ hydraulic-calculator/
│ ├─ pyproject.toml
│ ├─ uv.lock
│ └─ .venv/
└─ weight-calculator/
├─ pyproject.toml
├─ uv.lock
└─ .venv/
各アプリを独立させると、次の項目を個別に管理できます。
- Pythonバージョン
- 依存パッケージ
- 更新時期
- テスト方法
- 配布方法
uvには複数プロジェクトでロックファイルを共有するworkspace機能もあります。ただし、最初から共有するとプロジェクト間の依存が強くなるため、共通パッケージや一括テストの必要性が明確になってから検討する方が安全です。
pip installを直接使わない
uvプロジェクト内で、次のように直接インストールすると、.venvだけが変更される可能性があります。
pip install pandas
この状態では、pyproject.tomlと.venvが一致しません。これを環境ドリフトと呼びます。
uvプロジェクトでは、次のルールに統一します。
ライブラリ追加:uv add
ライブラリ削除:uv remove
プログラム実行:uv run
環境同期:uv sync
依存更新:uv lock --upgrade
requirements.txt生成:uv export
uvを使っても残る注意点
uvを導入しても、依存関係に関する判断が完全になくなるわけではありません。
- パッケージそのものの安全性は別途確認する必要がある
- import文だけでは依存関係を完全に特定できない
- バージョン更新後にはテストが必要
- OS固有パッケージや外部バイナリを考慮する必要がある
uv.lockはuv固有形式である- 古い実行環境では
requirements.txtが必要な場合がある
一方、直接依存の情報は標準形式のpyproject.tomlに保存できます。また、requirements.txtや標準化されたロック形式へ出力できるため、プロジェクトの基本情報をuvだけに閉じ込める構成ではありません。
最初に覚えるコマンド
まずは次のコマンドを覚えれば、基本的な個人開発を進められます。
# プロジェクトを初期化
uv init
# Pythonバージョンを指定
uv python pin 3.12
# 実行時依存を追加
uv add pandas
# 開発用依存を追加
uv add --dev pytest ruff
# 依存を削除
uv remove pandas
# プログラムを実行
uv run python main.py
# テストを実行
uv run pytest
# 仮想環境を同期
uv sync
# 依存ツリーを確認
uv tree
# 特定パッケージを更新
uv lock --upgrade-package pandas
# requirements.txtを生成
uv export --format requirements.txt --output-file requirements.txt
まとめ
uvの本質は、単なる高速なpipではありません。
pyproject.tomlをプロジェクトの依存仕様、uv.lockを依存関係の解決結果、.venvを再生成可能な実行環境として分離し、それらを同期するプロジェクト管理基盤です。
flowchart TD
A["依存関係を追加・削除"] --> B["pyproject.toml更新"]
B --> C["uv.lock自動更新"]
C --> D[".venv同期"]
D --> E["uv runでテスト"]
E --> F{"テスト成功?"}
F -- はい --> G["変更を確定"]
F -- いいえ --> H["依存またはコードを修正"]生成AI時代には、requirements.txtを人間が定期的に書き直すのではなく、コード変更、依存宣言、ロック、環境同期、テストを1つの処理として自動化する方が合理的です。
既存のpip freezeは、古い仮想環境の棚卸しや移行前のスナップショットには利用できます。しかし、新しいプロジェクトではpyproject.toml + uv.lockを正本とし、requirements.txtは必要な場合だけ自動生成する運用が有効です。

