Build policy¶
Some Python packages publish dependency metadata that nab can
read without running any code; others require invoking a PEP 517
build backend. [tool.nab].build-policy is the single knob that
controls how far nab is willing to go.
Three levels, strictest first. Each level reads static metadata from every source it admits; the difference is what is permitted to fall through to a backend invocation when the static read returns nothing usable.
The default is build-local: local checkouts and workspace
members may invoke a backend, but remote sources (PyPI sdists,
VCS clones, archive sources) are read statically only. Lift
a specific package to build-remote with a per-package
override when you know it needs a real build
([tool.nab.packages.<name>]); keep the global default tight
rather than enabling builds for the whole graph.
never¶
Static metadata only, from any source:
Wheels: always fine; their
METADATAis static by definition.PEP 643 (Metadata 2.2+) sdists: fine; the resolver reads the static
Requires-Distlines fromPKG-INFO.Sdists whose
PKG-INFOdeclaresDynamic: Requires-Dist: nab falls back to reading[project].dependenciesfrom the bundledpyproject.tomlwhen the field is not listed under[project].dynamic. Anything beyond that is skipped at look-ahead and surfaces as a no-version diagnostic if no candidate ultimately works.Local checkouts declared via
[[tool.nab.local-sources]]: the directory’spyproject.tomlis read statically. A member that requiresdynamic = ["dependencies"]is skipped.VCS clones declared via
[[tool.nab.vcs-sources]]: the clone is fetched and itspyproject.tomlis read statically. Same skip behaviour for dynamic deps.Archive sources declared via
[[tool.nab.archive-sources]]: the.tar.gzis downloaded, hash-verified, and extracted, then itspyproject.tomlis read statically. Same skip behaviour for dynamic deps.
Picks the most reproducible posture: every input to the SAT
problem is a file read, not a sandboxed subprocess. Use never
when you want a lockdown resolve with no backend invocations at
all.
build-local (default)¶
Adds PEP 517 backend invocation on local checkouts. When a
[[tool.nab.local-sources]] entry (or a workspace member) has
dynamic = ["dependencies"], the project’s
[build-system].build-backend runs inside an isolated venv via
nab_python._build.runner and the
resulting wheel METADATA is used. Remote PyPI sdists, VCS
clones, and archive sources remain static-only.
build-remote¶
Builds extend to VCS clones, archive sources, and remote PyPI
sdists. On top of build-local:
VCS-cloned trees with dynamic deps have the backend invoked on the clone.
Archive sources declared via
[[tool.nab.archive-sources]]with dynamic deps have the backend invoked on the extracted tree; the bytes are network-fetched, so they count as remote.PyPI sdists whose
PKG-INFOis dynamic and which have no staticpyproject.tomlfallback are downloaded, extracted to a temp directory, and built.
A backend failure on any of these surfaces as
UnsupportedSdistError; the resolver skips that version, then
either picks the next candidate or, if no candidate works,
reports the accumulated build failures as a no-version
diagnostic. Honesty over silence: a version that needs a build
which fails is treated as unbuildable, not as having zero
dependencies.
Choosing a level¶
The default build-local handles the common case (a local
checkout with dynamic = ["version"] from hatch-vcs or similar)
without opening the door to remote-sdist builds. Lower to
never when you want a fully hermetic resolve.
For transitive dependencies that only publish a dynamic sdist
(native or CUDA-heavy wheels are the usual offenders), prefer a
per-package override rather than raising the global to build-remote:
[tool.nab.packages.deepspeed]
build-policy = "build-remote"
That keeps the rest of the graph in the hermetic default while
permitting the one package you actually need to build. When you
know the package’s dependencies, a dependencies metadata override
(see the configuration reference) resolves it under
never without building at all.
Overrides¶
A per-package override replaces the global build policy for its selected packages, in either direction. Key it by name:
[tool.nab]
build-policy = "never"
[tool.nab.packages.deepspeed]
build-policy = "build-remote"
Or list several packages in one [[tool.nab.package-rules]] entry:
[[tool.nab.package-rules]]
match = ["deepspeed", "flash-attn"]
build-policy = "build-remote"
Set the build policy for every package served from a given index with a per-index override instead:
[tool.nab.index.internal]
build-policy = "build-remote"
A build-policy override for a local checkout, VCS clone, or archive
source is matched by bare name only. A source build is decided before
any version is resolved, so a version-scoped per-package override (a
quoted "name <specifier>" key) does not govern a local, VCS, or
archive source build, and per-index overrides do not apply to sources
(a local source has no serving index). Use a bare-name key to govern
a source build.
A declared platform forbids host builds¶
A PEP 517 backend always runs on the host nab runs on, so it reports the
host’s dependencies. That is correct when you resolve for the host, but
wrong when you resolve as if you were on another machine. Every target
that moves the platform axis therefore forbids host builds:
build-policy is forced to never, and an explicit non-never value
(global or in any override) is a config error, checked before the resolve
starts. That covers both surfaces that declare a machine:
[tool.nab.environment]with aplatformor animplementation.mode = "universal", where every matrix tuple declares one.
This matches pip, which requires --only-binary=:all: under --platform,
--abi, or --implementation.
A retarget of the python axis alone ([tool.nab.environment].python,
or --python X.Y) is different: the machine is still the host. nab warns
that a build would report the host interpreter’s metadata, and permits it.
This is a deliberate deviation from pip: the workspace build-local floor
exists for members with dynamic metadata, and forbidding a build here would
break every workspace that also pins a Python. Set build-policy = "never"
to forbid it.