pip workflow.
Where pip reads a flat requirements.txt, uv reads a uv.lock lockfile that
captures the full dependency graph, including every package, version, and download checksum, in a single file that works on any platform.
Load the extension from
@aspect_rules_py//uv/unstable:extension.bzl.
The unstable path means the extension API may change between
releases, not that the feature is unstable.What changes from a requirements.txt workflow
- One
uv.lockcovers every platform. - The lockfile records the full dependency graph, not a flat list.
- You can build Linux container images from a Mac; see Cross-platform builds.
How uv works
uv introduces the concept of a hub: a central Bazel repository that
provides all your Python packages at addresses like @pypi//requests or
@pypi//flask. The hub is backed by one or more venvs,
or virtual environments, each representing a different set of packages. A
Bazel flag controls which venv is active for a given build.
Setup
Step 1: Generate a uv.lock
Theuv_bin toolchain in Step 2 provides a Bazel-managed uv, so you don’t need to install it. If you already have a pyproject.toml, run:
requirements.txt, create a pyproject.toml and import your requirements:
uv binary on your PATH works the same way.
Step 2: Configure MODULE.bazel
Add theuv extension to your MODULE.bazel and declare a hub:
[dependency-group] defined in your pyproject.toml becomes a named venv configuration. If you have no dependency groups, rules_py creates an implicit default using the project name.
Step 3: Set a default venv in .bazelrc
Add a line to.bazelrc to tell Bazel which venv to use by default. Replace
myproject with the name from your pyproject.toml:
Step 4: Use packages in BUILD files
Reference packages from the hub in yourpy_binary, py_library, or
py_test targets:
Working with multiple dependency sets
If your project has different dependency sets, for example one for production and one for development or testing, you can register them as separate venvs within the same hub. Define them as[dependency-group] entries in your pyproject.toml:
venv attribute:
Cross-platform builds
uv can build packages for a different operating system or CPU architecture than the machine you’re building on, for example a Linux/ARM64 container image from a Mac.
Define a target platform
Define aplatform() target that describes the machine you’re building for. The platform_libc and platform_version flags tell uv which version of the C standard library is available on that target, which is important for packages that include compiled C code:
Build a container image
Usepy_image_layer and platform_transition_filegroup to produce a
container image layer set built for your target platform:
Swapping in a local package
Useuv.override_package in MODULE.bazel to replace a downloaded package
with a local Bazel target. Use it for vendoring, patching, or
iterating on a fork of a dependency:
@pypi//cowsay now gets your local version
instead of the one from PyPI.
Constraining library compatibility
By default, apy_library target works with any venv that provides its required packages. But sometimes you need to prevent a library from being used with a particular venv. For example, this is useful during a migration or when the library requires a package version unavailable in that venv.
Use the compatible_with and incompatible_with helpers from your hub’s
defs.bzl:
Best practices
Use one hub. You can name the hub anything you like, oftenpypi to match the old pip.parse convention, but keep one per repository. Different dependency sets belong as separate venvs inside the same hub, not as separate hubs. Multiple hubs lead to version conflicts where two parts of your build use different versions of the same package.
Set your default venv in .bazelrc. This keeps the scope of the default well-bounded to your repository. Don’t set defaults in MODULE.bazel.
Keep setuptools and build in your lockfile. uv needs these Python build tools to compile packages from source, called sdists. If they’re missing and a package requires a source build, you get a configuration error. Add them to your pyproject.toml dependencies.
Tips
Entrypoints aren’t auto-detected. Tools likeruff or black that install command-line scripts need their entrypoints manually declared as Bazel targets. uv installs packages during the build rather than at setup time, so it can’t inspect installed files the way pip does.
Annotations for sdist builds. The uv.lock format doesn’t record what packages are needed specifically for building other packages from source. If you’re hitting errors with sdist builds, annotations are the workaround.
No default venv error. If Bazel reports a missing venv, add common --@pypi//venv=<name> to your .bazelrc.
