python-constricter 0.3.3__tar.gz → 0.3.5__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.
- {python_constricter-0.3.3 → python_constricter-0.3.5}/PKG-INFO +42 -40
- {python_constricter-0.3.3 → python_constricter-0.3.5}/README.md +41 -39
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/__init__.py +1 -1
- python_constricter-0.3.5/constricter/__main__.py +26 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/command.py +103 -55
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/config.py +28 -1
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/hints.py +18 -12
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/options.py +29 -1
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/protocol.py +25 -5
- python_constricter-0.3.5/constricter/cli/remembered.py +52 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/report.py +7 -5
- python_constricter-0.3.5/constricter/cli/runs.py +90 -0
- python_constricter-0.3.5/constricter/cli/schedule.py +415 -0
- python_constricter-0.3.5/constricter/cli/traced.py +401 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/workers.py +85 -23
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/core/fixes.py +51 -7
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/core/imports.py +100 -2
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/core/inherited.py +117 -17
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/core/known.py +177 -49
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/core/signatures.py +22 -4
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/atoms.py +18 -3
- python_constricter-0.3.5/constricter/fix/index/awaits.py +90 -0
- python_constricter-0.3.5/constricter/fix/index/beyond.py +148 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/callers.py +2 -1
- python_constricter-0.3.5/constricter/fix/index/chained.py +69 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/classnames.py +17 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/declared.py +68 -4
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/decorated.py +3 -1
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/installed.py +35 -3
- python_constricter-0.3.5/constricter/fix/index/linked.py +142 -0
- python_constricter-0.3.5/constricter/fix/index/loose.py +98 -0
- python_constricter-0.3.5/constricter/fix/index/managed.py +63 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/modules.py +331 -47
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/offers.py +4 -1
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/order.py +10 -3
- python_constricter-0.3.5/constricter/fix/index/own_methods.py +67 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/own_overloads.py +14 -1
- python_constricter-0.3.5/constricter/fix/index/own_types.py +161 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/plain.py +3 -1
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/project.py +137 -153
- python_constricter-0.3.5/constricter/fix/index/sides.py +160 -0
- python_constricter-0.3.5/constricter/fix/index/strings.py +46 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/stubbed.py +51 -12
- python_constricter-0.3.5/constricter/fix/index/taken.py +45 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/tuples.py +54 -28
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/libraries/library.py +95 -7
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/libraries/opened.py +57 -16
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/libraries/overloads.py +32 -6
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/libraries/stdlib.py +64 -13
- python_constricter-0.3.5/constricter/fix/libraries/walks.py +49 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/attributes.json +2 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/classes.json +1 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/generic_attributes.json +2 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/method_overloads.json +14 -6
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/method_signatures.json +50 -20
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/methods.json +1 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/overloads.json +38 -37
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/scalars.json +2 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/source.json +1 -1
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/type_parameters.json +1 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/aliased.py +9 -1
- python_constricter-0.3.5/constricter/fix/values/bodies.py +76 -0
- python_constricter-0.3.5/constricter/fix/values/callables.py +321 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/called.py +10 -12
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/classvars.py +124 -21
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/decided.py +22 -2
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/doubts.py +104 -27
- python_constricter-0.3.5/constricter/fix/values/entered.py +331 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/fills.py +160 -13
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/guesses.py +51 -19
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/inference.py +48 -54
- python_constricter-0.3.5/constricter/fix/values/members.py +564 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/narrowed.py +60 -4
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/operated.py +7 -6
- python_constricter-0.3.5/constricter/fix/values/ordered.py +74 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/returned.py +319 -224
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/returns.py +44 -1
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/shapes.py +132 -32
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/targets.py +42 -15
- python_constricter-0.3.5/constricter/fix/values/unchecked.py +162 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/offences.py +45 -6
- python_constricter-0.3.5/constricter/recording.py +571 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/annotations.py +30 -10
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/binding.py +229 -37
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/calls.py +58 -8
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/checker.py +177 -70
- python_constricter-0.3.5/constricter/rules/keys.py +79 -0
- python_constricter-0.3.5/constricter/rules/late.py +663 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/quoted.py +32 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/rebinding.py +35 -10
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/recorded.py +12 -8
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/scope.py +241 -74
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/syntax.py +20 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/tables.py +18 -4
- python_constricter-0.3.5/constricter/rules/widened.py +322 -0
- python_constricter-0.3.5/constricter/trace.py +378 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/pyproject.toml +56 -8
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/elements.py +24 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/generate.py +12 -4
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/overloads.py +24 -19
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/reading.py +15 -0
- python_constricter-0.3.3/constricter/__main__.py +0 -9
- python_constricter-0.3.3/constricter/cli/runs.py +0 -43
- python_constricter-0.3.3/constricter/cli/schedule.py +0 -262
- python_constricter-0.3.3/constricter/cli/traced.py +0 -289
- python_constricter-0.3.3/constricter/fix/index/beyond.py +0 -90
- python_constricter-0.3.3/constricter/fix/index/sides.py +0 -65
- python_constricter-0.3.3/constricter/fix/values/entered.py +0 -173
- python_constricter-0.3.3/constricter/fix/values/members.py +0 -325
- python_constricter-0.3.3/constricter/rules/late.py +0 -324
- python_constricter-0.3.3/constricter/trace.py +0 -385
- {python_constricter-0.3.3 → python_constricter-0.3.5}/.gitignore +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/LICENSE.md +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/baseline.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/collecting.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/edits.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/explain.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/guard.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/cli/paths.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/core/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/core/asked.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/fixtures.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/index/read.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/libraries/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/aliases.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/awaited.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/awaited_overloads.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/bases.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/elements.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/functions.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/returns.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/scalar_members.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/subscriptable.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/tables/variables.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/displays.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/fix/values/hinted.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/jsonc.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/noqa.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/notebook.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/plugins/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/plugins/flake8.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/plugins/pylint.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/py.typed +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/decorators.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/flow.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/narrowing.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/parsed.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/redundant.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/constricter/rules/walked.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/hatch_build.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/__init__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/__main__.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/partial.json +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/stubs.py +0 -0
- {python_constricter-0.3.3 → python_constricter-0.3.5}/stdlib_tables/templates.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: python-constricter
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Import-Name: constricter
|
|
5
5
|
Summary: Lint rules: every local variable is typed where it's first bound. A flake8 plugin, a pylint plugin and a standalone CLI.
|
|
6
6
|
Author: Ivy Duggan
|
|
@@ -173,38 +173,39 @@ enabled).
|
|
|
173
173
|
|
|
174
174
|
Options:
|
|
175
175
|
|
|
176
|
-
| Option | CLI
|
|
177
|
-
| ---------------- |
|
|
178
|
-
| level | `--level`
|
|
179
|
-
| strictest | `--max`: `--level=suffocate` for every path, `--all-scopes`, and the opt-in `LVA012`
|
|
180
|
-
| every fix | `--max-fix`: `--max`, `--fix --unsafe-fixes`, and `--infer-with` each of basedpyright, ty and pyrefly that's installed and runs
|
|
181
|
-
| type comments | `--type-comments`
|
|
182
|
-
| all scopes | `--all-scopes`
|
|
183
|
-
| nesting | `--nesting N`
|
|
184
|
-
| max length | `--max-length N` (LVA011)
|
|
185
|
-
| vague | `--vague LEVEL` (LVA005, and the fixes offered): -1 none (the default), 0 one `Any` inside a type that says the rest (`tuple[str, Any]`), N from 1 N + 1, or one alone
|
|
186
|
-
| min python | `--min-python VERSION` (the oldest Python the code runs on, whose syntax `--fix` writes; default: `requires-python`'s)
|
|
187
|
-
| type hierarchy | -
|
|
188
|
-
| fix | `--fix` (`--unsafe-fixes` for guesses), `--diff` to preview
|
|
189
|
-
| infer with | `--infer-with CHECKERS` (`basedpyright`, `ty`, `pyrefly`, several: inferred types, as guesses)
|
|
190
|
-
| infer from | `--infer-from FILE` (the types `python -m constricter.trace -m pytest` recorded from a run, as guesses)
|
|
191
|
-
| show fixes | `--show-fixes` (each fix and how it was decided
|
|
192
|
-
| fix levels | `--fix-select`, `--fix-ignore`, `--unsafe-fix-select` (mechanisms: [docs/FIXES.md](https://github.com/ivylikethevine/python-constricter/blob/main/docs/FIXES.md#fix-levels))
|
|
193
|
-
| fix plain bases | `--fix-plain-bases BASES` (frameworks' classes whose subclasses' bodies `--fix` annotates; django's are built in)
|
|
194
|
-
|
|
|
195
|
-
|
|
|
196
|
-
|
|
|
197
|
-
|
|
|
198
|
-
|
|
|
199
|
-
|
|
|
200
|
-
|
|
|
201
|
-
|
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
| per-
|
|
205
|
-
|
|
|
206
|
-
|
|
|
207
|
-
|
|
|
176
|
+
| Option | CLI | `[tool.constricter]` | flake8 (CLI or config) | pylint |
|
|
177
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------- | --------------------------------- |
|
|
178
|
+
| level | `--level` | `level` | `--constricter-level` | `constricter-level` |
|
|
179
|
+
| strictest | `--max`: `--level=suffocate` for every path, `--all-scopes`, and the opt-in `LVA012` | — | — | — |
|
|
180
|
+
| every fix | `--max-fix`: `--max`, `--fix --unsafe-fixes`, and `--infer-with` each of basedpyright, ty and pyrefly that's installed and runs | — | — | — |
|
|
181
|
+
| type comments | `--type-comments` | `type-comments` | `--constricter-type-comments` | `constricter-type-comments = yes` |
|
|
182
|
+
| all scopes | `--all-scopes` | `all-scopes` | `--constricter-all-scopes` | `constricter-all-scopes = yes` |
|
|
183
|
+
| nesting | `--nesting N` | `nesting` | `--constricter-nesting` | `constricter-nesting` |
|
|
184
|
+
| max length | `--max-length N` (LVA011) | `max-length` | `--constricter-max-length` | `constricter-max-length` |
|
|
185
|
+
| vague | `--vague LEVEL` (LVA005, and the fixes offered): -1 none (the default), 0 one `Any` inside a type that says the rest (`tuple[str, Any]`), N from 1 N + 1, or one alone | `vague` | `--constricter-vague` | `constricter-vague` |
|
|
186
|
+
| min python | `--min-python VERSION` (the oldest Python the code runs on, whose syntax `--fix` writes; default: `requires-python`'s) | `min-python` | - | - |
|
|
187
|
+
| type hierarchy | - | `narrower` (a table) | `--constricter-narrower` | `constricter-narrower` |
|
|
188
|
+
| fix | `--fix` (`--unsafe-fixes` for guesses), `--diff` to preview | - | - | - |
|
|
189
|
+
| infer with | `--infer-with CHECKERS` (`basedpyright`, `ty`, `pyrefly`, several: inferred types, as guesses) | `infer-with` | - | - |
|
|
190
|
+
| infer from | `--infer-from FILE` (the types `python -m constricter.trace -m pytest` recorded from a run, as guesses) | `infer-from` | - | - |
|
|
191
|
+
| show fixes | `--show-fixes` (each fix and how it was decided; with `--fix`, those made too) | - | - | - |
|
|
192
|
+
| fix levels | `--fix-select`, `--fix-ignore`, `--unsafe-fix-select` (mechanisms: [docs/FIXES.md](https://github.com/ivylikethevine/python-constricter/blob/main/docs/FIXES.md#fix-levels)) | `fix-select`, `fix-ignore`, `unsafe-fix-select` | - | - |
|
|
193
|
+
| fix plain bases | `--fix-plain-bases BASES` (frameworks' classes whose subclasses' bodies `--fix` annotates; django's are built in) | `fix-plain-bases` | - | - |
|
|
194
|
+
| fix widen | `--fix-widen KINDS` (wider types `--fix` may write, each marked: `untyped-parameters`, `empty-containers`, `mixed-containers`, `unions`, `vague`, `untyped-calls`, `unknown-calls`; `all`: every one; [docs/FIXES.md](https://github.com/ivylikethevine/python-constricter/blob/main/docs/FIXES.md#wider-types-fix-widen)) | `fix-widen` | - | - |
|
|
195
|
+
| select | `--select CODES` (codes or prefixes) | `select` | flake8's own `select` | pylint's own `enable` |
|
|
196
|
+
| extend select | `--extend-select CODES` (also report these; an opt-in code by its full code) | `extend-select` | flake8's own `extend-select` | pylint's own `enable` |
|
|
197
|
+
| ignore | `--ignore CODES` | `ignore` | flake8's own `extend-ignore` | pylint's own `disable` |
|
|
198
|
+
| exclude | `--exclude GLOB` (repeatable) | `exclude` | flake8's own `exclude` | pylint's own `ignore-paths` |
|
|
199
|
+
| format | `--format`: `text`, `full` (with source), `json`, `github`, `sarif`, `gitlab`, `junit`, `rdjson` | - | - | - |
|
|
200
|
+
| statistics | `--statistics` (counts per code, text format) | - | - | - |
|
|
201
|
+
| jobs | `--jobs N` (`-j`; 0: one per CPU) | `jobs` | flake8's own `--jobs` | pylint's own `--jobs` |
|
|
202
|
+
| baseline | `--baseline FILE`; `--write-baseline` records it | `baseline` | - | - |
|
|
203
|
+
| coverage | `--coverage`, `--fail-under PCT` | - | - | - |
|
|
204
|
+
| per-path levels | - | `per-path-levels` | - | - |
|
|
205
|
+
| per-file ignores | - | `per-file-ignores` | flake8's own `per-file-ignores` | - |
|
|
206
|
+
| stdin | `-` as the path, `--stdin-filename PATH` | - | flake8's own `-` | - |
|
|
207
|
+
| exit status | `--exit-zero` | - | flake8's own `--exit-zero` | pylint's own `--exit-zero` |
|
|
208
|
+
| output file | `--output-file FILE` | - | flake8's own `--output-file` | pylint's own `--output` |
|
|
208
209
|
|
|
209
210
|
`constricter --explain LVA002` prints a code's rationale, its fix, and the levels that report it.
|
|
210
211
|
|
|
@@ -245,10 +246,11 @@ ignore = ["LVA003"]
|
|
|
245
246
|
return type or whose `return`s agree (in another checked file too), a copy, subscript, attribute or
|
|
246
247
|
method call of a local whose type is known, and values computed from those; a loop's target or an
|
|
247
248
|
unpacking's names get a declaration on the line before. `--unsafe-fixes` adds guesses, and
|
|
248
|
-
`--show-fixes` lists each fix and how its value decided it
|
|
249
|
-
`pyrefly`, or several: `basedpyright,ty`; each installed
|
|
250
|
-
what `--fix` can't type itself, as guesses;
|
|
251
|
-
|
|
249
|
+
`--show-fixes` lists each fix and how its value decided it, and after `--fix` each one made.
|
|
250
|
+
`--infer-with basedpyright` (or `ty`, or `pyrefly`, or several: `basedpyright,ty`; each installed
|
|
251
|
+
alongside) asks those type checkers for what `--fix` can't type itself, as guesses;
|
|
252
|
+
`--infer-from FILE` takes the types a run bound each name to, recorded by
|
|
253
|
+
`python -m constricter.trace -m pytest`, the same way. The full list is in
|
|
252
254
|
[docs/FIXES.md](https://github.com/ivylikethevine/python-constricter/blob/main/docs/FIXES.md).
|
|
253
255
|
|
|
254
256
|
### Installing and running
|
|
@@ -274,7 +276,7 @@ pre-commit, after ruff's hooks (or `constricter-fix`, which runs `--fix` first):
|
|
|
274
276
|
|
|
275
277
|
```yaml
|
|
276
278
|
- repo: https://github.com/ivylikethevine/python-constricter
|
|
277
|
-
rev: v0.3.
|
|
279
|
+
rev: v0.3.5
|
|
278
280
|
hooks:
|
|
279
281
|
- id: constricter
|
|
280
282
|
```
|
|
@@ -307,8 +309,8 @@ default one is used only if it exists.
|
|
|
307
309
|
total; the bindings are the ones the rules cover (with `--all-scopes`, module and class bodies too),
|
|
308
310
|
and `# noqa` doesn't make one typed. `--fail-under PCT` (which implies `--coverage`) exits 1 below
|
|
309
311
|
PCT, so CI can hold a codebase to a share. With `--format=json` it prints
|
|
310
|
-
`{"typed", "total", "percent", "files"}`, which a badge can read: publish that JSON
|
|
311
|
-
gist, a release asset) and point
|
|
312
|
+
`{"typed", "total", "widened", "percent", "files"}`, which a badge can read: publish that JSON
|
|
313
|
+
somewhere (a gist, a release asset) and point
|
|
312
314
|
[shields.io's dynamic JSON badge](https://shields.io/badges/dynamic-json-badge) at it with the query
|
|
313
315
|
`$.percent`. This project keeps its own share at 100% in CI, so its badge is static.
|
|
314
316
|
|
|
@@ -146,38 +146,39 @@ enabled).
|
|
|
146
146
|
|
|
147
147
|
Options:
|
|
148
148
|
|
|
149
|
-
| Option | CLI
|
|
150
|
-
| ---------------- |
|
|
151
|
-
| level | `--level`
|
|
152
|
-
| strictest | `--max`: `--level=suffocate` for every path, `--all-scopes`, and the opt-in `LVA012`
|
|
153
|
-
| every fix | `--max-fix`: `--max`, `--fix --unsafe-fixes`, and `--infer-with` each of basedpyright, ty and pyrefly that's installed and runs
|
|
154
|
-
| type comments | `--type-comments`
|
|
155
|
-
| all scopes | `--all-scopes`
|
|
156
|
-
| nesting | `--nesting N`
|
|
157
|
-
| max length | `--max-length N` (LVA011)
|
|
158
|
-
| vague | `--vague LEVEL` (LVA005, and the fixes offered): -1 none (the default), 0 one `Any` inside a type that says the rest (`tuple[str, Any]`), N from 1 N + 1, or one alone
|
|
159
|
-
| min python | `--min-python VERSION` (the oldest Python the code runs on, whose syntax `--fix` writes; default: `requires-python`'s)
|
|
160
|
-
| type hierarchy | -
|
|
161
|
-
| fix | `--fix` (`--unsafe-fixes` for guesses), `--diff` to preview
|
|
162
|
-
| infer with | `--infer-with CHECKERS` (`basedpyright`, `ty`, `pyrefly`, several: inferred types, as guesses)
|
|
163
|
-
| infer from | `--infer-from FILE` (the types `python -m constricter.trace -m pytest` recorded from a run, as guesses)
|
|
164
|
-
| show fixes | `--show-fixes` (each fix and how it was decided
|
|
165
|
-
| fix levels | `--fix-select`, `--fix-ignore`, `--unsafe-fix-select` (mechanisms: [docs/FIXES.md](https://github.com/ivylikethevine/python-constricter/blob/main/docs/FIXES.md#fix-levels))
|
|
166
|
-
| fix plain bases | `--fix-plain-bases BASES` (frameworks' classes whose subclasses' bodies `--fix` annotates; django's are built in)
|
|
167
|
-
|
|
|
168
|
-
|
|
|
169
|
-
|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
|
|
|
177
|
-
| per-
|
|
178
|
-
|
|
|
179
|
-
|
|
|
180
|
-
|
|
|
149
|
+
| Option | CLI | `[tool.constricter]` | flake8 (CLI or config) | pylint |
|
|
150
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------- | --------------------------------- |
|
|
151
|
+
| level | `--level` | `level` | `--constricter-level` | `constricter-level` |
|
|
152
|
+
| strictest | `--max`: `--level=suffocate` for every path, `--all-scopes`, and the opt-in `LVA012` | — | — | — |
|
|
153
|
+
| every fix | `--max-fix`: `--max`, `--fix --unsafe-fixes`, and `--infer-with` each of basedpyright, ty and pyrefly that's installed and runs | — | — | — |
|
|
154
|
+
| type comments | `--type-comments` | `type-comments` | `--constricter-type-comments` | `constricter-type-comments = yes` |
|
|
155
|
+
| all scopes | `--all-scopes` | `all-scopes` | `--constricter-all-scopes` | `constricter-all-scopes = yes` |
|
|
156
|
+
| nesting | `--nesting N` | `nesting` | `--constricter-nesting` | `constricter-nesting` |
|
|
157
|
+
| max length | `--max-length N` (LVA011) | `max-length` | `--constricter-max-length` | `constricter-max-length` |
|
|
158
|
+
| vague | `--vague LEVEL` (LVA005, and the fixes offered): -1 none (the default), 0 one `Any` inside a type that says the rest (`tuple[str, Any]`), N from 1 N + 1, or one alone | `vague` | `--constricter-vague` | `constricter-vague` |
|
|
159
|
+
| min python | `--min-python VERSION` (the oldest Python the code runs on, whose syntax `--fix` writes; default: `requires-python`'s) | `min-python` | - | - |
|
|
160
|
+
| type hierarchy | - | `narrower` (a table) | `--constricter-narrower` | `constricter-narrower` |
|
|
161
|
+
| fix | `--fix` (`--unsafe-fixes` for guesses), `--diff` to preview | - | - | - |
|
|
162
|
+
| infer with | `--infer-with CHECKERS` (`basedpyright`, `ty`, `pyrefly`, several: inferred types, as guesses) | `infer-with` | - | - |
|
|
163
|
+
| infer from | `--infer-from FILE` (the types `python -m constricter.trace -m pytest` recorded from a run, as guesses) | `infer-from` | - | - |
|
|
164
|
+
| show fixes | `--show-fixes` (each fix and how it was decided; with `--fix`, those made too) | - | - | - |
|
|
165
|
+
| fix levels | `--fix-select`, `--fix-ignore`, `--unsafe-fix-select` (mechanisms: [docs/FIXES.md](https://github.com/ivylikethevine/python-constricter/blob/main/docs/FIXES.md#fix-levels)) | `fix-select`, `fix-ignore`, `unsafe-fix-select` | - | - |
|
|
166
|
+
| fix plain bases | `--fix-plain-bases BASES` (frameworks' classes whose subclasses' bodies `--fix` annotates; django's are built in) | `fix-plain-bases` | - | - |
|
|
167
|
+
| fix widen | `--fix-widen KINDS` (wider types `--fix` may write, each marked: `untyped-parameters`, `empty-containers`, `mixed-containers`, `unions`, `vague`, `untyped-calls`, `unknown-calls`; `all`: every one; [docs/FIXES.md](https://github.com/ivylikethevine/python-constricter/blob/main/docs/FIXES.md#wider-types-fix-widen)) | `fix-widen` | - | - |
|
|
168
|
+
| select | `--select CODES` (codes or prefixes) | `select` | flake8's own `select` | pylint's own `enable` |
|
|
169
|
+
| extend select | `--extend-select CODES` (also report these; an opt-in code by its full code) | `extend-select` | flake8's own `extend-select` | pylint's own `enable` |
|
|
170
|
+
| ignore | `--ignore CODES` | `ignore` | flake8's own `extend-ignore` | pylint's own `disable` |
|
|
171
|
+
| exclude | `--exclude GLOB` (repeatable) | `exclude` | flake8's own `exclude` | pylint's own `ignore-paths` |
|
|
172
|
+
| format | `--format`: `text`, `full` (with source), `json`, `github`, `sarif`, `gitlab`, `junit`, `rdjson` | - | - | - |
|
|
173
|
+
| statistics | `--statistics` (counts per code, text format) | - | - | - |
|
|
174
|
+
| jobs | `--jobs N` (`-j`; 0: one per CPU) | `jobs` | flake8's own `--jobs` | pylint's own `--jobs` |
|
|
175
|
+
| baseline | `--baseline FILE`; `--write-baseline` records it | `baseline` | - | - |
|
|
176
|
+
| coverage | `--coverage`, `--fail-under PCT` | - | - | - |
|
|
177
|
+
| per-path levels | - | `per-path-levels` | - | - |
|
|
178
|
+
| per-file ignores | - | `per-file-ignores` | flake8's own `per-file-ignores` | - |
|
|
179
|
+
| stdin | `-` as the path, `--stdin-filename PATH` | - | flake8's own `-` | - |
|
|
180
|
+
| exit status | `--exit-zero` | - | flake8's own `--exit-zero` | pylint's own `--exit-zero` |
|
|
181
|
+
| output file | `--output-file FILE` | - | flake8's own `--output-file` | pylint's own `--output` |
|
|
181
182
|
|
|
182
183
|
`constricter --explain LVA002` prints a code's rationale, its fix, and the levels that report it.
|
|
183
184
|
|
|
@@ -218,10 +219,11 @@ ignore = ["LVA003"]
|
|
|
218
219
|
return type or whose `return`s agree (in another checked file too), a copy, subscript, attribute or
|
|
219
220
|
method call of a local whose type is known, and values computed from those; a loop's target or an
|
|
220
221
|
unpacking's names get a declaration on the line before. `--unsafe-fixes` adds guesses, and
|
|
221
|
-
`--show-fixes` lists each fix and how its value decided it
|
|
222
|
-
`pyrefly`, or several: `basedpyright,ty`; each installed
|
|
223
|
-
what `--fix` can't type itself, as guesses;
|
|
224
|
-
|
|
222
|
+
`--show-fixes` lists each fix and how its value decided it, and after `--fix` each one made.
|
|
223
|
+
`--infer-with basedpyright` (or `ty`, or `pyrefly`, or several: `basedpyright,ty`; each installed
|
|
224
|
+
alongside) asks those type checkers for what `--fix` can't type itself, as guesses;
|
|
225
|
+
`--infer-from FILE` takes the types a run bound each name to, recorded by
|
|
226
|
+
`python -m constricter.trace -m pytest`, the same way. The full list is in
|
|
225
227
|
[docs/FIXES.md](https://github.com/ivylikethevine/python-constricter/blob/main/docs/FIXES.md).
|
|
226
228
|
|
|
227
229
|
### Installing and running
|
|
@@ -247,7 +249,7 @@ pre-commit, after ruff's hooks (or `constricter-fix`, which runs `--fix` first):
|
|
|
247
249
|
|
|
248
250
|
```yaml
|
|
249
251
|
- repo: https://github.com/ivylikethevine/python-constricter
|
|
250
|
-
rev: v0.3.
|
|
252
|
+
rev: v0.3.5
|
|
251
253
|
hooks:
|
|
252
254
|
- id: constricter
|
|
253
255
|
```
|
|
@@ -280,8 +282,8 @@ default one is used only if it exists.
|
|
|
280
282
|
total; the bindings are the ones the rules cover (with `--all-scopes`, module and class bodies too),
|
|
281
283
|
and `# noqa` doesn't make one typed. `--fail-under PCT` (which implies `--coverage`) exits 1 below
|
|
282
284
|
PCT, so CI can hold a codebase to a share. With `--format=json` it prints
|
|
283
|
-
`{"typed", "total", "percent", "files"}`, which a badge can read: publish that JSON
|
|
284
|
-
gist, a release asset) and point
|
|
285
|
+
`{"typed", "total", "widened", "percent", "files"}`, which a badge can read: publish that JSON
|
|
286
|
+
somewhere (a gist, a release asset) and point
|
|
285
287
|
[shields.io's dynamic JSON badge](https://shields.io/badges/dynamic-json-badge) at it with the query
|
|
286
288
|
`$.percent`. This project keeps its own share at 100% in CI, so its badge is static.
|
|
287
289
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""`python -m constricter`, and the `constricter` script."""
|
|
3
|
+
|
|
4
|
+
import contextlib
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from constricter.cli.command import main
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run() -> None:
|
|
12
|
+
"""Run the command, and leave with its status at once.
|
|
13
|
+
|
|
14
|
+
Not by the interpreter's own exit, which frees every parsed file and the index an object at a
|
|
15
|
+
time: a fifth of a check of a thousand files. What the command wrote is flushed first; nothing
|
|
16
|
+
else waits on the exit (its workers have ended, its output file is closed).
|
|
17
|
+
"""
|
|
18
|
+
status: int = main()
|
|
19
|
+
with contextlib.suppress(OSError, ValueError): # a reader that left, a stream closed already
|
|
20
|
+
_ = sys.stdout.flush()
|
|
21
|
+
_ = sys.stderr.flush()
|
|
22
|
+
os._exit(status)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__": # not when `--jobs` workers import it
|
|
26
|
+
run()
|
|
@@ -21,11 +21,11 @@ from constricter.cli.options import Mode, Options, Output
|
|
|
21
21
|
from constricter.cli.paths import STDIN, python_files
|
|
22
22
|
from constricter.cli.protocol import HintError
|
|
23
23
|
from constricter.cli.report import Format, Result, fix_reasons, render, statistics
|
|
24
|
-
from constricter.cli.runs import BaselineRun, CheckRun, CoverageRun, FileRun
|
|
25
|
-
from constricter.cli.workers import Workers
|
|
24
|
+
from constricter.cli.runs import Added, BaselineRun, CheckRun, CoverageRun, FileRun, merged
|
|
25
|
+
from constricter.cli.workers import Workers, kept, started
|
|
26
26
|
from constricter.fix.core import fixes
|
|
27
27
|
from constricter.fix.core.known import Callee, Hints, Outside, Returns
|
|
28
|
-
from constricter.fix.index import callers, decorated, installed, plain, project
|
|
28
|
+
from constricter.fix.index import callers, decorated, installed, loose, plain, project
|
|
29
29
|
from constricter.noqa import lines, unsuppressed
|
|
30
30
|
from constricter.offences import (
|
|
31
31
|
DEFAULT_CHECKS,
|
|
@@ -127,6 +127,7 @@ def _placed(offence: Offence, where: list[notebook.Line]) -> Offence:
|
|
|
127
127
|
|
|
128
128
|
|
|
129
129
|
# One changed part of a fixed file (the file, or a notebook's cell): its label, old and new lines.
|
|
130
|
+
_Hinted: TypeAlias = dict[Path, tuple[Hints, ...]] # each file's hints, a checker's each
|
|
130
131
|
_Change: TypeAlias = tuple[str, list[str], list[str]]
|
|
131
132
|
# One JSON value a coverage report holds, and one file's row of them.
|
|
132
133
|
_Scalar: TypeAlias = str | int | float
|
|
@@ -308,7 +309,29 @@ def _handled(path: Path, text: tuple[str, Path], offences: list[Offence], option
|
|
|
308
309
|
return CheckRun(results, baselined, error=f"{name}: error: {message}")
|
|
309
310
|
if fixed:
|
|
310
311
|
_ = parsed.take(raw) # the tree of the text it had: no check will ask for it again
|
|
311
|
-
return CheckRun(
|
|
312
|
+
return CheckRun(
|
|
313
|
+
left,
|
|
314
|
+
baselined,
|
|
315
|
+
fixed,
|
|
316
|
+
made=[r._replace(replacements=(), fixed=True) for r in results if r.offence in fixing],
|
|
317
|
+
rounds=(_added(raw, name, fixing),),
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _added(raw: str, name: Path, fixing: Sequence[Offence]) -> Added:
|
|
322
|
+
"""Find the lines fixing `fixing` adds to `raw`, the text of `name` (see `fixes.inserted`).
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Each one's cell (`None` outside a notebook) and the line it goes before; a notebook's cells
|
|
326
|
+
get declarations alone.
|
|
327
|
+
|
|
328
|
+
"""
|
|
329
|
+
if name.suffix != notebook.SUFFIX:
|
|
330
|
+
return tuple((None, line) for line in fixes.inserted(lines(raw), fixing))
|
|
331
|
+
declared: list[tuple[int, int]] = [
|
|
332
|
+
(o.cell or 0, o.edit.span[0]) for o in fixing if o.edit is not None and o.edit.edit is Edit.DECLARE
|
|
333
|
+
]
|
|
334
|
+
return tuple(sorted(declared))
|
|
312
335
|
|
|
313
336
|
|
|
314
337
|
def _baseline_path(path: Path, outside: Outside, options: Options) -> BaselineRun:
|
|
@@ -340,6 +363,17 @@ def _cover_path(path: Path, _outside: Outside, options: Options) -> CoverageRun:
|
|
|
340
363
|
|
|
341
364
|
|
|
342
365
|
def _check_all(options: Options) -> tuple[list[Path], list[FileRun]]:
|
|
366
|
+
"""Check every file (see `_check_rounds`), its rounds by one set of worker processes.
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
The names, and what each file found.
|
|
370
|
+
|
|
371
|
+
"""
|
|
372
|
+
with kept():
|
|
373
|
+
return _check_rounds(options)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _check_rounds(options: Options) -> tuple[list[Path], list[FileRun]]:
|
|
343
377
|
"""Check every file (`--jobs` at a time), in order.
|
|
344
378
|
|
|
345
379
|
With `--infer-with`, the type checker's server runs throughout: it's asked for every file's
|
|
@@ -401,7 +435,7 @@ def _check_all(options: Options) -> tuple[list[Path], list[FileRun]]:
|
|
|
401
435
|
index: int
|
|
402
436
|
run: FileRun
|
|
403
437
|
for index, run in zip(again, redone, strict=True):
|
|
404
|
-
runs[index] =
|
|
438
|
+
runs[index] = merged(runs[index], run)
|
|
405
439
|
again = [index for index, run in zip(again, redone, strict=True) if cast("CheckRun", run).fixed]
|
|
406
440
|
checker: str
|
|
407
441
|
path: Path
|
|
@@ -447,7 +481,7 @@ def _called_again(
|
|
|
447
481
|
calling = [
|
|
448
482
|
at
|
|
449
483
|
for at in calling
|
|
450
|
-
if
|
|
484
|
+
if loose.returned(modules, paths[at], {}) != loose.returned(before, paths[at], {})
|
|
451
485
|
]
|
|
452
486
|
return runs, _checked_more(paths, runs, calling, (check, options, session), modules)
|
|
453
487
|
|
|
@@ -473,7 +507,7 @@ def _checked_more(
|
|
|
473
507
|
at: int
|
|
474
508
|
run: FileRun
|
|
475
509
|
for at, run in zip(which, redone, strict=True):
|
|
476
|
-
runs[at] =
|
|
510
|
+
runs[at] = merged(runs[at], run) if how[1].mode is Mode.FIX else run
|
|
477
511
|
return modules
|
|
478
512
|
|
|
479
513
|
|
|
@@ -509,44 +543,29 @@ def _checked_all(
|
|
|
509
543
|
What each file found, and the index of every file's module.
|
|
510
544
|
|
|
511
545
|
"""
|
|
512
|
-
|
|
546
|
+
# Before a worker starts: one forked while the servers' threads run could wait on a lock one holds.
|
|
547
|
+
hinted: _Hinted = {} if session is None else session.hints(_texts(paths))
|
|
513
548
|
if options.input.trace is not None:
|
|
514
549
|
hinted = traced.merged(hinted, traced.load(options.input.trace), paths)
|
|
515
550
|
coverage: bool = options.mode is Mode.COVERAGE # needs nothing from the other files
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if not coverage:
|
|
527
|
-
modules = plain.settled(
|
|
528
|
-
decorated.passed(installed.with_installed(modules, installed.search_path())),
|
|
529
|
-
options.checks.plain_bases,
|
|
530
|
-
)
|
|
531
|
-
return schedule.checked(
|
|
532
|
-
paths,
|
|
533
|
-
check,
|
|
534
|
-
pool,
|
|
535
|
-
(modules, hinted),
|
|
536
|
-
_merged if options.mode is Mode.FIX else None,
|
|
551
|
+
pool: Workers | None = started(paths, options.jobs) if options.jobs != 1 and len(paths) > 1 else None
|
|
552
|
+
if modules is None and pool:
|
|
553
|
+
modules = project.Index({}, []) if coverage else pool.index()
|
|
554
|
+
elif modules is None:
|
|
555
|
+
modules = project.Index({}, []) if coverage else project.index(paths)
|
|
556
|
+
collecting.indexed()
|
|
557
|
+
if not coverage:
|
|
558
|
+
modules = plain.settled(
|
|
559
|
+
decorated.passed(installed.with_installed(modules, installed.search_path())),
|
|
560
|
+
options.checks.plain_bases,
|
|
537
561
|
)
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
"""
|
|
547
|
-
first: CheckRun = cast("CheckRun", before)
|
|
548
|
-
later: CheckRun = cast("CheckRun", after)
|
|
549
|
-
return replace(later, fixed=first.fixed + later.fixed)
|
|
562
|
+
return schedule.checked(
|
|
563
|
+
paths,
|
|
564
|
+
check,
|
|
565
|
+
pool,
|
|
566
|
+
(modules, hinted),
|
|
567
|
+
merged if options.mode is Mode.FIX else None,
|
|
568
|
+
)
|
|
550
569
|
|
|
551
570
|
|
|
552
571
|
def _texts(paths: Sequence[Path]) -> dict[Path, str]:
|
|
@@ -579,10 +598,16 @@ def _report(options: Options, runs: Sequence[FileRun], files: int) -> int:
|
|
|
579
598
|
results: list[Result] = [result for run in checked for result in run.results]
|
|
580
599
|
output: Output = options.output
|
|
581
600
|
text: bool = output.fmt in {Format.TEXT, Format.FULL}
|
|
601
|
+
# With `--show-fixes`, the fixes `--fix` made too: after the rest, as text or JSON.
|
|
602
|
+
shown: list[Result] = [*results, *(made for run in checked if output.show_fixes for made in run.made)]
|
|
582
603
|
line: str
|
|
583
|
-
for line in
|
|
604
|
+
for line in (
|
|
605
|
+
statistics(results)
|
|
606
|
+
if text and output.statistics
|
|
607
|
+
else render(output.fmt, shown if output.fmt is Format.JSON else results)
|
|
608
|
+
):
|
|
584
609
|
_ = sys.stdout.write(f"{line}\n")
|
|
585
|
-
for line in fix_reasons(
|
|
610
|
+
for line in fix_reasons(shown) if text and output.show_fixes else ():
|
|
586
611
|
_ = sys.stdout.write(f"{line}\n")
|
|
587
612
|
errors: int = sum(r.offence.is_error(r.level) for r in results)
|
|
588
613
|
if text and not output.quiet:
|
|
@@ -611,30 +636,53 @@ def _coverage(options: Options, paths: Sequence[Path], runs: Sequence[FileRun])
|
|
|
611
636
|
counted: list[tuple[Path, Coverage]] = [
|
|
612
637
|
(path, run.coverage) for path, run in zip(paths, covered, strict=True) if run.coverage is not None
|
|
613
638
|
]
|
|
614
|
-
total: Coverage = Coverage(
|
|
639
|
+
total: Coverage = Coverage(
|
|
640
|
+
sum(c.typed for _, c in counted),
|
|
641
|
+
sum(c.total for _, c in counted),
|
|
642
|
+
sum(c.widened for _, c in counted),
|
|
643
|
+
)
|
|
615
644
|
if options.output.fmt is Format.JSON:
|
|
616
645
|
report: dict[str, _Scalar | list[_Row]] = {
|
|
617
|
-
|
|
618
|
-
"
|
|
619
|
-
"percent": round(total.percent, 1),
|
|
620
|
-
"files": [
|
|
621
|
-
{"path": str(path), "typed": c.typed, "total": c.total, "percent": round(c.percent, 1)}
|
|
622
|
-
for path, c in counted
|
|
623
|
-
],
|
|
646
|
+
**_counts(total),
|
|
647
|
+
"files": [{"path": str(path), **_counts(c)} for path, c in counted],
|
|
624
648
|
}
|
|
625
649
|
_ = sys.stdout.write(json.dumps(report, indent=2) + "\n")
|
|
626
650
|
else:
|
|
627
651
|
path: Path
|
|
628
652
|
c: Coverage
|
|
629
653
|
for path, c in counted:
|
|
630
|
-
_ = sys.stdout.write(f"{path}: {
|
|
631
|
-
_ = sys.stdout.write(
|
|
632
|
-
f"Total: {total.typed}/{total.total} typed ({total.percent:.1f}%) in {len(counted)} file(s).\n",
|
|
633
|
-
)
|
|
654
|
+
_ = sys.stdout.write(f"{path}: {_typed(c)}\n")
|
|
655
|
+
_ = sys.stdout.write(f"Total: {_typed(total)} in {len(counted)} file(s).\n")
|
|
634
656
|
threshold: float | None = options.output.fail_under
|
|
635
657
|
return EXIT_FOUND if threshold is not None and total.percent < threshold else EXIT_CLEAN
|
|
636
658
|
|
|
637
659
|
|
|
660
|
+
def _counts(found: Coverage) -> _Row:
|
|
661
|
+
"""Name a coverage's counts, for `--format=json`.
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
Them, by name.
|
|
665
|
+
|
|
666
|
+
"""
|
|
667
|
+
return {
|
|
668
|
+
"typed": found.typed,
|
|
669
|
+
"total": found.total,
|
|
670
|
+
"widened": found.widened,
|
|
671
|
+
"percent": round(found.percent, 1),
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def _typed(found: Coverage) -> str:
|
|
676
|
+
"""Write a coverage as text: `3/4 typed (75.0%)`, then what a widening annotated, if anything.
|
|
677
|
+
|
|
678
|
+
Returns:
|
|
679
|
+
It.
|
|
680
|
+
|
|
681
|
+
"""
|
|
682
|
+
wider: str = f", {found.widened} widened" if found.widened else ""
|
|
683
|
+
return f"{found.typed}/{found.total} typed ({found.percent:.1f}%){wider}"
|
|
684
|
+
|
|
685
|
+
|
|
638
686
|
def _run(options: Options) -> int:
|
|
639
687
|
"""Check, fix, diff, count or baseline, printing what that finds.
|
|
640
688
|
|