Lock with multiple indexes

Warning

Multi-index support is experimental. Cross-index attribution in the lockfile (which index served which package) is recorded but consumer behaviour across installers varies; the schema may tighten in future.

Resolve against PyPI plus a second index, with one package pinned to the second index.

Project setup

# pyproject.toml
[project]
name = "example"
version = "0.1.0"
dependencies = [
    "torch",
    "numpy",
]

[[tool.nab.indexes]]
name = "pypi"
url  = "https://pypi.org/simple/"

[[tool.nab.indexes]]
name = "torch-cpu"
url  = "https://download.pytorch.org/whl/cpu"

[tool.nab.packages.torch]
index = "torch-cpu"

Routing lives on the per-package override, via the index body field, alongside the other per-package policies (see the configuration reference). To route several packages to one index, list them in a [[tool.nab.package-rules]] entry instead:

[[tool.nab.package-rules]]
match = ["torch", "torchvision", "torchaudio"]
index = "torch-cpu"

A routing entry must use bare-name selectors: the routing decision happens before any version is known, so a version specifier alongside index is rejected, and a package may have only one route.

How nab routes the request

  • numpy has no routing override, so nab walks [pypi, torch-cpu] and picks the first index that lists it (PyPI).

  • torch has a routing override, so nab consults only torch-cpu.

If torch-cpu does not list torch, resolution fails for that requirement. Strict pinning is the point: silent fallthrough is a foot-gun on an index the override was meant to govern.

Per-index policy

[tool.nab.index.<name>] applies a policy to every package served from an index. Here every package that comes from PyPI is wheel-only, while packages from the torch index keep the global default:

[tool.nab.index.pypi]
dist-policy = "wheel-only"

A package is attributed to a single serving index: its route target if it has one, otherwise the first index in declared order that lists it. A per-index override therefore governs a package only through that one attributing index, not through every index that could also serve it.

Policy across both surfaces is an error, not a precedence

The per-package and per-index surfaces are not ranked. If a per-package override and the per-index override for the serving index both set the same field for a candidate, the resolve raises a clear error rather than picking one:

# torch routes to torch-cpu and this per-package override sets its
# dist-policy; the per-index override sets dist-policy for everything from
# torch-cpu.  A torch candidate served from torch-cpu is governed by both,
# so the resolve errors.  Remove one of the two settings.  (Both keys live
# in the one [tool.nab.packages.torch] table; a package cannot have two.)
[tool.nab.packages.torch]
index = "torch-cpu"
dist-policy = "wheel-only"

[tool.nab.index.torch-cpu]
dist-policy = "sdist-only"

Run

nab lock pyproject.toml

Notes

  • Index ordering is significant. Reorder the [[tool.nab.indexes]] entries to change which index wins for any package without a route.

  • Routing carries no version scope and no marker: a package has at most one route, fixed for the whole resolve.