CLI¶
nab exposes four subcommands: lock, download, config, and
cache. The first two read project shape from [tool.nab] in the
project’s pyproject.toml or a project-directory nab.toml; the CLI
carries runtime knobs and can override a project key for one run with a
--project-<key> flag. config inspects the layered configuration, and
cache inspects and clears the on-disk cache.
Synopsis¶
nab [--version | -V]
nab [OUTPUT FLAGS] lock [PATH] [RUNTIME OPTIONS] [--output PATH] [--format pylock|requirements|requirements-without-hashes]
nab [OUTPUT FLAGS] download [PATH] [RUNTIME OPTIONS] [--output DIR] [--max-concurrency N]
nab [OUTPUT FLAGS] config {list | get <key> | explain <key>} [--path PATH]
nab [OUTPUT FLAGS] cache {dir | verify | clear} [--cache-dir PATH]
OUTPUT FLAGS are the global verbosity, colour, and progress knobs listed
under Output control below. They are read before the subcommand, so they may
appear anywhere on the line.
PATH is positional and defaults to pyproject.toml in the
current directory. Run nab lock --help (or -h) for the full
per-command flag list. Boolean flags render as a --flag /
--no-flag pair (for example --cache / --no-cache). --offline
is layered, so an explicit --offline True / --offline False
overrides the config layers; bare --offline / --no-offline are
shorthands for those two values.
nab lock¶
Resolve and emit a lockfile or pin list. Three formats:
--format pylock(default) writes a PEP 751pylock.toml.--format requirementswrites a pip-compatiblerequirements.txtwith one--hash=<algo>:<digest>line per recorded digest.--format requirements-without-hasheswrites a sortedname==versionlist with no hashes.
Dependency-group selection (PEP 735):
--groups foo barfolds the named groups from the project’s[dependency-groups]table into the resolve. The selected group names land in the lockfile’s top-leveldependency-groupsarray. The separatedefault-groupsarray records the[tool.nab].default-groupsproject setting, not this run’s selection.--all-groupsselects every group defined in the project.
Extras selection:
--extras foo barfolds entries from the project’s own[project.optional-dependencies]table into the resolve. The selected extra names land in the lockfile’s top-levelextrasarray.--all-extrasselects every declared extra.
Both --groups and --extras produce a single union resolve. A
package that only a selected extra or group reaches is emitted with
a 'X' in extras or 'X' in dependency_groups marker, so an
installer given neither leaves it out; see
Lockfiles.
Workspace flags (see Lock a workspace):
--workspace-discovery(default) walks upward from the locked project for a[tool.nab.workspace]root and prefers its in-tree members over PyPI.--no-workspace-discoveryskips that search, so the run resolves the named project alone.--no-emit-workspacedrops the workspace members’ own[[packages]]entries from the emitted lockfile, along with the dependency edges and membership gates that reference them; the resolver still uses the members during the resolve. Default off (--no-no-emit-workspace). Use it when the lockfile is consumed by pip’s PEP 751 install or--require-hashes, which reject the directory entry a member pin emits because it cannot be hashed; pair it withpip install --no-deps -e <member>.
--output defaults to pylock.toml for pylock and
requirements.txt for the two requirements formats. Pass
--output - to write to stdout instead. A matrix has no default
requirements file: no one file can carry every tuple’s pins (see
below), so the requirements formats print to stdout unless --output
names a template.
A requirements --output template writes one file per tuple. The
variables are {python_version}, {platform_id}, and {selection},
which names the conflict fork a tuple belongs to (extra-cpu,
group-black22.group-isort5, empty when the resolve did not fork):
$ nab lock --format requirements-without-hashes --extras cpu gpu \
--output 'req-{python_version}-{selection}.txt'
Wrote req-3.12-extra-cpu.txt (12 packages, tuple py312-linux_x86_64-extra-cpu)
Wrote req-3.12-extra-gpu.txt (14 packages, tuple py312-linux_x86_64-extra-gpu)
A template that maps two tuples onto one path is rejected, naming the variable that would separate them.
Exits non-zero on resolution failure; the message starts with
error: resolution failed: followed by a derivation tree, and any
captured diagnostics are appended under a Diagnostics: section.
Universal mode ([tool.nab].mode = "universal") is supported for
all three formats:
pylockproduces one PEP 751 file with per-tuplePackageentries gated by markers (python_version,sys_platform,platform_machine, plusimplementation_namewhen the matrix declares a non-CPython implementation or more than one). Versions agreed across every tuple appear once without a marker; divergent versions appear once per(version, source)group with the matching tuples disjoined.requirementsandrequirements-without-hashesemit a sequence of# labelcomment blocks, one per(python, platform, implementation)tuple, followed by that tuple’s pins. Pip’s hash-checking mode cannot install a single requirements.txt across multiple tuples, so the per-tuple block format is for inspection or for tools that consume one block at a time.
Failed tuples render as # {label}: FAILED followed by the
indented error and exit 1.
--python X.Y resolves for that Python on this machine instead of the
running interpreter, like pip’s --python-version; it moves only the
python axis, so a declared [tool.nab.environment].platform stays. It is
rejected in universal mode, where the matrix declares the Python axis.
A project option can be overridden for one run with a --project-<key>
flag: --project-resolution, --project-mode, --project-requires-python,
--project-uploaded-prior-to, --project-dist-policy,
--project-build-policy, and the repeatable --project-constraint and
--project-default-group. Each changes the resolved set, so passing one
prints a reproducibility notice on stderr and records the override in the
lockfile’s [tool.nab] block, since the lock no longer derives from the
committed files alone.
--locked re-resolves and checks that the committed pylock.toml is
already up to date, writing nothing. It exits non-zero if the lock would
change or is missing, so CI can assert the lock is current. It covers
pylock output to a file in single-environment mode.
When a mismatch is provable from the inputs alone, a changed direct
dependency, a narrowed requires-python, a changed extra or group, or a
tightened constraint, --locked fails fast with that reason before
resolving. Otherwise it runs the full re-resolve, and only that comparison
reports the lock up to date: nab is non-sticky, so a lock can satisfy every
input yet be stale once a newer admissible version exists.
--upgrade re-anchors the P<n>D resolve window to the current time
instead of reusing the timestamp recorded in an existing lockfile, and
prints a notice naming the cutoff it dropped.
nab download¶
Resolve and download every wheel, sdist, and direct-URL archive into a local directory. The download is idempotent: files whose recorded sha256 matches a local file are left alone. Local and VCS pins are skipped.
Universal mode ([tool.nab].mode = "universal") re-resolves
across the matrix and downloads the union of every tuple’s
artefacts into the same directory, deduplicated by URL so a
wheel shared across tuples is fetched once.
--outputdefaults towheels/.--max-concurrencycontrols parallel HTTP fetches (default8, minimum1). Layered, so it can also be set in annab.tomlorNAB_MAX_CONCURRENCY.--workspace-discovery(default) mirrorsnab lock: it finds a[tool.nab.workspace]root and resolves against the in-tree members.--no-workspace-discoveryturns that off. See Lock a workspace.
--offline, --cache-dir, --http-backend, --max-concurrency and the
--project-* overrides flow through the same layered config sources nab lock uses, so a NAB_* variable or a system/user/project nab.toml is
read for nab download as for nab lock.
A summary of how many files were written and how many were already present is printed to stderr.
nab config¶
Inspect the effective layered configuration (read-only). Three actions:
nab config listprints every option with its value, scope, and the source it came from.nab config get <key>prints one effective value.nab config explain <key>prints the full source stack for one key, the winning source marked with a>. Array options concatenate every layer instead of one winning, so each source is shown as a contribution and the merged effective value is printed on a trailing= effective:line. Add--include-rejectedto also list sources that tried to set the key but were rejected by the category gate.
The same per-option override flags the run commands accept (the
--project-* overrides and the user knobs --offline, --cache-dir,
--http-backend, --max-concurrency) layer a CLI value on top, so the
inspector reflects the same effective values a run would see.
See Configuration for the source ladder and the
NAB_* environment variables.
nab cache¶
Inspect and clear the on-disk cache. It takes one location selector,
--cache-dir PATH. Without it the root is the one a run in the same
directory uses: cache-dir is read off the config source ladder, so a
nab.toml or NAB_CACHE_DIR sets it too (see
Configuration). Three actions:
nab cache dirprints the resolved cache root to stdout, whether or not it exists yet.nab cache verifywalks the cached index records read-only and reports any corrupt entry by path and reason. Cloned repositories and extracted archives hold upstream files, so they are not parsed.nab cache clearremoves every bucket under the root, including the cloned repositories and extracted archives, returning the cache to cold.
Runtime flags¶
Both subcommands accept the same runtime knobs:
Flag |
Default |
Effect |
|---|---|---|
|
|
Override the on-disk cache root. |
|
off |
Disable cache for this run. A declared VCS or archive source is materialised into a temporary directory instead, so it is refetched every run. Combine with |
|
unset |
Use cache only, never hit the network. Layered: |
|
|
Pick the async transport for fetches. Layered, so it can also be set in an |
A name absent from the index is remembered for a short window, so a repeated lookup is answered from cache, offline included.
urllib3 is the only backend pulled in by the base install. The
others surface a helpful ImportError if selected without the
matching extra.
Global flags¶
Flag |
Effect |
|---|---|
|
Print |
|
Standard argparse-style help. Per-subcommand help works too: |
The verbosity, colour, and progress flags below are also global.
Output control¶
These flags set how much nab writes to stderr, whether it colours it,
and whether it animates a progress line. They are global: nab reads them
before the subcommand, so they work with lock, download, config, and
cache. stdout carries only the requested output (the lockfile, the
requirements list, the config dump), so it stays pipeable at every
verbosity.
Flag |
Effect |
|---|---|
|
Raise verbosity. |
|
Lower verbosity. |
|
When to colour stderr: |
|
Shorthand for |
|
Suppress the live progress line (also |
Verbosity is the count of -v minus the count of -q. The five levels,
quietest first, are silent (-qq), quiet (-q), normal (the default),
verbose (-v), and debug (-vv). Errors print at every level; warnings
print unless -qq; the run summary and notes print at normal and above.
While nab lock or nab download resolves, a live line repaints on
stderr, counting package listings fetched and packages pinned:
Resolving... 12 fetched, 5 pinned
It shows only at normal verbosity on an stderr terminal; --no-progress
(or NAB_NO_PROGRESS) turns it off, and it never writes to stdout.
Environment variables¶
Variable |
Effect |
|---|---|
|
If set, |
|
Default verbosity when no |
|
If set to a non-empty value, suppress the live progress line, like |
|
If set to a non-empty value, disable colour under |
|
If set to a non-empty value, force colour under |
|
|
Exit codes¶
Code |
Meaning |
|---|---|
|
Success. |
|
Resolution failed, lockfile cannot be written (missing hash), download failed, missing |
|
Interrupted with Ctrl-C. |