Installation¶
Or with pip:
Requires Python 3.11+. It brings ruff along — six of the eight
commands are ruff, so the gate runs the moment the package installs. The
other runtime dependency is typer.
It installs two executables: tempest-cli and the short alias tc.
They are the same program — everything in these docs applies to both.
tc is also iproute2's tc(8)
While the environment that installed the package is on PATH, tc
resolves to this CLI. For Linux traffic control, call it by absolute
path (/usr/sbin/tc) — or use tempest-cli and leave tc alone.
Where ruff, mypy and pytest come from¶
ruff comes with the package: install it and lint, fix,
format and fmt-check already work — nothing else to install.
mypy and pytest stay with you. A mypy bump changes which errors your
code starts reporting, and pytest has to match your plugins and your
suite — those versions are the project's call. Add them yourself, or
take the bundle:
Why ruff is a dependency and the other two are not
Six of the eight commands are ruff — a tempest-cli without ruff is
a gate that cannot run. And it costs nothing: ruff is a static
binary with no Python dependencies at all, so no bound of its enters
your project's resolution. mypy and pytest, in contrast, run through
your code and your suite; pinning them here would be deciding for
you.
The lookup order¶
What your project pins wins over the bundled ruff. The CLI runs the first thing it finds, in this order:
- the project's environment —
$VIRTUAL_ENV, then the nearest.venvup the tree; - the CLI's own environment — where the bundled ruff lives;
PATH— skipping a version-manager shim that dispatches nowhere (see below);uv run --with <tool> <tool>whenuvis available — the project's environment plus the tool, with no activation and no prioruv syncrequired.
If nothing resolves, the command exits with 127, naming the missing tool and how to install it, instead of raising a traceback.
Why PATH does not come first
On a pyenv/asdf machine the shims directory is on PATH
globally and holds a stub for every tool any installed version ever
provided. Looking up ruff there finds ~/.pyenv/shims/ruff even
when the project has no ruff at all — and running it prints
pyenv: ruff: command not found. So the run's own environments win
over PATH, and a shim is only accepted after proving it dispatches
(<tool> --version exiting 0).
Verifying¶
In CI¶
Every command returns the underlying tool's exit code, so the job reads
it exactly as it would read ruff directly:
Recap¶
uv add --dev tempest-cli, Python 3.11+,ruffalready included.mypyandpytestare yours — or take"tempest-cli[tools]".- Two executables:
tempest-cliand thetcalias. - What the project pins beats the bundled ruff; then come
PATH(never a dead shim) anduv run --with. - A missing tool means exit 127 with a message, not a traceback.