ワークスペース
同名のCargoの概念に触発されて、ワークスペースは「_ワークスペースメンバー_と呼ばれる1つ以上のパッケージのコレクションであり、一緒に管理されます。」
ワークスペースは、共通の依存関係を持つ複数のパッケージに分割することで、大規模なコードベースを整理します。たとえば、FastAPIベースのWebアプリケーションと、同じGitリポジトリ内で別々のPythonパッケージとしてバージョン管理および保守される一連のライブラリを考えてみてください。
ワークスペースでは、各パッケージが独自のpyproject.toml
を定義しますが、ワークスペースは単一のロックファイルを共有し、ワークスペースが一貫した依存関係セットで動作することを保証します。
そのため、uv lock
はワークスペース全体に対して一度に操作し、uv run
およびuv sync
はデフォルトでワークスペースのルートで操作しますが、どちらも--package
引数を受け入れ、任意のワークスペースディレクトリから特定のワークスペースメンバーでコマンドを実行できます。
始めに
ワークスペースを作成するには、pyproject.toml
にtool.uv.workspace
テーブルを追加します。これにより、そのパッケージをルートとするワークスペースが暗黙的に作成されます。
Tip
既存のパッケージ内でuv init
を実行すると、新しく作成されたメンバーがワークスペースに追加され、ワークスペースルートにtool.uv.workspace
テーブルが存在しない場合は作成されます。
ワークスペースを定義する際には、members
(必須)およびexclude
(オプション)のキーを指定する必要があります。これにより、ワークスペースはそれぞれメンバーとして特定のディレクトリを含めたり除外したりするように指示され、グロブのリストを受け入れます。
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]
bird-feeder = { workspace = true }
[tool.uv.workspace]
members = ["packages/*"]
exclude = ["packages/seeds"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
members
のグロブに含まれる(およびexclude
のグロブによって除外されない)すべてのディレクトリには、pyproject.toml
ファイルが含まれている必要があります。ただし、ワークスペースメンバーはアプリケーションまたはライブラリのいずれかであることができます。どちらもワークスペースコンテキストでサポートされています。
すべてのワークスペースにはルートが必要であり、それもワークスペースメンバーです。上記の例では、albatross
がワークスペースのルートであり、ワークスペースメンバーにはpackages
ディレクトリ下のすべてのプロジェクトが含まれますが、seeds
は除外されます。
デフォルトでは、uv run
およびuv sync
はワークスペースのルートで操作します。たとえば、上記の例では、uv run
およびuv run --package albatross
は同等であり、uv run --package bird-feeder
はbird-feeder
パッケージでコマンドを実行します。
ワークスペースソース
ワークスペース内では、ワークスペースメンバーへの依存関係はtool.uv.sources
を介して提供されます。
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]
bird-feeder = { workspace = true }
[tool.uv.workspace]
members = ["packages/*"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
この例では、albatross
プロジェクトはワークスペースのメンバーであるbird-feeder
プロジェクトに依存しています。tool.uv.sources
テーブルのworkspace = true
キーと値のペアは、bird-feeder
依存関係がPyPIや他のレジストリから取得されるのではなく、ワークスペースによって提供されるべきであることを示しています。
ワークスペースルートのtool.uv.sources
定義は、特定のメンバーのtool.uv.sources
でオーバーライドされない限り、すべてのメンバーに適用されます。たとえば、次のpyproject.toml
を考えてみましょう。
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]
bird-feeder = { workspace = true }
tqdm = { git = "https://github.com/tqdm/tqdm" }
[tool.uv.workspace]
members = ["packages/*"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
すべてのワークスペースメンバーは、特定のメンバーが独自のtool.uv.sources
テーブルでtqdm
エントリをオーバーライドしない限り、デフォルトでGitHubからtqdm
をインストールします。
ワークスペースレイアウト
最も一般的なワークスペースレイアウトは、ルートプロジェクトと一連の付随するライブラリとして考えることができます。
たとえば、上記の例を続けると、このワークスペースにはalbatross
に明示的なルートがあり、packages
ディレクトリに2つのライブラリ(bird-feeder
およびseeds
)があります。
albatross
├── packages
│ ├── bird-feeder
│ │ ├── pyproject.toml
│ │ └── src
│ │ └── bird_feeder
│ │ ├── __init__.py
│ │ └── foo.py
│ └── seeds
│ ├── pyproject.toml
│ └── src
│ └── seeds
│ ├── __init__.py
│ └── bar.py
├── pyproject.toml
├── README.md
├── uv.lock
└── src
└── albatross
└── main.py
pyproject.toml
でseeds
が除外されているため、ワークスペースには合計2つのメンバーがあります:albatross
(ルート)とbird-feeder
です。
ワークスペースを使用する場合と使用しない場合
ワークスペースは、単一のリポジトリ内で複数の相互接続されたパッケージの開発を促進することを目的としています。コードベースが複雑になるにつれて、それをより小さく、コンポーザブルなパッケージに分割し、それぞれが独自の依存関係とバージョン制約を持つことが役立ちます。
ワークスペースは、分離と関心の分離を強制するのに役立ちます。たとえば、uvでは、コアライブラリとコマンドラインインターフェースのために別々のパッケージを持ち、CLIとは独立してコアライブラリをテストできるようにしています。
ワークスペースの他の一般的な使用例には次のようなものがあります。
- 拡張モジュール(Rust、C++など)で実装されたパフォーマンスクリティカルなサブルーチンを持つライブラリ。
- 各プラグインがルートに依存する別々のワークスペースパッケージであるプラグインシステムを持つライブラリ。
ワークスペースは、メンバーが競合する要件を持っている場合や、各メンバーに対して個別の仮想環境を必要とする場合には適していません。この場合、パス依存関係が好まれることがよくあります。たとえば、albatross
とそのメンバーをワークスペースにグループ化する代わりに、各パッケージを独立したプロジェクトとして定義し、tool.uv.sources
でパス依存関係としてパッケージ間の依存関係を定義できます。
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]
bird-feeder = { path = "packages/bird-feeder" }
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
このアプローチは多くの同じ利点を提供しますが、依存関係の解決と仮想環境の管理に対するより細かい制御を可能にします(uv run --package
が利用できなくなるという欠点があります。代わりに、コマンドは関連するパッケージディレクトリから実行する必要があります)。
最後に、uvのワークスペースは、すべてのメンバーのrequires-python
値の交差を取り、ワークスペース全体に対して単一のrequires-python
を強制します。特定のメンバーを他のワークスペースがサポートしていないPythonバージョンでテストする必要がある場合は、そのメンバーを別の仮想環境にインストールするためにuv pip
を使用する必要があるかもしれません。
Note
Pythonは依存関係の分離を提供しないため、uvはパッケージが宣言された依存関係のみを使用することを保証できません。特にワークスペースでは、uvはパッケージが他のワークスペースメンバーによって宣言された依存関係をインポートしないことを保証できません。