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.
Files changed (47) hide show
  1. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/CHANGELOG.md +53 -0
  2. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/PKG-INFO +15 -4
  3. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/README.md +13 -3
  4. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/changelog.en.md +25 -0
  5. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/changelog.md +24 -0
  6. tempest_cli-0.4.0/docs/commands.en.md +159 -0
  7. tempest_cli-0.4.0/docs/commands.md +156 -0
  8. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/configuration.en.md +6 -0
  9. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/configuration.md +6 -0
  10. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/index.en.md +3 -0
  11. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/index.md +3 -0
  12. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/installation.en.md +6 -1
  13. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/installation.md +6 -1
  14. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/library.en.md +1 -0
  15. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/library.md +1 -0
  16. tempest_cli-0.4.0/docs/rules.en.md +252 -0
  17. tempest_cli-0.4.0/docs/rules.md +249 -0
  18. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/mkdocs.yml +3 -0
  19. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/pyproject.toml +6 -2
  20. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/__init__.py +1 -1
  21. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/lint.py +231 -7
  22. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/main.py +121 -4
  23. tempest_cli-0.4.0/tests/test_fast.py +388 -0
  24. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/uv.lock +27 -1
  25. tempest_cli-0.3.0/docs/commands.en.md +0 -82
  26. tempest_cli-0.3.0/docs/commands.md +0 -81
  27. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/.github/workflows/ci.yml +0 -0
  28. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/.github/workflows/docs.yml +0 -0
  29. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/.github/workflows/release-pypi.yml +0 -0
  30. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/.gitignore +0 -0
  31. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/LICENSE +0 -0
  32. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/Makefile +0 -0
  33. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/pr-prompt.en.md +0 -0
  34. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/pr-prompt.md +0 -0
  35. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/reference.en.md +0 -0
  36. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/docs/reference.md +0 -0
  37. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/_templates/pull_request_template.en-US.md +0 -0
  38. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/_templates/pull_request_template.pt-BR.md +0 -0
  39. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/config.py +0 -0
  40. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/pr_prompt.py +0 -0
  41. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tempest_cli/py.typed +0 -0
  42. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/__init__.py +0 -0
  43. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_cli.py +0 -0
  44. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_config.py +0 -0
  45. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_lint_runners.py +0 -0
  46. {tempest_cli-0.3.0 → tempest_cli-0.4.0}/tests/test_lint_strictness.py +0 -0
  47. {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.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