Skip to main content

tempest-cli

PyPI Python License: MIT

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

Why it exists

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.

Commands

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.

Typing strictness

[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).

PR descriptions from the branch itself

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.

Use it as a library

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.

Where the tools come from

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:

  1. the project's environment — $VIRTUAL_ENV, then the nearest .venv up the tree;
  2. the environment tempest-cli itself runs from (where the bundled ruff lives);
  3. PATH, skipping a pyenv/asdf shim that dispatches nowhere (the one that answers pyenv: ruff: command not found);
  4. uv run --with <tool> <tool>, when uv is available.

Relationship with tempest-fastapi-sdk

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.

License

MIT — see LICENSE.

Metadata

Release files for tempest-cli 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tempest-cli 0.6.0
File Size Uploaded Provenance
tempest_cli-0.6.0.tar.gz 171.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for tempest-cli 0.6.0
File Interpreter ABI Platform
tempest_cli-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 216.0 kB

Release files / tempest_cli-0.6.0.tar.gz

Download URL tempest_cli-0.6.0.tar.gz
Size 171.6 kB
Tags Source
SHA-256 checksum
How to use checksums
a781341e46cfee8bdb0b2ceeeb20185cbaec34a72344fe6c49d98e8a0261aeab
BLAKE2b-256 checksum
How to use checksums
6684e6b3500463be57978b20caf6a592c3e64b49de471f834f448a2b95e87b46
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / tempest_cli-0.6.0-py3-none-any.whl

Download URL tempest_cli-0.6.0-py3-none-any.whl
Size 44.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f5172532eeebad1b8b66c2f93740b9841c6bd5fcf56c3270f3602a942f10e33c
BLAKE2b-256 checksum
How to use checksums
d71ffe644ff94711ccab644f67087682efc9bc2ce24f924f61c549d7eb2313be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Security for tempest-cli 0.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for tempest_cli-0.6.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page