uvを活用したPythonのマルチプロジェクトのモノレポ構成
はじめに
本記事では、Pythonのパッケージマネージャーとして注目を集めているuvを使った、Pythonのマルチプロジェクトのモノレポ構成を紹介します。
環境構築アプローチの選択
uvを使ったマルチプロジェクト環境の構築には、主に2つのアプローチがあります。
-
uvのワークスペース機能を使う方法
- 単一の仮想環境で複数プロジェクトを管理
- 依存関係を1つのlockファイルで一元管理
- プロジェクト間の依存関係の解決が簡単
-
プロジェクトごとに独立した環境を作る方法
- 各プロジェクトが独自の仮想環境を持つ
- プロジェクト間の独立性が高く、個別のデプロイが簡単
本記事では、プロジェクトごとに個別のバージョンアップやデプロイを行う前提だったため、プロジェクトごとに独立した環境を作る方法を採用しています。この方法には以下のような利点があります。
- 各プロジェクトの依存関係を明確に分離
- プロジェクトごとに異なるPythonバージョンを使用
- プロジェクト単位でRuffやmypyの設定を調整
本記事で紹介した実装例のソースコードは以下のリポジトリで公開しています。
プロジェクトの概要
このプロジェクトは、以下の2つのコンポーネントで構成されるPythonモノレポの実装例です。
- 共有モジュール(
module1
): 再利用可能なPythonパッケージ - サンプルアプリケーション(
application1
): 共有モジュールであるmodule1
を使った実装例
プロジェクト構造
.
├── apps/ # アプリケーションのプロジェクト置き場
│ └── application1/ # サンプルアプリケーションのプロジェクト
├── shared/ # 共有モジュールのプロジェクト置き場
│ └── module1/ # サンプルの共有モジュールのプロジェクト
└── shared_config/ # 各プロジェクトで使う共通の設定ファイル置き場
1. 共有モジュールのプロジェクト
shared
ディレクトリには、各アプリケーションプロジェクトで共通して使うAPIなどを持つモジュールを置きます。この例ではmodule1
という共有モジュールが1つありますが、複数の共有モジュールを置くことを想定した構成です。
サンプルの共有モジュールmodule1
は以下の特徴を持ちます。
- アプリケーションプロジェクトから呼び出せるAPIを提供
- module1からアプリケーションプロジェクトへの依存を持たない
-
uv init module1 --lib --package
で作成
2. アプリケーションのプロジェクト
apps
ディレクトリには、アプリケーションプロジェクトを置きます。この例ではapplication1
というアプリケーションプロジェクトが1つありますが、複数のアプリケーションプロジェクトを置くことを想定した構成です。
サンプルアプリケーションapplication1
は共有モジュールを使ったアプリケーション実装例として以下の特徴を持ちます。
- module1から提供されるAPIを呼び出す
-
uv init application1 --app --package
で作成
使っている技術やツールについて
本リポジトリで使っている技術スタックの詳しい選定理由と設定方法については、以下の記事を参照してください。
上記の記事では、各ツールの特徴や利点、基本的な設定方法を紹介しています。
本記事でも、同じツールを採用しています。
採用したツールと、この記事執筆時点のバージョンは以下の通りです。
- mise: 2025.1.3
- uv: 0.5.13
- Task: 3.40.1
- Ruff: 0.9.1
- mypy: 1.14.1
- pre-commit: 4.0.1
プロジェクト構成のポイント
1. プロジェクトごとの独立した仮想環境
各プロジェクトディレクトリにpyproject.toml
を配置し、uvを使って独立した仮想環境(.venv
)を作ります。
apps/application1/
├── pyproject.toml # application1用の依存関係定義
└── .venv/ # application1専用の仮想環境
shared/module1/
├── pyproject.toml # module1用の依存関係定義
└── .venv/ # module1専用の仮想環境
この構成には以下のような利点があります。
- プロジェクトごとに必要な依存関係を明確に分離
- 各プロジェクトの開発・デプロイを独立して実施できる
- プロジェクトごとにPythonバージョンや依存関係を管理できる
2. アプリケーションプロジェクトから、共有モジュールを依存関係に追加
アプリケーションプロジェクトから共有モジュールを依存関係に追加するには、次のコマンドを実行します。--editable
オプションを付けると、共有モジュールの修正がアプリケーションプロジェクトにも反映されます。
cd apps/application1
uv add --editable shared/module1
これによりpyproject.toml
は以下のようになります。
# apps/application1/pyproject.toml
[tool.uv.sources]
module1 = { path = "../../shared/module1", editable = true }
3. 共有設定の活用
各プロジェクトのpyproject.toml
では、mypyやRuffなどの開発ツールの設定を記述しています。
Ruffやmypyなどの開発環境としての設定は、それぞれのプロジェクトで個別で設定すると冗長になり、管理も複雑になってしまいます。各プロジェクトで共通する設定は、shared_config
ディレクトリに共通の設定ファイルとしてまとめています。
.
├── shared_config/
│ ├── mypy.toml # mypy共通設定
│ └── ruff.toml # Ruff共通設定
各プロジェクトのpyproject.toml
からは、これらの共通設定を参照する形で設定を継承しています。
# apps/application1/pyproject.toml
# shared/module1/pyproject.toml
[tool.mypy]
extends = "../../shared_config/mypy.toml"
[tool.ruff]
config_file = "../../shared_config/ruff.toml"
各プロジェクトで個別の設定を追加したい場合は、各プロジェクトのpyproject.toml
で設定を追加することができます。それにより、共通のルールを統一的に適用できるとともに、プロジェクト固有の要件に応じた設定のカスタマイズが可能になります。
この方式には次のようなメリットがあります。
- 共通のルールを統一的に適用できる
- プロジェクト固有の要件に応じた設定のカスタマイズが可能
- 設定の重複を排除し、メンテナンス性を向上
4. pre-commitフックの統合管理
マルチプロジェクトをモノレポで管理する場合、プロジェクトごとに静的解析や単体テストなど設定も異なる可能性があるため、開発者によらない形で正しく実行するためにpre-commitフックの設定も重要になってきます。
ただし、Gitのhookは1つしか設定できないため、下記のような工夫を行っています。
各プロジェクトに.pre-commit-config.yaml
を配置し、各プロジェクトで個別のpre-commitチェックの設定をしていますが、ルートディレクトリの.pre-commit-config.yaml
の中でsub-pre-commitを使用することで統合しています。
.
├── .pre-commit-config.yaml
├── ...
└── apps/
├── application1
│ ├── pyproject.toml
│ ├── ...
│ └── .pre-commit-config.yaml
└── shared/
├── module1
│ ├── pyproject.toml
│ ├── ...
│ └── .pre-commit-config.yaml
pre-commitだけでは、リポジトリ単位でグローバルな設定ファイルを1つしか作れないため、プロジェクトごとに異なるニーズがある場合に問題がおきます。特にモノレポでは、異なるテクノロジーを組み合わせるため、この問題がよく起きます。
sub-pre-commitを使用すると、各プロジェクトで独自のpre-commitチェックを定義可能です。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/ddanier/sub-pre-commit.git
rev: v4.0.1
hooks:
- id: sub-pre-commit
alias: module1
name: "Module1 pre-commit"
args: ["-p", "shared/module1"]
files: ^shared/module1/
stages: ["pre-commit"]
- id: sub-pre-commit
alias: application1
name: "Application1 pre-commit"
args: ["-p", "apps/application1"]
files: ^apps/application1/
stages: ["pre-commit"]
この構成によって次のことが実現できます。
- 各プロジェクトで独自のpre-commitチェックを定義可能
- コミット時に全プロジェクトのチェックを一括実行
- プロジェクト固有のコンテキストを維持したチェックが可能
5. VSCode Multi Root Workspaceの活用
VSCodeのMulti Root Workspace機能を使用することで、複数のPythonプロジェクトを1つのウィンドウで効率的に管理できます。
各プロジェクトディレクトリに.vscode/settings.json
を配置し、VSCodeのMulti Root Workspace機能と組み合わせることで、プロジェクトごとの開発環境を適切に構成しています。
.
├── apps/
│ └── application1/
│ ├── .vscode/
│ │ └── settings.json
│ └── ...
└── shared/
├── module1/
│ ├── .vscode/
│ │ └── settings.json
│ └── ...
// application1/.vscode/settings.json
// shared/module1/.vscode/settings.json
{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.analysis.extraPaths": ["${workspaceFolder}/src"],
"ruff.interpreter": ["${workspaceFolder}/.venv/bin/python"],
"mypy.dmypyExecutable": "${workspaceFolder}/.venv/bin/dmypy"
}
この設定によって以下が可能になります。
- プロジェクトごとに適切な仮想環境が自動的に選択される
- Python、Ruff、mypyなどの拡張機能が正しい環境を参照
- プロジェクト切り替え時に設定が自動的に切り替わる
IDEの設定をGit管理することには賛否両論がありますが、VSCodeのMulti Root Workspace機能を使用して、IDEの設定をGit管理することで、そのプロジェクト構成に合わせた開発環境を複数開発者で共有できるため、開発環境の構築が容易になります。
.vscode/sample_settings.json
などのサンプルファイルを作成しGit管理し、開発者が使用する設定をコピーして使うのも良いと思います。
おわりに
本記事では、uvを活用したPythonのマルチプロジェクトのモノレポ構成について、設定例を交えながら解説しました。
この構成の主なポイントは以下の通りです。
- プロジェクトごとに独立した仮想環境を持つことで、依存関係の分離と個別デプロイを実現
- 共通設定を
shared_config
にまとめることで、設定の重複を排除し保守性を向上 - sub-pre-commitを活用し、プロジェクトごとのpre-commitの設定を統合的に管理
- VSCode Multi Root Workspaceを活用し、効率的に仮想環境を切り替え
今回紹介した構成がどなたかの参考になれば幸いです。
Discussion