Aspect’s Python rules use uv as the recommended package manager, which replaces the traditionalDocumentation Index
Fetch the complete documentation index at: https://docs.aspect.build/llms.txt
Use this file to discover all available pages before exploring further.
pip workflow.
Where pip reads a flat requirements.txt, uv reads a uv.lock lockfile that
captures the full dependency graph that includes every package, every version, and every download checksum in a single file that works on any platform.
The uv integration is currently experimental. The API is stable enough for
production use, but some advanced features may change between releases.
Load it from
@aspect_rules_py//uv/unstable:extension.bzl.Why uv instead of pip?
| pip / rules_python | uv | |
|---|---|---|
| Lockfile format | requirements.txt (flat list) | uv.lock (full dependency graph) |
| Cross-platform | Separate file per OS | Single file, all platforms |
| Cross-platform container builds | Not supported | Build Linux images from Mac |
| Build speed | Slow (runs at Bazel startup) | Fast (lazy, runs only when needed) |
| Dependency cycles | Manual resolution required | Automatic |
| Private package registries | Limited | Full support |
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. Under the hood, 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
If you already have apyproject.toml, run:
requirements.txt, uv can convert it
for you. This script creates a temporary project, imports your requirements,
and generates the lockfile:
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, producing a Linux/ARM64 container image from a Mac. This makes docker run your app possible without a Linux machine.
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. This is useful for vendoring, patching, or
iterating on a fork of a dependency. For example:
@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 just 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 any package requires a source build, you’ll get a configuration error. Just add them to your pyproject.toml dependencies to be safe.
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 flags a missing venv, make sure you’ve added common --@pypi//venv=<name> to your .bazelrc.
