1. はじめに
チーム開発を進める中で、各開発者の Python バージョンやインストールされているパッケージが異なり、開発環境の統一が難しいという問題に直面することがあります。この問題を解決し、Python のバージョン管理だけでなく、使用するライブラリ群のバージョンもプロジェクトごとに統一し、かつディレクトリ移動に応じて自動的に開発環境を切り替える方法を、uv と direnv を用いて実現します。
2. 前提
本記事の手順は、以下の環境を前提としています。
- macOS であること
- Homebrew が利用可能であること
3. 本題
3-1. 必要なツールのインストール
まず、Python パッケージインストーラー兼バージョマネージャーである uv と、ディレクトリベースで環境変数を管理する direnv を インストールします。
brew install uv
brew install direnv
3-2. direnv をシェルに設定
direnv を利用するためには、お使いのシェルに応じた設定をシェルの設定ファイル(例: .zshrc, .bashrc など)に追加します。
zsh の場合 (.zshrc):
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc
source ~/.zshrc
bash の場合 (.bashrc):
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
source ~/.bashrc
設定後、ターミナルを再起動するか、source コマンドで設定を読み込んでください。
3-3. プロジェクトごとの環境構築
ここでは、例として sample_project という名前のプロジェクトを作成し、Python 3.13.3 の環境を構築します。
プロジェクトディレクトリの作成と uv init による初期化
まずプロジェクトディレクトリを作成し、uv init コマンドでプロジェクトを初期化します。これにより、pyproject.toml ファイルが生成されます。
mkdir sample_project
cd sample_project
uv init
uv init を実行することで以下ファイルが生成されます。
注意: uv init が生成する内容は uv のバージョンによって変わることがあります。下記は一例です。
生成される pyproject.toml の例:
[project]
name = "sample-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12.2"
dependencies = []
pyproject.toml の編集
次に、pyproject.toml を編集して、プロジェクトに必要な依存関係を定義します。
例として、fastapi と uvicorn を通常依存、ruff と pytest を開発用依存として追加します。
[project]
name = "sample_project"
version = "0.1.0"
description = "A FastAPI project managed with uv and direnv."
readme = "README.md"
requires-python = ">=3.12.2"
dependencies = [
"fastapi>=0.100.0",
"uvicorn[standard]>=0.20.0",
]
# 開発用依存関係 (uv がサポートする形式)
[project.optional-dependencies]
dev = [
"ruff",
"pytest",
"black",
]
requires-python は、このプロジェクトが要求する Python のバージョンを指定します。使用する Python バージョンに合わせて適切に設定してください。
pyproject.toml が編集できたらライブラリを同期します。
uv pip sync pyproject.toml
uv による仮想環境の作成
uv venv コマンドを使用して、プロジェクト用の仮想環境を作成します。pyproject.toml の requires-python で指定したバージョンと互換性のある Python バージョンを指定します(例: Python 3.13.3)。
uv venv --python 3.13.3
成功すると、プロジェクトディレクトリ直下に .venv という名前の仮想環境が作成されます。
.envrc ファイルの作成と編集
次に、ディレクトリに入ったときに自動的に仮想環境が有効になり、依存関係が同期されるように .envrc ファイルを作成・編集します。
# 0. uv コマンドの存在確認
# `has` は direnv の標準関数で、コマンドの存在をチェックします。
if ! has uv; then
echo "uv が見つからないためパッケージの同期をスキップします" >&2
return 1 # .envrc の処理を中断し、エラーコード 1 を返すが、シェルは終了しない
fi
# 1. 仮想環境のアクティベート
# プロジェクトローカルの .venv を有効化します。
source .venv/bin/activate
# 2. pyproject.toml と uv.lock の変更を監視
# これらのファイルが変更された場合、direnv は .envrc を再ロードします。
watch_file pyproject.toml
watch_file uv.lock # uv.lock は uv pip sync/add などで生成・更新されます
# 3. 依存関係の自動同期
# uv.lock に変更があった場合のみ実行されます。
# 開発環境を想定し、開発用依存関係も含めて同期します (--extra-index-url dev)。
# `uv pip sync` は `pyproject.toml` と `uv.lock` (あれば) に基づいて環境を同期します。
if [ -f uv.lock ]; then
uv pip sync pyproject.toml --extra-index-url dev
fi
上記の内容を sample_project/.envrc として保存します。
解説
-
has uv:uvコマンドが利用可能かチェックします -
source .venv/bin/activate:uvで作成したローカルの仮想環境を有効にします -
watch_file pyproject.toml/watch_file uv.lock: これらのファイルが変更されると、.envrcが再評価され、後続のコマンド(ここではuv pip sync)が再実行されるトリガーとなります -
uv pip sync pyproject.toml --extras dev:pyproject.tomlに記述された通常依存と、[project.optional-dependencies]のdevグループに記述された開発用依存関係を仮想環境に同期する。uv.lockファイルが存在すれば、ロックファイルに基づいて同期し、なければpyproject.tomlから解決してuv.lockを生成/更新する
direnv に設定の読み込みを許可
セキュリティのため、direnv は .envrc ファイルの内容を自動的には実行しません。
以下のコマンドで、このディレクトリの .envrc ファイルの実行を許可します。
direnv allow .
初回許可後、sample_project ディレクトリに入るたびに、自動的に .venv 仮想環境が有効になり、依存関係が pyproject.toml に基づいて同期されます。
依存関係の確認
.envrc が正しく設定されていれば、direnv allow . を実行した際、または一度ディレクトリ外に出て再度入った際に、uv pip sync が実行され、pyproject.toml に記述した依存関係がインストールされます。
uv pip list でインストールされたパッケージを確認できます。
uv pip list
4. 開発ワークフロー
4-1. 新しいパッケージの追加
プロジェクトに新しいパッケージを追加する場合は、uv add コマンドを使用します。これにより、pyproject.toml が自動的に更新され、uv.lock も更新されます。direnv が変更を検知し、自動的に uv pip sync を実行して仮想環境にパッケージをインストールします。
通常依存の追加:
uv add requests
pyproject.toml の [project].dependencies に requests が追加されます。
開発用依存の追加 (optional-dependencies の 'dev' グループへ):
uv add "ruff" --group dev
uv add "black" --optional dev
pyproject.toml の [project.optional-dependencies.dev] に ruff や black が追加されます。
(uv add の --dev や --group オプションの挙動は uv のバージョンにより若干異なる場合があるため、公式ドキュメントをご確認ください。[project.optional-dependencies] を使うのが標準的です。)
uv add 実行後、.envrc の watch_file 機能により direnv が pyproject.toml の変更を検知し、自動的に uv pip sync を実行して新しいパッケージが仮想環境にインストールされます。手動で同期したい場合は、ディレクトリに入り直すか、direnv reload を実行します。
4-2. 依存関係の更新
既存のパッケージを最新バージョンに更新したい場合も、pyproject.toml を直接編集するか、uv add package_name@latest のようにして更新後、direnv が自動で同期します。
もし手動ですべてのパッケージを最新バージョンに更新する場合は以下コマンドを実行します。
# すべてのパッケージを最新バージョンに更新
uv pip sync pyproject.toml --upgrade
# 特定のパッケージのみを更新
uv pip sync pyproject.toml --upgrade-package requests
# 開発用依存関係も含めて更新
uv pip sync pyproject.toml --upgrade --group=dev
5. 動作確認
設定が正しく行われていれば、以下の動作が確認できます。
5-1. sample_project へ移動
ターミナルで sample_project ディレクトリに移動すると、.envrc がロードされ、uv pip sync が実行されます。プロンプトに (.venv) が表示され、仮想環境が有効になります。
python --version や which python で仮想環境の Python が使われていることを確認します。
pyproject.toml に記述した fastapi や開発用ツールの ruff などが import できるか(またはコマンドとして利用できるか)確認します。
5-2. sample_project から移動:
他のディレクトリに移動すると、.envrc がアンロードされ、仮想環境が無効になります。
5-3. pyproject.toml の変更:
pyproject.toml の依存関係を手動で変更・保存すると、direnv が変更を検知し、再度 uv pip sync を実行して環境を更新します。
6. まとめ
uv と direnv用いてプロジェクトごとの Python 環境を動的に切り替える方法を紹介しました。
チームメンバー内における Python バージョンやライブラリのバージョン統一に向けて少しでも参考になれば幸いです。
7. 【推奨】 .gitignore の設定
プロジェクトの .gitignore ファイルには、uv が作成する仮想環境ディレクトリと direnv が使用する状態管理ディレクトリを追加しておくことを推奨します。
# uv
.venv/
.direnv/
参考







