2025, Dec 26 09:02

uv workspace в Python-монорепозитории: как подтянуть зависимости подпакетов

Почему uv sync в корне workspace ставит только зависимости проекта и пропускает подпакеты. Как исправить: [tool.uv.sources] в корне или uv sync --all-packages.

Рабочие пространства uv и «пропавшие» зависимости подпакетов: что происходит и как это исправить

При настройке монорепозитория Python с uv легко предположить, что запуск uv sync в корне подтянет зависимости для каждого участника рабочего пространства. На практике вы можете увидеть, что устанавливаются только зависимости корневого проекта, а требования подпакетов пропускаются. Ниже — минимальная, воспроизводимая структура, которая демонстрирует это поведение и два точных способа заставить uv установить всё, что вы ожидаете.

Минимальный монорепозиторий, воспроизводящий поведение

Репозиторий содержит корневой проект и три внутренних пакета: базовую библиотеку, оркестратор пайплайнов и комплект dbt. Запуск uv sync в корне устанавливает только зависимости корня; dbt-core, dbt-clickhouse и apache-airflow из подпакетов не появляются.

forge-hub/
├── pyproject.toml
└── modules/
   ├── core-kernel/
   │ └── pyproject.toml
   ├── pipeline-scheduler/
   │ └── pyproject.toml
   └── analytics/
     └── dbt_crafter/
       └── pyproject.toml

Корневой pyproject.toml:

[project]
name = "forge-hub"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["pandas", "sqlalchemy", "tabulate"]
[tool.uv.workspace]
members = [
    "modules/core-kernel",
    "modules/pipeline-scheduler",
    "modules/analytics/dbt_crafter",
]

Подпакеты:

# modules/core-kernel/pyproject.toml
[project]
name = "core-kernel"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["dbt-core", "dbt-clickhouse"]
# modules/pipeline-scheduler/pyproject.toml
[project]
name = "pipeline-scheduler"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["core-kernel", "apache-airflow"]
# modules/analytics/dbt_crafter/pyproject.toml
[project]
name = "dbt-crafter"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["dbt-core", "dbt-clickhouse"]

Что на самом деле происходит при запуске uv sync в корне

Выполнение uv sync в корне репозитория устанавливает зависимости, объявленные корневым проектом. Подпакеты, перечисленные в workspace, распознаются как участники, но их наборы зависимостей не устанавливаются только потому, что они существуют в рабочем пространстве. Поэтому вы получаете pandas, sqlalchemy и tabulate, а dbt-core, dbt-clickhouse и apache-airflow отсутствуют.

Решение: два точных способа установить зависимости подпакетов

Решить это можно двумя путями. Первый — объявить подпакеты зависимостями корневого проекта и связать эти имена с локальными участниками рабочего пространства. Тогда при синхронизации в корне uv подтянет их деревья зависимостей в текущее окружение.

[project]
name = "forge-hub"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
  "core-kernel",
  "pipeline-scheduler",
  "dbt-crafter",
  "pandas",
  "sqlalchemy",
  "tabulate"
]
[tool.uv.workspace]
members = [
    "modules/core-kernel",
    "modules/pipeline-scheduler",
    "modules/analytics/dbt_crafter",
]
[tool.uv.sources]
core-kernel = { workspace = true }
pipeline-scheduler = { workspace = true }
dbt-crafter = { workspace = true }

Второй — явно указать uv установить зависимости для всех пакетов рабочего пространства при синхронизации из корня. Это разовая команда, не требующая изменений в конфигурации вашего pyproject.

uv sync --all-packages

Почему это важно в повседневной работе

Если синхронизировать только корень, в окружении не окажется библиотек, нужных подпакетам. Импорты из этих компонентов будут падать, пока вы либо не объявите подпакеты зависимостями корня, либо не воспользуетесь переключателем all-packages, чтобы установить всё разом. Помня об этом, вы избежите сюрпризов при первичной настройке репозитория на новой машине или подготовке окружения для разработки и запуска.

Главные выводы

В рабочем пространстве uv запуск uv sync на верхнем уровне устанавливает зависимости корневого проекта. Чтобы подтянуть зависимости подпакетов, либо перечислите подпакеты в зависимостях корневого проекта и сопоставьте их через [tool.uv.sources], либо используйте uv sync --all-packages. Выберите вариант, который лучше соответствует тому, как вы хотите создавать окружения в своём монорепозитории, и вы избежите отсутствующих зависимостей у участников рабочего пространства.