Skip to content

ワークスペース

同名のCargoの概念に触発されて、ワークスペースは「_ワークスペースメンバー_と呼ばれる1つ以上のパッケージのコレクションであり、一緒に管理されます。」

ワークスペースは、共通の依存関係を持つ複数のパッケージに分割することで、大規模なコードベースを整理します。たとえば、FastAPIベースのWebアプリケーションと、同じGitリポジトリ内で別々のPythonパッケージとしてバージョン管理および保守される一連のライブラリを考えてみてください。

ワークスペースでは、各パッケージが独自のpyproject.tomlを定義しますが、ワークスペースは単一のロックファイルを共有し、ワークスペースが一貫した依存関係セットで動作することを保証します。

そのため、uv lockはワークスペース全体に対して一度に操作し、uv runおよびuv syncはデフォルトでワークスペースのルートで操作しますが、どちらも--package引数を受け入れ、任意のワークスペースディレクトリから特定のワークスペースメンバーでコマンドを実行できます。

始めに

ワークスペースを作成するには、pyproject.tomltool.uv.workspaceテーブルを追加します。これにより、そのパッケージをルートとするワークスペースが暗黙的に作成されます。

Tip

既存のパッケージ内でuv initを実行すると、新しく作成されたメンバーがワークスペースに追加され、ワークスペースルートにtool.uv.workspaceテーブルが存在しない場合は作成されます。

ワークスペースを定義する際には、members(必須)およびexclude(オプション)のキーを指定する必要があります。これにより、ワークスペースはそれぞれメンバーとして特定のディレクトリを含めたり除外したりするように指示され、グロブのリストを受け入れます。

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 }

[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-feederbird-feederパッケージでコマンドを実行します。

ワークスペースソース

ワークスペース内では、ワークスペースメンバーへの依存関係は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 }

[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を考えてみましょう。

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.tomlseedsが除外されているため、ワークスペースには合計2つのメンバーがあります:albatross(ルート)とbird-feederです。

ワークスペースを使用する場合と使用しない場合

ワークスペースは、単一のリポジトリ内で複数の相互接続されたパッケージの開発を促進することを目的としています。コードベースが複雑になるにつれて、それをより小さく、コンポーザブルなパッケージに分割し、それぞれが独自の依存関係とバージョン制約を持つことが役立ちます。

ワークスペースは、分離と関心の分離を強制するのに役立ちます。たとえば、uvでは、コアライブラリとコマンドラインインターフェースのために別々のパッケージを持ち、CLIとは独立してコアライブラリをテストできるようにしています。

ワークスペースの他の一般的な使用例には次のようなものがあります。

  • 拡張モジュール(Rust、C++など)で実装されたパフォーマンスクリティカルなサブルーチンを持つライブラリ。
  • 各プラグインがルートに依存する別々のワークスペースパッケージであるプラグインシステムを持つライブラリ。

ワークスペースは、メンバーが競合する要件を持っている場合や、各メンバーに対して個別の仮想環境を必要とする場合には適していません。この場合、パス依存関係が好まれることがよくあります。たとえば、albatrossとそのメンバーをワークスペースにグループ化する代わりに、各パッケージを独立したプロジェクトとして定義し、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 = { 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はパッケージが他のワークスペースメンバーによって宣言された依存関係をインポートしないことを保証できません。