If you’re using aspect_rules_py with the uv extension to manage Python deps in Bazel, and you have a package that’s system-installed (not pip-installed) — like torch from an NVIDIA NGC Docker image — there’s a clean escape hatch I didn’t know about until today.
The Problem
In our monorepo, torch is gated to macOS in pyproject.toml (sys_platform == 'darwin') because on Linux CI it comes pre-installed in the Docker image. This means uv.lock has macOS wheels for torch but nothing for Linux. When Bazel generates the @pypi//torch target, the select() statement has no Linux match and you get:
no match for @platforms//os:linux in this configuration
62% of our Python packages depend on torch. That’s… a problem.
(The full story is in How Bazel Resolves Python Deps (and Why torch Breaks Everything).)
The Fix
aspect_rules_py (1.8.4+) has uv.override_requirement():
# MODULE.bazel
uv.override_requirement(
hub_name = "pypi",
venv_name = "default",
requirement = "torch",
target = "//third_party/torch:torch", # your custom target
)
That //third_party/torch:torch can be whatever you want — a py_library wrapping system site-packages, a prebuilt wheel, an empty placeholder for CI. The point is: you control it.
How It Works
I went spelunking through extension.bzl to understand the mechanism. Three things happen:
-
_parse_overrides()validates that the requirement actually exists inuv.lock. This is a sanity check — you can’t override a package Bazel doesn’t know about. -
When an override exists, it completely replaces the wheel install repo. No sdist builds, no whl downloads, no install steps. The entire pip machinery is bypassed.
-
The swap happens in hub repo generation:
overrides.get(package, _whl_install_repo_name(...)). If there’s an override, your custom target wins. If not, normal wheel resolution proceeds.
Clean and surgical. No monkey-patching, no wrapper hacks.
One Gotcha
The overridden package must still exist in uv.lock. Our torch works because there are macOS wheels in the lockfile — the validation passes. But packages like flash-attn or triton that are completely absent from the lockfile (e.g., sys_platform == 'never') can’t be overridden this way. Those need a different approach.
The Smoke Test
After wiring this up:
- macOS:
@pypi//torchresolves,bazel testpasses, torch 2.7.0 imports, tensor ops work - Linux CI: test target has
target_compatible_with = ["@platforms//os:macos"], correctly SKIPs
That’s it. One uv.override_requirement() call, and 62% of our packages stop failing resolution.