Chapter 08

工作区 Workspace

一个仓库、多个包、共享锁文件——uv 把 Rust Cargo 的工作区范式带到了 Python

Monorepo 的痛点

很多公司把多个相关的 Python 项目放在同一个仓库里:

在 pip/Poetry 时代,要么每个子项目各自一个 venv + 各自 lock(管理麻烦、依赖不一致),要么所有都塞进根 venv(谁依赖谁不清楚、测试边界模糊)。Rust 的 Cargo 用 workspace 优雅解决了这个问题,uv 把同样的模式引入 Python。

Workspace 基本结构

myrepo/
├── pyproject.toml          # 根 workspace 配置
├── uv.lock                  # 唯一的 lock,所有成员共享
├── .venv/                   # 唯一的 venv
├── packages/
│   ├── shared/
│   │   ├── pyproject.toml
│   │   └── src/shared/__init__.py
│   ├── api/
│   │   ├── pyproject.toml  # 依赖 shared
│   │   └── src/api/__init__.py
│   └── worker/
│       ├── pyproject.toml  # 也依赖 shared
│       └── src/worker/__init__.py
└── cli/
    ├── pyproject.toml      # 独立 CLI
    └── src/cli/__init__.py

根 pyproject.toml 配置

[project]
name = "myrepo"
version = "0.0.0"
requires-python = ">=3.12"
# 根项目可以不声明 dependencies,仅作为 workspace 协调器

[tool.uv.workspace]
members = ["packages/*", "cli"]
# 支持通配符和显式路径

exclude = ["packages/legacy"]  # 可选:排除某些目录

[tool.uv.sources]
# 把 shared 解析为 workspace 成员(而不是去 PyPI 找)
shared = { workspace = true }

成员包的 pyproject.toml

# packages/api/pyproject.toml
[project]
name = "api"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
  "fastapi>=0.110",
  "shared",   # 依赖另一个 workspace 成员
]

[tool.uv.sources]
shared = { workspace = true }

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Workspace 的工作方式

常用 workspace 命令

# 在根目录
uv sync                         # 同步所有成员的依赖
uv sync --package api           # 只同步 api 的依赖(其他成员不装)
uv run --package api uvicorn api.main:app     # 在 api 环境中运行

# 在任意子目录(uv 会自动向上找根)
cd packages/api
uv sync                         # 效果同根目录的 uv sync
uv add httpx                    # 添加到当前包的 dependencies

# 构建所有成员
uv build --all-packages

# 构建特定成员
uv build --package shared

依赖跨成员共享的 4 种模式

模式 1:workspace 源依赖(最常见)
api 依赖 shared:dependencies = ["shared"] + [tool.uv.sources] shared = { workspace = true }。构建发布时 uv 会把 shared 作为正常版本发布。
模式 2:editable workspace
默认情况下 workspace 成员之间就是 editable 安装——你改 shared 的代码,api 的测试立刻看到变化,不需要重装。
模式 3:共享 dev 依赖
在根 pyproject 的 [dependency-groups].dev 里放所有成员共用的开发依赖(pytest、ruff、mypy),所有成员可直接使用。
模式 4:成员特定依赖
每个成员在自己的 pyproject 里声明。但 uv 会把所有成员的依赖合并解析到同一个 lock(需要兼容)。

发布多包

workspace 成员可以独立发布到 PyPI:

# 1. 只构建 shared 包
uv build --package shared
# 生成 dist/shared-0.1.0-py3-none-any.whl

# 2. 发布
uv publish dist/shared-*.whl

# 3. 发完之后再发 api(api 依赖的是已发布的 shared)
uv build --package api
uv publish dist/api-*.whl
版本管理建议

Monorepo 里多个包可以选择 统一版本号(像 Next.js、Nx 那样所有包一起发)或 独立版本号(像 npm 的 lerna --independent)。独立版本更灵活但管理复杂。小团队建议统一版本;若业务拆分清晰可独立。

实战:一个典型的 Python Monorepo

company-platform/
├── pyproject.toml            # workspace root
├── uv.lock
├── packages/
│   ├── models/               # Pydantic 数据模型
│   │   └── src/models/
│   ├── db/                   # SQLAlchemy 封装
│   │   └── src/db/
│   ├── auth/                 # 认证中间件
│   │   └── src/auth/
│   └── telemetry/            # 日志/指标/追踪
│       └── src/telemetry/
├── services/
│   ├── api/                  # FastAPI 主服务
│   ├── worker/               # Celery 后台任务
│   └── scheduler/            # APScheduler 定时
└── tools/
    ├── migrate/              # 数据库迁移 CLI
    └── admin/                # 运维 CLI
# 根 pyproject.toml
[tool.uv.workspace]
members = ["packages/*", "services/*", "tools/*"]

[tool.uv.sources]
models = { workspace = true }
db = { workspace = true }
auth = { workspace = true }
telemetry = { workspace = true }

[dependency-groups]
dev = ["pytest", "pytest-asyncio", "ruff", "mypy", "pre-commit"]

IDE 配置

VS Code / PyCharm 要指向根目录的 .venv/bin/python。由于成员都是 editable 安装,代码跳转、类型检查、refactor 在 Monorepo 里完全流畅。

// .vscode/settings.json
{
  "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
  "python.analysis.extraPaths": [
    "packages/models/src",
    "packages/db/src",
    "packages/auth/src",
    "packages/telemetry/src"
  ]
}
本章小结

workspace = 多包 + 单 lock + 单 venv,让 Python Monorepo 终于像 npm/Cargo 一样体验丝滑。核心三个配置:[tool.uv.workspace].members 声明成员、[tool.uv.sources] 把内部依赖解析为 workspace、每个成员自己的 pyproject 声明依赖关系。下一章处理一个折磨所有 AI 开发者的话题——PyTorch CUDA 多索引源。