Generated Computers
Locals: an Apple Silicon engine that runs local LLMs up to 3x faster. A product of Generated Computers.
Translation pending
Quality gate for any Python project — ruff, mypy and pytest behind one command, with a typing-strictness dial read from pyproject.toml.
Developer toolsPython · MakefileGitHub @mauriciobenjamin700OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Documentação: Português (BR) · English (US)
One command for the quality gate of any Python project — ruff +
mypy + pytest, with a typing-strictness dial that lives in your
pyproject.toml instead of in four different Makefile targets.
Framework-agnostic on purpose: Django, Flask, Litestar, FastAPI, a
library, a script. It brings ruff along, so the gate runs the moment
you install it; typer is the only other runtime dependency.
uv add --dev tempest-cli
tempest-cli check # lint + fmt-check + type + test, in order, stops at the first failure
tempest-cli fix # every ruff autofix, then format
tempest-cli type -s strict # override the configured strictness for one run
tc check # `tc` is the short alias for the same program
The four commands are always the same, and every project rewrites them
slightly differently — a Makefile here, a tox.ini there, a CI job that
drifts from what runs locally. tempest-cli check is the same gate on
your machine and in CI, and --strictness makes "how much typing do we
enforce" a value in pyproject.toml rather than a flag someone
remembered to pass.
| Command | Runs |
|---|---|
tempest-cli lint | ruff check |
tempest-cli fix | ruff check --fix-only, ruff format, wraps long comments and splits long strings the formatter leaves alone, then a final ruff check that explains what is left (--unsafe for the risky autofixes) |
tempest-cli format | ruff format (writes) |
tempest-cli fmt-check | ruff format --check (read-only) |
tempest-cli type | mypy |
tempest-cli test | pytest, in parallel by default with pytest-xdist (-n auto -p no:cacheprovider, -w N to choose; serial with a notice when it is not installed; --serial opts out) |
tempest-cli check | all four, in order, stopping at the first failure (the test step in parallel as in test; a host CLI can add steps between mypy and pytest) |
tempest-cli pr-prompt | builds the prompt that makes an AI write this branch's PR description |
Every command takes an optional path (tempest-cli lint src/) and
returns the underlying tool's exit code, so CI reads it exactly as it
would read ruff directly.
[tool.tempest]
typing_strictness = "strict" # lenient | standard | strict
The level adds flags on top of your own [tool.ruff] /
[tool.mypy] — it never relaxes what you already configured:
| Level | ruff (extra ANN rules) | mypy |
|---|---|---|
lenient | none | none |
standard | ANN001, ANN201, ANN202, ANN205, ANN206 | --disallow-untyped-defs --disallow-incomplete-defs |
strict | the above plus ANN204 | --strict |
ANN401 is never enabled at any level: Any is a legitimate
annotation. The levels enforce that things are annotated, never that
they avoid Any.
Override per run with --strictness / -s. Absent config means
standard.
Everything that is not typing — silencing an untyped library, skipping
generated code, dropping a rule that does not fit your framework — is
ordinary [tool.ruff] / [tool.mypy] configuration. The recipes,
including the one that takes a legacy Django project to a green gate,
are in Tuning the rules
(EN).
tempest-cli pr-prompt | claude -p
tempest-cli pr-prompt develop --lang en --out pr_prompt.txt
The prompt carries the repository's own pull-request template (or a bundled PT-BR / EN-US default), the rules that stop a model from handing back the template with its placeholders intact, and the branch context: commit subjects, changed files, and a bounded excerpt of each patch.
Diffs are read as base...head — the merge-base diff the forge shows —
so commits that landed on the base after the branch started are not
attributed to it. Whatever the bounds leave out is stated inside the
prompt, so a partial diff reads as partial.
from tempest_cli import load_tempest_config, run_full_check
config = load_tempest_config()
exit_code = run_full_check(".", config=config)
And to expose the same gate from your own CLI, without copying command bodies:
import typer
from tempest_cli.main import register_commands
cli: typer.Typer = typer.Typer(name="mytool")
register_commands(cli)
A framework that wants its own rules in check passes them as
CheckSteps — they run after mypy and before pytest, and the first
non-zero exit code stops the gate:
import typer
from tempest_cli import CheckStep
from tempest_cli.main import register_commands
def layers(target: str) -> int:
"""Return 0 when every module under ``target`` follows the layering."""
return 0
cli: typer.Typer = typer.Typer(name="mytool")
register_commands(cli, extra_steps=[CheckStep(name="layers", run=layers)])
run_full_check(".", extra_steps=[...]) takes the same list.
ruff comes with the package — six of the eight commands are ruff,
so lint, fix, format and fmt-check work straight after
uv add --dev tempest-cli, with nothing else to install. It is a static
binary wheel with no Python dependencies of its own, so nothing of its
propagates into your resolution.
mypy and pytest are deliberately left to you — a mypy bump changes
which errors your code reports, and pytest has to match your plugins and
your suite. Add them yourself, or take the bundle:
uv add --dev "tempest-cli[tools]" # mypy + pytest + pytest-xdist
pytest-xdist is what test runs the suite in parallel with, by
default (without it the suite runs serially, with a notice); its
requires-dist (execnet>=2.1, pytest>=7.0.0) carries no upper bound.
Whatever you pin wins over the bundled ruff. The lookup runs in this order:
$VIRTUAL_ENV, then the nearest .venv
up the tree;tempest-cli itself runs from (where the bundled
ruff lives);PATH, skipping a pyenv/asdf shim that dispatches nowhere (the one
that answers pyenv: ruff: command not found);uv run --with <tool> <tool>, when uv is available.This package was extracted from
tempest-fastapi-sdk,
where the same gate shipped as tempest check. Getting it meant
installing FastAPI, SQLAlchemy, Alembic and Pydantic — 38.7 MB of
dependencies and roughly 0.5 s of import time per invocation, for four
commands that never touch any of it.
tempest check keeps working: the SDK depends on this package and
registers the same commands. Both stay in sync because there is now one
implementation.
MIT — see LICENSE.