tempest-cli 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/CHANGELOG.md +53 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/PKG-INFO +15 -4
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/README.md +13 -3
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/changelog.en.md +25 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/changelog.md +24 -0
- tempest_cli-0.4.0/docs/commands.en.md +159 -0
- tempest_cli-0.4.0/docs/commands.md +156 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/configuration.en.md +6 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/configuration.md +6 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/index.en.md +3 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/index.md +3 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/installation.en.md +6 -1
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/installation.md +6 -1
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/library.en.md +1 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/library.md +1 -0
- tempest_cli-0.4.0/docs/rules.en.md +252 -0
- tempest_cli-0.4.0/docs/rules.md +249 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/mkdocs.yml +3 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/pyproject.toml +6 -2
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/__init__.py +1 -1
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/lint.py +231 -7
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/main.py +121 -4
- tempest_cli-0.4.0/tests/test_fast.py +388 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/uv.lock +27 -1
- tempest_cli-0.3.0/docs/commands.en.md +0 -82
- tempest_cli-0.3.0/docs/commands.md +0 -81
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/.github/workflows/ci.yml +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/.github/workflows/docs.yml +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/.github/workflows/release-pypi.yml +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/.gitignore +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/LICENSE +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/Makefile +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/pr-prompt.en.md +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/pr-prompt.md +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/reference.en.md +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/reference.md +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/_templates/pull_request_template.en-US.md +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/_templates/pull_request_template.pt-BR.md +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/config.py +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/pr_prompt.py +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/py.typed +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/__init__.py +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_cli.py +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_config.py +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_lint_runners.py +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_lint_strictness.py +0 -0
- {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_pr_prompt.py +0 -0
|
@@ -5,6 +5,59 @@ All notable changes to **tempest-cli** are listed below.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.0] — 2026-09-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`test --fast` and `check --fast`: the suite in parallel.** `--fast`
|
|
13
|
+
runs pytest as `pytest -n <workers> -p no:cacheprovider [target]`, with
|
|
14
|
+
pytest-xdist spreading the suite across the cores; `--workers` / `-w`
|
|
15
|
+
takes what `pytest -n` takes (an integer, `auto` — the default — or
|
|
16
|
+
`logical`) and is a usage error (exit 2) without `--fast`. The cache
|
|
17
|
+
plugin is off because several workers writing `.pytest_cache` at once
|
|
18
|
+
is a race that buys nothing. The target is forwarded and pytest's exit
|
|
19
|
+
code comes straight back, as in the serial run. It is a flag rather
|
|
20
|
+
than a `test fast` subcommand because `test` already takes a
|
|
21
|
+
positional path: `test fast` keeps meaning the `fast/` folder.
|
|
22
|
+
Requested in mauriciobenjamin700/tempest-fastapi-sdk#328.
|
|
23
|
+
|
|
24
|
+
Measured on a machine with 6 physical cores / 12 threads. On this
|
|
25
|
+
package's own suite (100 tests, warm environment, three runs each)
|
|
26
|
+
`tempest-cli test` took 2.2 s and `tempest-cli test --fast` 1.6–1.7 s
|
|
27
|
+
(12 workers: no `psutil` here, so `auto` counts logical CPUs) — worker
|
|
28
|
+
start-up is most of the fast run at this size. On the
|
|
29
|
+
tempest-fastapi-sdk suite (10 218 tests, one run each, same
|
|
30
|
+
invocation), pytest reported 2127 s serial and 411 s with `--fast`
|
|
31
|
+
(`auto` → 6 workers, since `psutil` is installed there), about 5.2x;
|
|
32
|
+
both runs ended with the same 10 failures, all from `ruff` being a dead
|
|
33
|
+
pyenv shim on that `PATH`.
|
|
34
|
+
|
|
35
|
+
`auto` is pytest-xdist's rule, not ours: physical cores when `psutil`
|
|
36
|
+
is importable, logical CPUs otherwise. `-w logical` asks for the
|
|
37
|
+
threads explicitly.
|
|
38
|
+
|
|
39
|
+
- **A missing pytest-xdist is a sentence, not `unrecognized arguments:
|
|
40
|
+
-n`.** Before spawning, `--fast` asks the interpreter pytest will run
|
|
41
|
+
under — read from the pytest script's shebang, its sibling `python`,
|
|
42
|
+
or the same `uv run --with pytest` overlay — whether it can import
|
|
43
|
+
`xdist`. When it cannot, the message names `pytest-xdist`,
|
|
44
|
+
`tempest-cli[tools]` and `tempest-fastapi-sdk[tests]`, and the exit code
|
|
45
|
+
is 127, with no traceback. `check --fast` makes that check before its
|
|
46
|
+
first step. A probe that cannot run at all is not taken as "missing":
|
|
47
|
+
the run proceeds and pytest reports for itself.
|
|
48
|
+
|
|
49
|
+
- **`run_pytest(..., fast=, workers=)` and `run_full_check(..., fast=,
|
|
50
|
+
workers=)`** for library callers — keyword-only, defaulting to the
|
|
51
|
+
serial run, so every existing call is unchanged. `DEFAULT_FAST_WORKERS`,
|
|
52
|
+
`XDIST_MODULE` and `MISSING_TOOL_EXIT_CODE` are exported from
|
|
53
|
+
`tempest_cli.lint`.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- **`[tools]` now carries `pytest-xdist>=3.8.0`** next to mypy and pytest,
|
|
58
|
+
so the bundle is enough for `--fast`. Its `requires-dist`
|
|
59
|
+
(`execnet>=2.1`, `pytest>=7.0.0`) has no upper bound.
|
|
60
|
+
|
|
8
61
|
## [0.3.0] — 2026-08-15
|
|
9
62
|
|
|
10
63
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tempest-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Framework-agnostic quality gate for Python projects: ruff + mypy + pytest behind one command, with a typing-strictness dial and a PR-description prompt generator.
|
|
5
5
|
Project-URL: Homepage, https://github.com/mauriciobenjamin700/tempest-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/mauriciobenjamin700/tempest-cli
|
|
@@ -26,6 +26,7 @@ Requires-Dist: ruff>=0.8.0
|
|
|
26
26
|
Requires-Dist: typer>=0.12.0
|
|
27
27
|
Provides-Extra: tools
|
|
28
28
|
Requires-Dist: mypy>=1.13.0; extra == 'tools'
|
|
29
|
+
Requires-Dist: pytest-xdist>=3.8.0; extra == 'tools'
|
|
29
30
|
Requires-Dist: pytest>=8.3.3; extra == 'tools'
|
|
30
31
|
Description-Content-Type: text/markdown
|
|
31
32
|
|
|
@@ -73,8 +74,8 @@ remembered to pass.
|
|
|
73
74
|
| `tempest-cli format` | `ruff format` (writes) |
|
|
74
75
|
| `tempest-cli fmt-check` | `ruff format --check` (read-only) |
|
|
75
76
|
| `tempest-cli type` | `mypy` |
|
|
76
|
-
| `tempest-cli test` | `pytest` |
|
|
77
|
-
| `tempest-cli check` | all four, in order, stopping at the first failure |
|
|
77
|
+
| `tempest-cli test` | `pytest` (`--fast` spreads it across cores with pytest-xdist: `-n auto -p no:cacheprovider`, `-w N` to choose) |
|
|
78
|
+
| `tempest-cli check` | all four, in order, stopping at the first failure (`--fast` runs the test step in parallel) |
|
|
78
79
|
| `tempest-cli pr-prompt` | builds the prompt that makes an AI write this branch's PR description |
|
|
79
80
|
|
|
80
81
|
Every command takes an optional path (`tempest-cli lint src/`) and
|
|
@@ -104,6 +105,13 @@ they avoid `Any`.
|
|
|
104
105
|
Override per run with `--strictness` / `-s`. Absent config means
|
|
105
106
|
`standard`.
|
|
106
107
|
|
|
108
|
+
Everything that is *not* typing — silencing an untyped library, skipping
|
|
109
|
+
generated code, dropping a rule that does not fit your framework — is
|
|
110
|
+
ordinary `[tool.ruff]` / `[tool.mypy]` configuration. The recipes,
|
|
111
|
+
including the one that takes a legacy Django project to a green gate,
|
|
112
|
+
are in **[Tuning the rules](https://mauriciobenjamin700.github.io/tempest-cli/rules/)**
|
|
113
|
+
([EN](https://mauriciobenjamin700.github.io/tempest-cli/en/rules/)).
|
|
114
|
+
|
|
107
115
|
## PR descriptions from the branch itself
|
|
108
116
|
|
|
109
117
|
```bash
|
|
@@ -155,9 +163,12 @@ which errors your code reports, and pytest has to match your plugins and
|
|
|
155
163
|
your suite. Add them yourself, or take the bundle:
|
|
156
164
|
|
|
157
165
|
```bash
|
|
158
|
-
uv add --dev "tempest-cli[tools]"
|
|
166
|
+
uv add --dev "tempest-cli[tools]" # mypy + pytest + pytest-xdist
|
|
159
167
|
```
|
|
160
168
|
|
|
169
|
+
`pytest-xdist` is what `test --fast` runs the suite with; its
|
|
170
|
+
`requires-dist` (`execnet>=2.1`, `pytest>=7.0.0`) carries no upper bound.
|
|
171
|
+
|
|
161
172
|
Whatever you pin wins over the bundled ruff. The lookup runs in this
|
|
162
173
|
order:
|
|
163
174
|
|
|
@@ -42,8 +42,8 @@ remembered to pass.
|
|
|
42
42
|
| `tempest-cli format` | `ruff format` (writes) |
|
|
43
43
|
| `tempest-cli fmt-check` | `ruff format --check` (read-only) |
|
|
44
44
|
| `tempest-cli type` | `mypy` |
|
|
45
|
-
| `tempest-cli test` | `pytest` |
|
|
46
|
-
| `tempest-cli check` | all four, in order, stopping at the first failure |
|
|
45
|
+
| `tempest-cli test` | `pytest` (`--fast` spreads it across cores with pytest-xdist: `-n auto -p no:cacheprovider`, `-w N` to choose) |
|
|
46
|
+
| `tempest-cli check` | all four, in order, stopping at the first failure (`--fast` runs the test step in parallel) |
|
|
47
47
|
| `tempest-cli pr-prompt` | builds the prompt that makes an AI write this branch's PR description |
|
|
48
48
|
|
|
49
49
|
Every command takes an optional path (`tempest-cli lint src/`) and
|
|
@@ -73,6 +73,13 @@ they avoid `Any`.
|
|
|
73
73
|
Override per run with `--strictness` / `-s`. Absent config means
|
|
74
74
|
`standard`.
|
|
75
75
|
|
|
76
|
+
Everything that is *not* typing — silencing an untyped library, skipping
|
|
77
|
+
generated code, dropping a rule that does not fit your framework — is
|
|
78
|
+
ordinary `[tool.ruff]` / `[tool.mypy]` configuration. The recipes,
|
|
79
|
+
including the one that takes a legacy Django project to a green gate,
|
|
80
|
+
are in **[Tuning the rules](https://mauriciobenjamin700.github.io/tempest-cli/rules/)**
|
|
81
|
+
([EN](https://mauriciobenjamin700.github.io/tempest-cli/en/rules/)).
|
|
82
|
+
|
|
76
83
|
## PR descriptions from the branch itself
|
|
77
84
|
|
|
78
85
|
```bash
|
|
@@ -124,9 +131,12 @@ which errors your code reports, and pytest has to match your plugins and
|
|
|
124
131
|
your suite. Add them yourself, or take the bundle:
|
|
125
132
|
|
|
126
133
|
```bash
|
|
127
|
-
uv add --dev "tempest-cli[tools]"
|
|
134
|
+
uv add --dev "tempest-cli[tools]" # mypy + pytest + pytest-xdist
|
|
128
135
|
```
|
|
129
136
|
|
|
137
|
+
`pytest-xdist` is what `test --fast` runs the suite with; its
|
|
138
|
+
`requires-dist` (`execnet>=2.1`, `pytest>=7.0.0`) carries no upper bound.
|
|
139
|
+
|
|
130
140
|
Whatever you pin wins over the bundled ruff. The lookup runs in this
|
|
131
141
|
order:
|
|
132
142
|
|
|
@@ -3,6 +3,31 @@
|
|
|
3
3
|
The full history lives in the repository's
|
|
4
4
|
[`CHANGELOG.md`](https://github.com/mauriciobenjamin700/tempest-cli/blob/main/CHANGELOG.md).
|
|
5
5
|
|
|
6
|
+
## [0.4.0] — 2026-09-27
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`test --fast` and `check --fast`: the suite in parallel.** Runs
|
|
11
|
+
`pytest -n <workers> -p no:cacheprovider [target]` with pytest-xdist;
|
|
12
|
+
`--workers` / `-w` takes what `pytest -n` takes (an integer, `auto` —
|
|
13
|
+
the default — or `logical`) and is a usage error without `--fast`. The
|
|
14
|
+
target is forwarded and pytest's exit code comes back untranslated. It
|
|
15
|
+
is a flag, not a subcommand, because `test fast` keeps meaning the
|
|
16
|
+
`fast/` folder. [Details](commands.md#the-suite-in-parallel-fast).
|
|
17
|
+
- **A missing pytest-xdist is a sentence, not `unrecognized arguments:
|
|
18
|
+
-n`.** `--fast` asks the interpreter that will run pytest whether it
|
|
19
|
+
can import `xdist`; when it cannot, the message names the package and
|
|
20
|
+
the extras (`tempest-cli[tools]`, `tempest-fastapi-sdk[tests]`) and the
|
|
21
|
+
exit code is 127, with no traceback. Under `check --fast` the check
|
|
22
|
+
comes before the first step.
|
|
23
|
+
- **`run_pytest` / `run_full_check` gain `fast=` and `workers=`**,
|
|
24
|
+
keyword-only, serial by default.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **`[tools]` now carries `pytest-xdist>=3.8.0`.** No upper bound in its
|
|
29
|
+
`requires-dist` (`execnet>=2.1`, `pytest>=7.0.0`).
|
|
30
|
+
|
|
6
31
|
## [0.3.0] — 2026-08-15
|
|
7
32
|
|
|
8
33
|
### Changed
|
|
@@ -4,6 +4,30 @@ O histórico completo vive no
|
|
|
4
4
|
[`CHANGELOG.md`](https://github.com/mauriciobenjamin700/tempest-cli/blob/main/CHANGELOG.md)
|
|
5
5
|
do repositório.
|
|
6
6
|
|
|
7
|
+
## [0.4.0] — 2026-09-27
|
|
8
|
+
|
|
9
|
+
### Adicionado
|
|
10
|
+
|
|
11
|
+
- **`test --fast` e `check --fast`: a suíte em paralelo.** Roda
|
|
12
|
+
`pytest -n <workers> -p no:cacheprovider [alvo]` com o pytest-xdist;
|
|
13
|
+
`--workers` / `-w` aceita o que o `pytest -n` aceita (inteiro, `auto` —
|
|
14
|
+
o padrão — ou `logical`) e é erro de uso sem `--fast`. O alvo é
|
|
15
|
+
repassado e o código de saída do pytest volta sem tradução. É flag, e
|
|
16
|
+
não subcomando, porque `test fast` continua significando a pasta
|
|
17
|
+
`fast/`. [Detalhes](commands.md#a-suite-em-paralelo-fast).
|
|
18
|
+
- **pytest-xdist ausente vira frase, não `unrecognized arguments: -n`.**
|
|
19
|
+
O `--fast` pergunta ao interpretador que vai rodar o pytest se ele
|
|
20
|
+
importa o `xdist`; faltando, a mensagem nomeia o pacote e os extras
|
|
21
|
+
(`tempest-cli[tools]`, `tempest-fastapi-sdk[tests]`) e a saída é 127,
|
|
22
|
+
sem traceback. No `check --fast` a checagem vem antes do primeiro passo.
|
|
23
|
+
- **`run_pytest` / `run_full_check` ganham `fast=` e `workers=`**
|
|
24
|
+
keyword-only, com default serial.
|
|
25
|
+
|
|
26
|
+
### Mudado
|
|
27
|
+
|
|
28
|
+
- **O `[tools]` agora leva `pytest-xdist>=3.8.0`.** Sem teto no
|
|
29
|
+
`requires-dist` (`execnet>=2.1`, `pytest>=7.0.0`).
|
|
30
|
+
|
|
7
31
|
## [0.3.0] — 2026-08-15
|
|
8
32
|
|
|
9
33
|
### Mudado
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Commands
|
|
2
|
+
|
|
3
|
+
Eight commands. All take an optional path and return the underlying
|
|
4
|
+
tool's exit code.
|
|
5
|
+
|
|
6
|
+
| Command | Runs |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| `lint` | `ruff check` |
|
|
9
|
+
| `fix` | `ruff check --fix` then `ruff format` |
|
|
10
|
+
| `format` | `ruff format` (writes) |
|
|
11
|
+
| `fmt-check` | `ruff format --check` (read-only) |
|
|
12
|
+
| `type` | `mypy` |
|
|
13
|
+
| `test` | `pytest` |
|
|
14
|
+
| `check` | the four above, in order, stopping at the first failure |
|
|
15
|
+
| `pr-prompt` | builds the PR-description prompt — [its own page](pr-prompt.md) |
|
|
16
|
+
|
|
17
|
+
!!! tip "`tc` is the short form"
|
|
18
|
+
The package installs `tempest-cli` and `tc` pointing at the same
|
|
19
|
+
program. The examples use the long name; `tc check`, `tc fix` and
|
|
20
|
+
`tc type -s strict` work exactly the same.
|
|
21
|
+
|
|
22
|
+
## The full gate
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
tempest-cli check # the whole project
|
|
26
|
+
tempest-cli check src/ # a single path
|
|
27
|
+
tempest-cli check -s strict # with raised typing strictness for this run
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The order is deliberate: **lint → formatting → types → tests**. What
|
|
31
|
+
fails fastest and is cheapest to fix comes first, and the run stops at
|
|
32
|
+
the first failure — there is no point running the whole suite when the
|
|
33
|
+
imports are unsorted.
|
|
34
|
+
|
|
35
|
+
## Fixing what can be fixed
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
tempest-cli fix # safe autofixes + formatting
|
|
39
|
+
tempest-cli fix --unsafe # also ruff's risky autofixes
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`fix` makes two passes: `ruff check --fix` (sorts and dedupes imports,
|
|
43
|
+
drops unused ones, normalizes quotes) and then `ruff format`
|
|
44
|
+
(indentation, line length, blank lines).
|
|
45
|
+
|
|
46
|
+
!!! warning "`--unsafe` can change behavior"
|
|
47
|
+
Ruff's "unsafe" autofixes are the ones that may alter semantics.
|
|
48
|
+
They are off by default. Turned them on? Read the `git diff` before
|
|
49
|
+
committing.
|
|
50
|
+
|
|
51
|
+
## One at a time
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
tempest-cli lint src/
|
|
55
|
+
tempest-cli fmt-check
|
|
56
|
+
tempest-cli type mypackage
|
|
57
|
+
tempest-cli test tests/unit
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`test` forwards the path to pytest as a filter; with no argument it runs
|
|
61
|
+
the whole suite.
|
|
62
|
+
|
|
63
|
+
## The suite in parallel: `--fast`
|
|
64
|
+
|
|
65
|
+
A suite whose tests are already isolated (a database per test, no global
|
|
66
|
+
state) pays the serial time and buys nothing for it. `--fast` spreads the
|
|
67
|
+
suite across the cores with
|
|
68
|
+
[pytest-xdist](https://pytest-xdist.readthedocs.io/):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
tempest-cli test --fast # -n auto: one worker per core
|
|
72
|
+
tempest-cli test --fast -w 4 # four workers
|
|
73
|
+
tempest-cli test tests/unit --fast # the target is still forwarded
|
|
74
|
+
tempest-cli check --fast # the full gate, with the test step in parallel
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Underneath it becomes `pytest -n <workers> -p no:cacheprovider [target]`.
|
|
78
|
+
`-p no:cacheprovider` is there because several workers writing
|
|
79
|
+
`.pytest_cache` at once is a race that buys nothing — and that cache is
|
|
80
|
+
what `--lf` / `--ff` read, so those two remain a serial-run affair.
|
|
81
|
+
|
|
82
|
+
`--workers` (`-w`) takes what `pytest -n` takes: an integer, `auto` (the
|
|
83
|
+
default) or `logical`. Without `--fast` it is a usage error (exit 2)
|
|
84
|
+
instead of being silently ignored.
|
|
85
|
+
|
|
86
|
+
!!! info "`auto` counts physical cores when `psutil` is installed"
|
|
87
|
+
pytest-xdist decides: with `psutil` importable, `auto` is the number of
|
|
88
|
+
**physical** cores; without it, the number of logical CPUs. On a machine
|
|
89
|
+
with 6 cores and 12 threads, with `psutil` in the environment, `auto`
|
|
90
|
+
started `created: 6/6 workers`. Want the 12 threads? `-w logical` or
|
|
91
|
+
`-w 12`.
|
|
92
|
+
|
|
93
|
+
!!! note "Why a flag, not `tempest-cli test fast`"
|
|
94
|
+
`test` already takes a positional path. `test fast` keeps meaning
|
|
95
|
+
"run pytest on the `fast/` folder" — a subcommand by that name would
|
|
96
|
+
break anyone who has one.
|
|
97
|
+
|
|
98
|
+
### Without pytest-xdist
|
|
99
|
+
|
|
100
|
+
Before running anything, the CLI asks **the very interpreter that will
|
|
101
|
+
run pytest** whether it can import `xdist` — not the CLI's own
|
|
102
|
+
interpreter, which under `uv tool install` or pipx lives in a different
|
|
103
|
+
environment. When it is missing, the message names what to install and
|
|
104
|
+
the exit code is **127**, with no traceback:
|
|
105
|
+
|
|
106
|
+
```console
|
|
107
|
+
$ tempest-cli test --fast
|
|
108
|
+
error: --fast needs pytest-xdist, which is not installed in the environment pytest runs in (.venv/bin/pytest). Install it with 'uv add --dev pytest-xdist' — or a bundle that carries it, 'uv add --dev "tempest-cli[tools]"' or 'uv add --dev "tempest-fastapi-sdk[tests]"' — and retry, or drop --fast to run the suite serially.
|
|
109
|
+
$ echo $?
|
|
110
|
+
127
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Under `check --fast` the check happens **before** the first step: there
|
|
114
|
+
is no point waiting for lint and mypy to finish to learn the tests will
|
|
115
|
+
not run.
|
|
116
|
+
|
|
117
|
+
### A test that fails only in parallel
|
|
118
|
+
|
|
119
|
+
Parallelism exposes tests that depend on order or on an idle machine:
|
|
120
|
+
two tests writing the same file, the same port, a module-level global, a
|
|
121
|
+
fixed `sleep` waiting for something that takes longer once every core is
|
|
122
|
+
busy. Before treating the failure as a regression, run the test **alone
|
|
123
|
+
and serially**:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
tempest-cli test "tests/test_scheduler.py::test_lease_expires"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
- **It passes alone**: the defect is the test's isolation, not the change
|
|
130
|
+
under review. Fix the test (a file under `tmp_path`, a free port, wait
|
|
131
|
+
on a condition instead of a `sleep`).
|
|
132
|
+
- **It fails alone too**: it is a real regression.
|
|
133
|
+
|
|
134
|
+
## What each returns
|
|
135
|
+
|
|
136
|
+
The exit code is the tool's own, untranslated:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
tempest-cli lint; echo "exited $?"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
There is exactly one code of its own: **127**, when the tool is in none
|
|
143
|
+
of the places the CLI looks — the run's environment, `PATH`, `uv run
|
|
144
|
+
--with` — and the message then names what was missing and how to install
|
|
145
|
+
it. The lookup order is in
|
|
146
|
+
[Installation](installation.md#where-ruff-mypy-and-pytest-come-from).
|
|
147
|
+
The same 127 comes out of `--fast` when pytest-xdist is not in pytest's
|
|
148
|
+
environment.
|
|
149
|
+
|
|
150
|
+
## Recap
|
|
151
|
+
|
|
152
|
+
- `check` is lint + fmt-check + type + test, in order, stopping at the
|
|
153
|
+
first failure.
|
|
154
|
+
- `fix` is the pass that repairs; `--unsafe` only when you will review.
|
|
155
|
+
- `test --fast` / `check --fast` run the suite with pytest-xdist
|
|
156
|
+
(`-n auto`, `-w N` to choose); a test that fails only in parallel is
|
|
157
|
+
checked by running it alone.
|
|
158
|
+
- Exit codes are the tools'; 127 means a missing tool (or the
|
|
159
|
+
pytest-xdist `--fast` needs).
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Comandos
|
|
2
|
+
|
|
3
|
+
Oito comandos. Todos aceitam um caminho opcional e devolvem o código de
|
|
4
|
+
saída da ferramenta por baixo.
|
|
5
|
+
|
|
6
|
+
| Comando | Roda |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| `lint` | `ruff check` |
|
|
9
|
+
| `fix` | `ruff check --fix` e depois `ruff format` |
|
|
10
|
+
| `format` | `ruff format` (escreve) |
|
|
11
|
+
| `fmt-check` | `ruff format --check` (só leitura) |
|
|
12
|
+
| `type` | `mypy` |
|
|
13
|
+
| `test` | `pytest` |
|
|
14
|
+
| `check` | os quatro acima, em ordem, parando na primeira falha |
|
|
15
|
+
| `pr-prompt` | monta o prompt da descrição do PR — [página própria](pr-prompt.md) |
|
|
16
|
+
|
|
17
|
+
!!! tip "`tc` é a forma curta"
|
|
18
|
+
O pacote instala `tempest-cli` e `tc` apontando para o mesmo
|
|
19
|
+
programa. Os exemplos usam o nome longo; `tc check`, `tc fix` e
|
|
20
|
+
`tc type -s strict` funcionam igual.
|
|
21
|
+
|
|
22
|
+
## O gate completo
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
tempest-cli check # o projeto todo
|
|
26
|
+
tempest-cli check src/ # só um caminho
|
|
27
|
+
tempest-cli check -s strict # com rigor de tipagem elevado nesta execução
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A ordem é deliberada: **lint → formatação → tipos → testes**. O que falha
|
|
31
|
+
mais rápido e é mais barato de corrigir vem primeiro, e a execução para
|
|
32
|
+
na primeira falha — não adianta rodar a suíte inteira se o import está
|
|
33
|
+
desordenado.
|
|
34
|
+
|
|
35
|
+
## Corrigindo o que dá para corrigir
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
tempest-cli fix # autofixes seguros + formatação
|
|
39
|
+
tempest-cli fix --unsafe # inclui os autofixes de risco do ruff
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
O `fix` faz duas passadas: `ruff check --fix` (ordena e deduplica
|
|
43
|
+
imports, remove imports não usados, normaliza aspas) e depois `ruff
|
|
44
|
+
format` (indentação, comprimento de linha, linhas em branco).
|
|
45
|
+
|
|
46
|
+
!!! warning "`--unsafe` muda comportamento"
|
|
47
|
+
Os autofixes "unsafe" do ruff são os que podem alterar semântica.
|
|
48
|
+
Ficam desligados por padrão. Ligou? Leia o `git diff` antes de
|
|
49
|
+
commitar.
|
|
50
|
+
|
|
51
|
+
## Um comando por vez
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
tempest-cli lint src/
|
|
55
|
+
tempest-cli fmt-check
|
|
56
|
+
tempest-cli type meupacote
|
|
57
|
+
tempest-cli test tests/unit
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`test` repassa o caminho ao pytest como filtro; sem argumento, roda a
|
|
61
|
+
suíte inteira.
|
|
62
|
+
|
|
63
|
+
## A suíte em paralelo: `--fast`
|
|
64
|
+
|
|
65
|
+
Suíte cujos testes já são isolados (banco por teste, nada de estado
|
|
66
|
+
global) paga o tempo serial sem comprar nada. `--fast` espalha a suíte
|
|
67
|
+
pelos núcleos com o [pytest-xdist](https://pytest-xdist.readthedocs.io/):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
tempest-cli test --fast # -n auto: um worker por núcleo
|
|
71
|
+
tempest-cli test --fast -w 4 # quatro workers
|
|
72
|
+
tempest-cli test tests/unit --fast # o alvo continua sendo repassado
|
|
73
|
+
tempest-cli check --fast # o gate completo, com o passo de teste em paralelo
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Por baixo, vira `pytest -n <workers> -p no:cacheprovider [alvo]`. O
|
|
77
|
+
`-p no:cacheprovider` existe porque vários workers escrevendo
|
|
78
|
+
`.pytest_cache` ao mesmo tempo é corrida à toa — e é esse cache que o
|
|
79
|
+
`--lf` / `--ff` leem, então esses dois continuam sendo coisa de execução
|
|
80
|
+
serial.
|
|
81
|
+
|
|
82
|
+
`--workers` (`-w`) aceita o que o `pytest -n` aceita: um inteiro, `auto`
|
|
83
|
+
(o padrão) ou `logical`. Sem `--fast` ele é erro de uso (saída 2), em vez
|
|
84
|
+
de ser ignorado em silêncio.
|
|
85
|
+
|
|
86
|
+
!!! info "`auto` conta núcleo físico quando o `psutil` está instalado"
|
|
87
|
+
É o pytest-xdist que decide: com `psutil` importável, `auto` é o número
|
|
88
|
+
de núcleos **físicos**; sem ele, o de CPUs lógicas. Numa máquina de 6
|
|
89
|
+
núcleos e 12 threads, com `psutil` no ambiente, `auto` subiu
|
|
90
|
+
`created: 6/6 workers`. Quer as 12 threads? `-w logical` ou `-w 12`.
|
|
91
|
+
|
|
92
|
+
!!! note "Por que flag, e não `tempest-cli test fast`"
|
|
93
|
+
`test` já recebe um caminho posicional. `test fast` continua
|
|
94
|
+
significando "rode o pytest na pasta `fast/`" — um subcomando com esse
|
|
95
|
+
nome quebraria quem tem essa pasta.
|
|
96
|
+
|
|
97
|
+
### Sem o pytest-xdist
|
|
98
|
+
|
|
99
|
+
Antes de rodar qualquer coisa, a CLI pergunta ao **mesmo interpretador
|
|
100
|
+
que vai rodar o pytest** se ele importa o `xdist` — não ao interpretador
|
|
101
|
+
da própria CLI, que sob `uv tool install` ou pipx mora em outro ambiente.
|
|
102
|
+
Faltou, a mensagem diz o que instalar e a saída é **127**, sem traceback:
|
|
103
|
+
|
|
104
|
+
```console
|
|
105
|
+
$ tempest-cli test --fast
|
|
106
|
+
error: --fast needs pytest-xdist, which is not installed in the environment pytest runs in (.venv/bin/pytest). Install it with 'uv add --dev pytest-xdist' — or a bundle that carries it, 'uv add --dev "tempest-cli[tools]"' or 'uv add --dev "tempest-fastapi-sdk[tests]"' — and retry, or drop --fast to run the suite serially.
|
|
107
|
+
$ echo $?
|
|
108
|
+
127
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
No `check --fast` essa checagem acontece **antes** do primeiro passo:
|
|
112
|
+
não adianta esperar lint e mypy terminarem para descobrir que o teste
|
|
113
|
+
não vai rodar.
|
|
114
|
+
|
|
115
|
+
### Teste que só falha em paralelo
|
|
116
|
+
|
|
117
|
+
O paralelismo expõe teste que depende de ordem ou de máquina ociosa:
|
|
118
|
+
dois testes escrevendo o mesmo arquivo, a mesma porta, um global de
|
|
119
|
+
módulo, um `sleep` fixo esperando algo que, com os núcleos ocupados,
|
|
120
|
+
demora mais. Antes de tratar a falha como regressão, rode o teste
|
|
121
|
+
**sozinho e em série**:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
tempest-cli test "tests/test_scheduler.py::test_lease_expires"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- **Passou sozinho**: o defeito é o isolamento do teste, não a mudança
|
|
128
|
+
em revisão. Conserte o teste (arquivo em `tmp_path`, porta livre,
|
|
129
|
+
espera por condição em vez de `sleep`).
|
|
130
|
+
- **Falhou sozinho também**: é regressão de verdade.
|
|
131
|
+
|
|
132
|
+
## O que cada um devolve
|
|
133
|
+
|
|
134
|
+
O código de saída é o da ferramenta, sem tradução:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
tempest-cli lint; echo "saiu $?"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Só há um código próprio: **127**, quando a ferramenta não está em nenhum
|
|
141
|
+
dos lugares onde a CLI procura — ambiente da execução, `PATH`, `uv run
|
|
142
|
+
--with` — e aí a mensagem diz qual faltou e como instalá-la. A ordem de
|
|
143
|
+
busca está em [Instalação](installation.md#de-onde-vem-o-ruff-o-mypy-e-o-pytest).
|
|
144
|
+
O mesmo 127 sai do `--fast` quando o pytest-xdist não está no ambiente do
|
|
145
|
+
pytest.
|
|
146
|
+
|
|
147
|
+
## Recap
|
|
148
|
+
|
|
149
|
+
- `check` é lint + fmt-check + type + test, nessa ordem, parando na
|
|
150
|
+
primeira falha.
|
|
151
|
+
- `fix` é a passada que conserta; `--unsafe` só quando você for revisar.
|
|
152
|
+
- `test --fast` / `check --fast` rodam a suíte com pytest-xdist
|
|
153
|
+
(`-n auto`, `-w N` para escolher); teste que só falha em paralelo se
|
|
154
|
+
confere rodando-o sozinho.
|
|
155
|
+
- Código de saída é o da ferramenta; 127 significa ferramenta (ou o
|
|
156
|
+
pytest-xdist do `--fast`) ausente.
|
|
@@ -36,6 +36,12 @@ The level **adds** flags on top of what you already configured in
|
|
|
36
36
|
`ANN002` / `ANN003` (`*args` / `**kwargs`) are left out too: on a
|
|
37
37
|
passthrough wrapper they are noise carrying no information.
|
|
38
38
|
|
|
39
|
+
!!! tip "And the rules themselves?"
|
|
40
|
+
This knob only moves typing. Turning any other rule on, off or down
|
|
41
|
+
— including silencing mypy's complaints about untyped libraries,
|
|
42
|
+
the ones that make the first run look alarming — belongs to
|
|
43
|
+
**[Tuning the rules](rules.md)**.
|
|
44
|
+
|
|
39
45
|
## Per-run override
|
|
40
46
|
|
|
41
47
|
```bash
|
|
@@ -35,6 +35,12 @@ O nível **soma** flags ao que você já configurou em `[tool.ruff]` e
|
|
|
35
35
|
`ANN002` / `ANN003` (`*args` / `**kwargs`) também ficam de fora: em
|
|
36
36
|
wrapper de passagem viram ruído sem informação.
|
|
37
37
|
|
|
38
|
+
!!! tip "E as regras em si?"
|
|
39
|
+
Este knob só mexe em tipagem. Ligar, desligar e afrouxar qualquer
|
|
40
|
+
outra regra — inclusive calar as reclamações do mypy sobre
|
|
41
|
+
bibliotecas sem tipos, que assustam na primeira execução — é
|
|
42
|
+
assunto de **[Ajustando as regras](rules.md)**.
|
|
43
|
+
|
|
38
44
|
## Override por execução
|
|
39
45
|
|
|
40
46
|
```bash
|
|
@@ -55,6 +55,9 @@ into a versioned value rather than a flag someone remembered to pass.
|
|
|
55
55
|
- **[Commands »](commands.md)** — all eight, what each runs and returns.
|
|
56
56
|
- **[Typing strictness »](configuration.md)** — the three levels, what
|
|
57
57
|
each adds, and why `ANN401` is never enabled.
|
|
58
|
+
- **[Tuning the rules »](rules.md)** — turning rules on, off and down
|
|
59
|
+
from `pyproject.toml`; the recipe that makes a legacy Django project
|
|
60
|
+
green.
|
|
58
61
|
- **[PR descriptions »](pr-prompt.md)** — the prompt that makes an AI
|
|
59
62
|
write the PR description from the branch's own diff.
|
|
60
63
|
- **[Use as a library »](library.md)** — calling the runners from your
|
|
@@ -56,6 +56,9 @@ cobra" num valor versionado, não numa flag que alguém lembrou de passar.
|
|
|
56
56
|
devolve.
|
|
57
57
|
- **[Rigor de tipagem »](configuration.md)** — os três níveis, o que
|
|
58
58
|
cada um acrescenta e por que `ANN401` nunca entra.
|
|
59
|
+
- **[Ajustando as regras »](rules.md)** — ligar, desligar e afrouxar
|
|
60
|
+
regra pelo `pyproject.toml`; a receita que deixa um Django legado
|
|
61
|
+
verde.
|
|
59
62
|
- **[Descrições de PR »](pr-prompt.md)** — o prompt que faz uma IA
|
|
60
63
|
escrever a descrição do PR a partir do diff da branch.
|
|
61
64
|
- **[Usar como biblioteca »](library.md)** — chamar os runners do seu
|
|
@@ -42,9 +42,14 @@ suite — those versions are the project's call. Add them yourself, or
|
|
|
42
42
|
take the bundle:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
uv add --dev "tempest-cli[tools]" # mypy + pytest
|
|
45
|
+
uv add --dev "tempest-cli[tools]" # mypy + pytest + pytest-xdist
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
`pytest-xdist` is what [`test --fast`](commands.md#the-suite-in-parallel-fast)
|
|
49
|
+
uses to run the suite in parallel. Its `requires-dist` (`execnet>=2.1`,
|
|
50
|
+
`pytest>=7.0.0`) carries no upper bound, so it tightens nobody's
|
|
51
|
+
resolution.
|
|
52
|
+
|
|
48
53
|
!!! question "Why ruff is a dependency and the other two are not"
|
|
49
54
|
Six of the eight commands are ruff — a `tempest-cli` without ruff is
|
|
50
55
|
a gate that cannot run. And it costs nothing: ruff is a static
|
|
@@ -43,9 +43,14 @@ e a sua suíte — versão dessas duas é decisão do projeto. Adicione você
|
|
|
43
43
|
mesmo, ou pegue o pacote pronto:
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
uv add --dev "tempest-cli[tools]" # mypy + pytest
|
|
46
|
+
uv add --dev "tempest-cli[tools]" # mypy + pytest + pytest-xdist
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
O `pytest-xdist` é o que o [`test --fast`](commands.md#a-suite-em-paralelo-fast)
|
|
50
|
+
usa para rodar a suíte em paralelo. O `requires-dist` dele
|
|
51
|
+
(`execnet>=2.1`, `pytest>=7.0.0`) não tem teto nenhum, então ele não
|
|
52
|
+
aperta a resolução de ninguém.
|
|
53
|
+
|
|
49
54
|
!!! question "Por que o ruff é dependência e as outras duas não"
|
|
50
55
|
Seis dos oito comandos são ruff — um `tempest-cli` sem ruff é um
|
|
51
56
|
gate que não roda. E o custo é zero: o ruff é um binário estático,
|
|
@@ -30,6 +30,7 @@ run_ruff_fix("src", unsafe=False, config=strict)
|
|
|
30
30
|
run_ruff_format("src", check=True) # --check: writes nothing
|
|
31
31
|
run_mypy("src", config=strict)
|
|
32
32
|
run_pytest("tests/unit")
|
|
33
|
+
run_pytest("tests/unit", fast=True, workers="4") # pytest-xdist: -n 4
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
`load_tempest_config(start)` walks up from `start` (the cwd by default)
|
|
@@ -31,6 +31,7 @@ run_ruff_fix("src", unsafe=False, config=strict)
|
|
|
31
31
|
run_ruff_format("src", check=True) # --check: não escreve
|
|
32
32
|
run_mypy("src", config=strict)
|
|
33
33
|
run_pytest("tests/unit")
|
|
34
|
+
run_pytest("tests/unit", fast=True, workers="4") # pytest-xdist: -n 4
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
`load_tempest_config(start)` procura o `pyproject.toml` mais próximo
|