uv (Python dependency manager)

The repo manages its top-level Python dependencies with uv. The nested buildscripts/bazel_rules_mongo/ package still uses Poetry — see its own pyproject.toml/poetry.lock for that lifecycle.

What lives where

File Purpose
pyproject.toml Source of truth. Declares deps under [project.dependencies] and PEP 735 [dependency-groups], plus uv-specific config under [tool.uv.*].
uv.lock Pinned resolution of pyproject.toml. Generated by uv lock --static-urls (rules_pycross requires per-wheel URLs). Cross-platform via PEP 508 markers.
bazel/uv/defs.bzl Compatibility shim exposing dependency(name, group=None)@pypi//<name>, matching the prior rules_poetry call sites verbatim.
buildscripts/uv_sync.sh Workstation/CI bootstrap. Installs the pinned uv into the active venv and runs uv sync --all-groups --no-install-project.
buildscripts/uv_version.txt Pinned uv version (kept in sync with [tool.uv] in pyproject.toml).
buildscripts/uv_lock_check.py Pre-submit check: fails if uv.lock is stale relative to pyproject.toml.

Workflows

Edit a dependency

  1. Modify pyproject.toml under [project.dependencies] or one of the [dependency-groups] tables.
  2. buildscripts/uv_sync.sh -f — refreshes uv.lock (uses --static-urls).
  3. Commit pyproject.toml and uv.lock together.

Populate the workstation venv

source python3-venv/bin/activate
buildscripts/uv_sync.sh

Reference a dependency from BUILD.bazel

load("//bazel/uv:defs.bzl", "dependency")

py_library(
    name = "...",
    deps = [
        dependency("pyyaml"),
        dependency("boto3", group = "aws"),  # group arg is accepted but ignored at build time
    ],
)

The group kwarg is preserved from the prior rules_poetry API for source compatibility. All groups are merged into the single @pypi hub; the package label is the same regardless of group.

Add/skip groups at install time

# `--active` targets the activated venv; without it (or
# UV_PROJECT_ENVIRONMENT) uv installs into `<repo>/.venv`.
# `--locked` refuses to run if uv.lock is stale instead of rewriting it.
uv sync --active --locked --all-groups                                       # everything (default for dev)
uv sync --active --locked --only-group lint                                  # just the lint group
uv sync --active --locked --all-groups --no-group powercycle-incompatible    # powercycle remote setup

Bazel integration

MODULE.bazel uses rules_pycross's lock_import.import_uv extension to consume uv.lock directly:

pyproject.toml + uv.lock
        │
        │  lock_import.import_uv(lock_file = "//:uv.lock", repo = "pypi")   (MODULE.bazel)
        ▼
@pypi//<package>          (http_file for wheels, pycross_wheel_build for sdists)
        ▲
        │  //bazel/uv:defs.bzl::dependency()
        │
BUILD.bazel:  deps = [ dependency("pyyaml") ]

Wheels resolve to http_file targets (remote-cacheable). Sdists become pycross_wheel_build actions (real Bazel actions — remote-executable). Both consume the URLs and hashes baked into uv.lock; no per-build simple-index fetch.

Why uv

  • Speed: uv sync and uv lock are an order of magnitude faster than Poetry.
  • Single Rust binary — no pip/python startup tax.
  • PEP 735 native — dependency groups are a standardized concept.
  • Preserves cross-platform resolution via PEP 508 markers (Linux x86_64 / aarch64 / ppc64le / s390x, macOS x86_64 / arm64, Windows x86_64).