Referência¶
Gerada a partir das docstrings do pacote via
mkdocstrings.
Superfície de topo¶
tempest_cli
¶
Framework-agnostic quality gate for Python projects.
tempest-cli runs ruff, mypy and pytest behind one command, with a
typing-strictness dial read from [tool.tempest] in the project's
pyproject.toml — and a generator for the prompt that makes an AI fill
a pull-request description from the branch's own diff.
It knows nothing about any web framework. ruff ships with it, so the
lint/format commands work the moment it is installed; mypy and
pytest are left to the project, which pins the versions it wants.
The tools are invoked from the project's environment first, so a pinned
version always beats the bundled one.
tempest-cli check # lint + fmt-check + type + test
tempest-cli fix # every ruff autofix, then format
tempest-cli type -s strict # override the configured strictness
tempest-cli pr-prompt | claude -p
tc check # `tc` is the short alias for the same CLI
Everything is importable too, for a project that would rather wire the gate into its own tooling:
from tempest_cli import load_tempest_config, run_full_check
config = load_tempest_config()
exit_code = run_full_check(".", config=config)
And :func:tempest_cli.main.register_commands mounts the whole gate onto
an existing :class:typer.Typer, so another CLI can expose these
commands under its own name without copying them.
DEFAULT_TYPING_STRICTNESS
module-attribute
¶
DEFAULT_TYPING_STRICTNESS: TypingStrictness = 'standard'
Level applied when the key is absent or no pyproject.toml is found.
TypingStrictness
module-attribute
¶
Allowed values for [tool.tempest] typing_strictness.
TempestConfig
dataclass
¶
TempestConfig(typing_strictness: TypingStrictness = DEFAULT_TYPING_STRICTNESS)
Resolved [tool.tempest] settings.
Attributes:
| Name | Type | Description |
|---|---|---|
typing_strictness |
TypingStrictness
|
How strictly the CLI gates
enforce typing. One of |
ruff_ann_select
¶
Return the ANN rule codes to add to ruff for this level.
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: Rule codes for |
list[str]
|
|
Source code in tempest_cli/config.py
mypy_flags
¶
Return the extra mypy flags to add for this level.
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: Flags layered on top of the project's |
list[str]
|
|
Source code in tempest_cli/config.py
GitError
¶
Bases: RuntimeError
A git invocation failed, or the repository lacks what was asked.
Carries the command's own stderr so the caller can print the
reason git gave instead of a generic failure.
PromptLanguage
¶
Bases: StrEnum
Language of the bundled template and of the prompt's instructions.
Only the bundled template is translated: a repository template is used verbatim in whatever language it was written in, since it is that repository's contract.
find_pyproject
¶
Locate the nearest pyproject.toml walking up from start.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
Path | None
|
Directory to begin the search. Defaults to the current working directory. |
None
|
Returns:
| Type | Description |
|---|---|
Path | None
|
Path | None: The path to the first |
Path | None
|
|
Source code in tempest_cli/config.py
load_tempest_config
¶
load_tempest_config(start: Path | None = None) -> TempestConfig
Load [tool.tempest] from the nearest pyproject.toml.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
Path | None
|
Directory to begin the search. Defaults to the current working directory. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
TempestConfig |
TempestConfig
|
The resolved config. Falls back to defaults when |
TempestConfig
|
no |
Raises:
| Type | Description |
|---|---|
ValueError
|
When |
Source code in tempest_cli/config.py
resolve_tool
¶
Return an argv prefix invoking executable or None when absent.
Public because callers outside the gate need the same lookup — the
SDK's OpenAPI code generator formats what it emits with the project's
own ruff, and reimplementing the environment/uv run fallback
there would be a second answer to the same question.
Preference order:
- the environments that belong to this run (see
:func:
_environment_dirs): the CLI's own interpreter directory,$VIRTUAL_ENV, then the nearest.venv; executableonPATH— skipped when it resolves to a version-manager shim that does not dispatch anywhere;uv run --with <executable> <executable>whenuvis on thePATH: the project's own environment plus the tool, without requiring activation or a prioruv sync.
Step 3 carries --with on purpose. A plain uv run ruff falls
back to PATH when the project environment has no ruff — landing
right back on the dead shim this lookup just rejected. --with
puts the tool in the run's own overlay, so the command is always the
one that runs. When the project pins a version, uv resolves the
overlay against the project's requirements, so the pin still wins.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
executable
|
str
|
The command name ( |
required |
Returns:
| Type | Description |
|---|---|
list[str] | None
|
list[str] | None: argv prefix to extend with extra arguments, or |
list[str] | None
|
|
Source code in tempest_cli/lint.py
run_full_check
¶
run_full_check(
target: str,
*,
config: TempestConfig | None = None,
fast: bool = False,
workers: str = DEFAULT_FAST_WORKERS,
) -> int
Run the entire quality gate sequentially.
Order: ruff check → ruff format --check → mypy → pytest.
Stops at the first non-zero exit code so failures surface fast.
With fast=True the pytest step runs the way :func:run_pytest
runs it with fast=True. The pytest-xdist preflight happens
before the first step, so a missing plugin fails in a second
instead of after lint and mypy have already run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path inspected by ruff/mypy. Pytest always runs
against the project's configured |
required |
config
|
TempestConfig | None
|
Resolved |
None
|
fast
|
bool
|
When True, run the pytest step in parallel with pytest-xdist. |
False
|
workers
|
str
|
The |
DEFAULT_FAST_WORKERS
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The first non-zero exit code, or |
Source code in tempest_cli/lint.py
run_mypy
¶
run_mypy(target: str, *, config: TempestConfig | None = None) -> int
Invoke mypy <target> with the configured strictness flags.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path passed verbatim to mypy. |
required |
config
|
TempestConfig | None
|
Resolved |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The mypy exit code. |
Source code in tempest_cli/lint.py
run_pytest
¶
Invoke pytest with an optional target.
With fast=True the suite is spread across pytest-xdist workers:
-n <workers> plus -p no:cacheprovider, since several workers
writing .pytest_cache at once is a race that buys nothing. The
cache is what --lf / --ff read, so those belong to serial
runs. Before spawning, the interpreter pytest runs under is asked
whether it can import pytest-xdist; when it cannot, the fix is
printed and nothing runs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str | None
|
Optional pytest path filter. |
required |
fast
|
bool
|
When True, run the suite in parallel with pytest-xdist. |
False
|
workers
|
str
|
Worker count for |
DEFAULT_FAST_WORKERS
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The pytest exit code, or :data: |
int
|
pytest — or, with |
Source code in tempest_cli/lint.py
run_ruff_check
¶
run_ruff_check(target: str, *, config: TempestConfig | None = None) -> int
Invoke ruff check <target> with the configured ANN rules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path passed verbatim to ruff. |
required |
config
|
TempestConfig | None
|
Resolved |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The ruff exit code. |
Source code in tempest_cli/lint.py
run_ruff_fix
¶
run_ruff_fix(
target: str, *, unsafe: bool = False, config: TempestConfig | None = None
) -> int
Apply every automatic fix ruff can perform, then format the target.
Runs in two passes so the second one sees the rewritten file:
ruff check --fix [--unsafe-fixes] <target>— autofix imports (sort + dedupe), remove unused imports, normalize string quotes, drop trailing whitespace, fix the rest of the lint rules that have safe (or, withunsafe=True, also unsafe) autofixers.ruff format <target>— normalize indentation, line length, blank lines and trailing newlines.
Both passes always run. ruff check --fix exits non-zero whenever
any residual violation remains that it cannot autofix (an
over-length string/comment, an undefined name, etc.) — even though
it already rewrote everything it could. Short-circuiting on that
exit code would skip ruff format entirely, leaving the file
un-wrapped and its extra blank lines intact. So the formatter runs
unconditionally; the lint exit code is surfaced afterwards so CI
still fails on the leftover issues.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path passed verbatim to ruff. |
required |
unsafe
|
bool
|
When True, pass |
False
|
config
|
TempestConfig | None
|
Resolved |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
|
int
|
otherwise the lint pass exit code (residual violations), or the |
|
int
|
format pass exit code when the lint pass was clean. |
Source code in tempest_cli/lint.py
run_ruff_format
¶
Invoke ruff format (write or check-only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path passed verbatim to ruff. |
required |
check
|
bool
|
When True, run |
required |
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The ruff exit code. |
Source code in tempest_cli/lint.py
generate_pr_prompt
¶
generate_pr_prompt(
*,
base: str = DEFAULT_BASE,
head: str | None = None,
cwd: Path | None = None,
template: Path | None = None,
language: PromptLanguage = PT_BR,
max_files: int | None = DEFAULT_MAX_FILES,
max_chars: int | None = DEFAULT_MAX_CHARS,
) -> tuple[str, PullRequestContext, ResolvedTemplate]
Read the repository and render the prompt in one call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
str
|
The base ref the pull request targets. |
DEFAULT_BASE
|
head
|
str | None
|
The branch being described. Defaults to the checked-out one. |
None
|
cwd
|
Path | None
|
Any directory inside the repository. |
None
|
template
|
Path | None
|
An explicit template path. |
None
|
language
|
PromptLanguage
|
Language of the instructions and of the bundled fallback template. |
PT_BR
|
max_files
|
int | None
|
How many files contribute a patch
excerpt. |
DEFAULT_MAX_FILES
|
max_chars
|
int | None
|
Characters kept per patch, or |
DEFAULT_MAX_CHARS
|
Returns:
| Type | Description |
|---|---|
str
|
tuple[str, PullRequestContext, ResolvedTemplate]: The prompt plus |
PullRequestContext
|
the context and template it was built from, so a caller can |
ResolvedTemplate
|
report what was read and what was dropped. |
Raises:
| Type | Description |
|---|---|
GitError
|
When the repository, the base ref or the template path cannot be resolved. |
Source code in tempest_cli/pr_prompt.py
Configuração¶
TempestConfig
dataclass
¶
TempestConfig(typing_strictness: TypingStrictness = DEFAULT_TYPING_STRICTNESS)
Resolved [tool.tempest] settings.
Attributes:
| Name | Type | Description |
|---|---|---|
typing_strictness |
TypingStrictness
|
How strictly the CLI gates
enforce typing. One of |
ruff_ann_select
¶
Return the ANN rule codes to add to ruff for this level.
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: Rule codes for |
list[str]
|
|
Source code in tempest_cli/config.py
mypy_flags
¶
Return the extra mypy flags to add for this level.
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: Flags layered on top of the project's |
list[str]
|
|
Source code in tempest_cli/config.py
TypingStrictness
module-attribute
¶
Allowed values for [tool.tempest] typing_strictness.
load_tempest_config
¶
load_tempest_config(start: Path | None = None) -> TempestConfig
Load [tool.tempest] from the nearest pyproject.toml.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
Path | None
|
Directory to begin the search. Defaults to the current working directory. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
TempestConfig |
TempestConfig
|
The resolved config. Falls back to defaults when |
TempestConfig
|
no |
Raises:
| Type | Description |
|---|---|
ValueError
|
When |
Source code in tempest_cli/config.py
find_pyproject
¶
Locate the nearest pyproject.toml walking up from start.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
Path | None
|
Directory to begin the search. Defaults to the current working directory. |
None
|
Returns:
| Type | Description |
|---|---|
Path | None
|
Path | None: The path to the first |
Path | None
|
|
Source code in tempest_cli/config.py
Runners¶
run_ruff_check
¶
run_ruff_check(target: str, *, config: TempestConfig | None = None) -> int
Invoke ruff check <target> with the configured ANN rules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path passed verbatim to ruff. |
required |
config
|
TempestConfig | None
|
Resolved |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The ruff exit code. |
Source code in tempest_cli/lint.py
run_ruff_fix
¶
run_ruff_fix(
target: str, *, unsafe: bool = False, config: TempestConfig | None = None
) -> int
Apply every automatic fix ruff can perform, then format the target.
Runs in two passes so the second one sees the rewritten file:
ruff check --fix [--unsafe-fixes] <target>— autofix imports (sort + dedupe), remove unused imports, normalize string quotes, drop trailing whitespace, fix the rest of the lint rules that have safe (or, withunsafe=True, also unsafe) autofixers.ruff format <target>— normalize indentation, line length, blank lines and trailing newlines.
Both passes always run. ruff check --fix exits non-zero whenever
any residual violation remains that it cannot autofix (an
over-length string/comment, an undefined name, etc.) — even though
it already rewrote everything it could. Short-circuiting on that
exit code would skip ruff format entirely, leaving the file
un-wrapped and its extra blank lines intact. So the formatter runs
unconditionally; the lint exit code is surfaced afterwards so CI
still fails on the leftover issues.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path passed verbatim to ruff. |
required |
unsafe
|
bool
|
When True, pass |
False
|
config
|
TempestConfig | None
|
Resolved |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
|
int
|
otherwise the lint pass exit code (residual violations), or the |
|
int
|
format pass exit code when the lint pass was clean. |
Source code in tempest_cli/lint.py
run_ruff_format
¶
Invoke ruff format (write or check-only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path passed verbatim to ruff. |
required |
check
|
bool
|
When True, run |
required |
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The ruff exit code. |
Source code in tempest_cli/lint.py
run_mypy
¶
run_mypy(target: str, *, config: TempestConfig | None = None) -> int
Invoke mypy <target> with the configured strictness flags.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path passed verbatim to mypy. |
required |
config
|
TempestConfig | None
|
Resolved |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The mypy exit code. |
Source code in tempest_cli/lint.py
run_pytest
¶
Invoke pytest with an optional target.
With fast=True the suite is spread across pytest-xdist workers:
-n <workers> plus -p no:cacheprovider, since several workers
writing .pytest_cache at once is a race that buys nothing. The
cache is what --lf / --ff read, so those belong to serial
runs. Before spawning, the interpreter pytest runs under is asked
whether it can import pytest-xdist; when it cannot, the fix is
printed and nothing runs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str | None
|
Optional pytest path filter. |
required |
fast
|
bool
|
When True, run the suite in parallel with pytest-xdist. |
False
|
workers
|
str
|
Worker count for |
DEFAULT_FAST_WORKERS
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The pytest exit code, or :data: |
int
|
pytest — or, with |
Source code in tempest_cli/lint.py
run_full_check
¶
run_full_check(
target: str,
*,
config: TempestConfig | None = None,
fast: bool = False,
workers: str = DEFAULT_FAST_WORKERS,
) -> int
Run the entire quality gate sequentially.
Order: ruff check → ruff format --check → mypy → pytest.
Stops at the first non-zero exit code so failures surface fast.
With fast=True the pytest step runs the way :func:run_pytest
runs it with fast=True. The pytest-xdist preflight happens
before the first step, so a missing plugin fails in a second
instead of after lint and mypy have already run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str
|
The path inspected by ruff/mypy. Pytest always runs
against the project's configured |
required |
config
|
TempestConfig | None
|
Resolved |
None
|
fast
|
bool
|
When True, run the pytest step in parallel with pytest-xdist. |
False
|
workers
|
str
|
The |
DEFAULT_FAST_WORKERS
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
The first non-zero exit code, or |
Source code in tempest_cli/lint.py
Prompt de PR¶
generate_pr_prompt
¶
generate_pr_prompt(
*,
base: str = DEFAULT_BASE,
head: str | None = None,
cwd: Path | None = None,
template: Path | None = None,
language: PromptLanguage = PT_BR,
max_files: int | None = DEFAULT_MAX_FILES,
max_chars: int | None = DEFAULT_MAX_CHARS,
) -> tuple[str, PullRequestContext, ResolvedTemplate]
Read the repository and render the prompt in one call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
str
|
The base ref the pull request targets. |
DEFAULT_BASE
|
head
|
str | None
|
The branch being described. Defaults to the checked-out one. |
None
|
cwd
|
Path | None
|
Any directory inside the repository. |
None
|
template
|
Path | None
|
An explicit template path. |
None
|
language
|
PromptLanguage
|
Language of the instructions and of the bundled fallback template. |
PT_BR
|
max_files
|
int | None
|
How many files contribute a patch
excerpt. |
DEFAULT_MAX_FILES
|
max_chars
|
int | None
|
Characters kept per patch, or |
DEFAULT_MAX_CHARS
|
Returns:
| Type | Description |
|---|---|
str
|
tuple[str, PullRequestContext, ResolvedTemplate]: The prompt plus |
PullRequestContext
|
the context and template it was built from, so a caller can |
ResolvedTemplate
|
report what was read and what was dropped. |
Raises:
| Type | Description |
|---|---|
GitError
|
When the repository, the base ref or the template path cannot be resolved. |
Source code in tempest_cli/pr_prompt.py
PromptLanguage
¶
Bases: StrEnum
Language of the bundled template and of the prompt's instructions.
Only the bundled template is translated: a repository template is used verbatim in whatever language it was written in, since it is that repository's contract.
GitError
¶
Bases: RuntimeError
A git invocation failed, or the repository lacks what was asked.
Carries the command's own stderr so the caller can print the
reason git gave instead of a generic failure.
CLI¶
register_commands
¶
Register the quality gate on an existing Typer application.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app
|
Typer
|
The application to extend. Its own name and help text are left untouched; only commands are added. |
required |
Example
Source code in tempest_cli/main.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | |