python-constricter 0.3.6__tar.gz → 0.4.1__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.4.1/.gitignore +107 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/LICENSE.md +1 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/PKG-INFO +12 -6
- {python_constricter-0.3.6 → python_constricter-0.4.1}/README.md +11 -5
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/__init__.py +1 -1
- python_constricter-0.4.1/constricter/_version.py +4 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/collecting.py +8 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/command.py +22 -255
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/config.py +2 -1
- python_constricter-0.4.1/constricter/cli/lsp/__init__.py +2 -0
- python_constricter-0.3.6/constricter/cli/hints.py → python_constricter-0.4.1/constricter/cli/lsp/connection.py +16 -362
- {python_constricter-0.3.6/constricter/cli → python_constricter-0.4.1/constricter/cli/lsp}/edits.py +1 -1
- {python_constricter-0.3.6/constricter/cli → python_constricter-0.4.1/constricter/cli/lsp}/guard.py +5 -1
- python_constricter-0.4.1/constricter/cli/lsp/hints.py +391 -0
- python_constricter-0.4.1/constricter/cli/lsp/remembered.py +81 -0
- python_constricter-0.4.1/constricter/cli/lsp/stored.py +200 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/options.py +27 -6
- python_constricter-0.4.1/constricter/cli/paths.py +151 -0
- python_constricter-0.4.1/constricter/cli/reading.py +36 -0
- python_constricter-0.4.1/constricter/cli/rounds.py +328 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/schedule.py +84 -36
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/workers.py +78 -8
- python_constricter-0.4.1/constricter/empty.py +66 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/imports.py +66 -13
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/inherited.py +2 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/known.py +83 -53
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/signatures.py +14 -7
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/beyond.py +3 -2
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/callers.py +3 -2
- python_constricter-0.4.1/constricter/fix/index/crossed.py +327 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/decorated.py +29 -10
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/loose.py +11 -6
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/modules.py +90 -198
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/offers.py +123 -9
- python_constricter-0.4.1/constricter/fix/index/origins.py +238 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/own_methods.py +31 -7
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/own_overloads.py +2 -2
- python_constricter-0.4.1/constricter/fix/index/own_types.py +330 -0
- python_constricter-0.4.1/constricter/fix/index/params.py +81 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/plain.py +109 -13
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/project.py +86 -269
- python_constricter-0.4.1/constricter/fix/index/stubs/__init__.py +2 -0
- {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/atoms.py +3 -3
- {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/chained.py +2 -2
- {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/classnames.py +2 -2
- {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/declared.py +8 -6
- {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/installed.py +84 -7
- python_constricter-0.4.1/constricter/fix/index/stubs/names.py +200 -0
- {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/read.py +2 -2
- python_constricter-0.3.6/constricter/fix/index/stubbed.py → python_constricter-0.4.1/constricter/fix/index/stubs/signatures.py +28 -372
- python_constricter-0.4.1/constricter/fix/index/stubs/stubbed.py +208 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/library.py +60 -14
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/overloads.py +315 -323
- python_constricter-0.4.1/constricter/fix/libraries/receivers.py +274 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/stdlib.py +14 -2
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/attributes.json +8 -8
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/functions.json +15 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/method_overloads.json +91 -91
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/method_signatures.json +60 -2
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/overloads.json +5 -2
- python_constricter-0.4.1/constricter/fix/tables/platform.json +37 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/source.json +5 -3
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/variables.json +3 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/callables.py +4 -2
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/called.py +89 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/classvars.py +100 -12
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/decided.py +39 -6
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/displays.py +19 -5
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/doubts.py +10 -6
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/entered.py +2 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/fills.py +18 -7
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/guesses.py +11 -5
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/hinted.py +175 -9
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/inference.py +21 -319
- python_constricter-0.4.1/constricter/fix/values/loops.py +335 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/members.py +9 -6
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/operated.py +10 -8
- python_constricter-0.4.1/constricter/fix/values/returned.py +479 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/shapes.py +85 -10
- python_constricter-0.3.6/constricter/fix/values/returned.py → python_constricter-0.4.1/constricter/fix/values/stored.py +47 -450
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/targets.py +49 -13
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/notebook.py +6 -8
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/offences.py +49 -4
- python_constricter-0.4.1/constricter/overrides.py +24 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/plugins/pylint.py +20 -17
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/recording.py +93 -16
- python_constricter-0.4.1/constricter/rules/annotations.py +296 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/binding.py +11 -6
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/calls.py +3 -2
- python_constricter-0.4.1/constricter/rules/checker.py +536 -0
- python_constricter-0.3.6/constricter/rules/annotations.py → python_constricter-0.4.1/constricter/rules/declarations.py +42 -286
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/decorators.py +158 -28
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/keys.py +1 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/late.py +319 -20
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/parsed.py +9 -3
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/rebinding.py +5 -3
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/recorded.py +13 -12
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/scope.py +31 -224
- python_constricter-0.4.1/constricter/rules/scoped.py +232 -0
- python_constricter-0.4.1/constricter/rules/scoping.py +346 -0
- python_constricter-0.4.1/constricter/rules/settings.py +189 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/tables.py +1 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/widened.py +1 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/trace.py +5 -9
- {python_constricter-0.3.6 → python_constricter-0.4.1}/hatch_build.py +6 -7
- python_constricter-0.4.1/mypyc.txt +280 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/pyproject.toml +97 -16
- python_constricter-0.3.6/stdlib_tables/overloads.py → python_constricter-0.4.1/stdlib_tables/accepting.py +118 -472
- python_constricter-0.4.1/stdlib_tables/configured.py +464 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/elements.py +2 -1
- python_constricter-0.4.1/stdlib_tables/forms.py +222 -0
- python_constricter-0.4.1/stdlib_tables/generate.py +552 -0
- python_constricter-0.4.1/stdlib_tables/overloads.py +507 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/partial.json +0 -1
- {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/reading.py +46 -229
- python_constricter-0.4.1/stdlib_tables/shed.py +95 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/stubs.py +77 -249
- {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/templates.py +2 -1
- python_constricter-0.4.1/stdlib_tables/written.py +98 -0
- python_constricter-0.3.6/.gitignore +0 -48
- python_constricter-0.3.6/constricter/cli/paths.py +0 -82
- python_constricter-0.3.6/constricter/cli/remembered.py +0 -52
- python_constricter-0.3.6/constricter/fix/index/own_types.py +0 -161
- python_constricter-0.3.6/constricter/rules/checker.py +0 -950
- python_constricter-0.3.6/stdlib_tables/generate.py +0 -996
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/__main__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/baseline.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/explain.py +0 -0
- {python_constricter-0.3.6/constricter/cli → python_constricter-0.4.1/constricter/cli/lsp}/protocol.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/report.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/runs.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/traced.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/asked.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/fixes.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/awaits.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/fixtures.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/linked.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/managed.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/order.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/sides.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/strings.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/taken.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/tuples.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/opened.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/walks.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/aliases.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/awaited.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/awaited_overloads.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/bases.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/classes.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/elements.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/generic_attributes.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/methods.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/returns.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/scalar_members.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/scalars.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/subscriptable.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/type_parameters.json +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/aliased.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/bodies.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/narrowed.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/ordered.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/returns.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/unchecked.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/jsonc.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/noqa.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/plugins/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/plugins/flake8.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/py.typed +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/flow.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/narrowing.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/quoted.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/redundant.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/syntax.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/walked.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/__init__.py +0 -0
- {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/__main__.py +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
# Everything generated on this machine (scratch, tool caches, local builds).
|
|
4
|
+
local/
|
|
5
|
+
|
|
6
|
+
# npm installs, the pinned CI Markdown tools' (.github/package.json) included.
|
|
7
|
+
node_modules/
|
|
8
|
+
|
|
9
|
+
# Claude Code: personal overrides, agent worktrees and host-specific agent
|
|
10
|
+
# notes stay local.
|
|
11
|
+
.claude/settings.local.json
|
|
12
|
+
.claude/worktrees/
|
|
13
|
+
CLAUDE.local.md
|
|
14
|
+
# Claude Code's sandbox bind-mounts /dev/null over these denied paths while a
|
|
15
|
+
# session runs; none is a file of this repo
|
|
16
|
+
/.bash_profile
|
|
17
|
+
/.bashrc
|
|
18
|
+
/.gitconfig
|
|
19
|
+
/.gitmodules
|
|
20
|
+
/.idea
|
|
21
|
+
/.mcp.json
|
|
22
|
+
/.profile
|
|
23
|
+
/.ripgreprc
|
|
24
|
+
/.zprofile
|
|
25
|
+
/.zshrc
|
|
26
|
+
/.claude/agents
|
|
27
|
+
/.claude/commands
|
|
28
|
+
/.claude/hooks
|
|
29
|
+
/.claude/launch.json
|
|
30
|
+
/.claude/loop.md
|
|
31
|
+
/.claude/output-styles
|
|
32
|
+
/.claude/routines
|
|
33
|
+
/.claude/scheduled_tasks.json
|
|
34
|
+
/.claude/skills
|
|
35
|
+
/.claude/workflows
|
|
36
|
+
/.claude/.cc-writes
|
|
37
|
+
# .claude/settings.json too: where it is tracked the ignore changes nothing,
|
|
38
|
+
# and where it is not the device would show as untracked
|
|
39
|
+
/.claude/settings.json
|
|
40
|
+
|
|
41
|
+
# Secrets and local environment. A .env.example is a template with no real
|
|
42
|
+
# values, so it stays tracked.
|
|
43
|
+
.env
|
|
44
|
+
.env.*
|
|
45
|
+
!.env.example
|
|
46
|
+
.envrc
|
|
47
|
+
.direnv/
|
|
48
|
+
*.key
|
|
49
|
+
*.pem
|
|
50
|
+
*.pfx
|
|
51
|
+
*.crt
|
|
52
|
+
*.cert
|
|
53
|
+
*.pub
|
|
54
|
+
*.rsa
|
|
55
|
+
*.priv
|
|
56
|
+
secret/
|
|
57
|
+
secrets/
|
|
58
|
+
private/
|
|
59
|
+
keys/
|
|
60
|
+
|
|
61
|
+
# Editor and OS droppings, and the backups merge tools leave beside a conflict.
|
|
62
|
+
.DS_Store
|
|
63
|
+
*.swp
|
|
64
|
+
*.swo
|
|
65
|
+
*~
|
|
66
|
+
*.orig
|
|
67
|
+
|
|
68
|
+
# Logs.
|
|
69
|
+
*.log
|
|
70
|
+
|
|
71
|
+
# Python's bytecode caches and packaging metadata, and Hypothesis's example
|
|
72
|
+
# database.
|
|
73
|
+
__pycache__/
|
|
74
|
+
*.pyc
|
|
75
|
+
*.egg-info/
|
|
76
|
+
.hypothesis/
|
|
77
|
+
.import_linter_cache/
|
|
78
|
+
# Packaging output at the root; a build/ deeper in the tree is source.
|
|
79
|
+
/build/
|
|
80
|
+
/dist/
|
|
81
|
+
# Rust build output.
|
|
82
|
+
/target
|
|
83
|
+
**/*.rs.bk
|
|
84
|
+
# A local Jekyll build of the docs site; CI builds the real one.
|
|
85
|
+
_site/
|
|
86
|
+
.jekyll-cache/
|
|
87
|
+
# Not used in this project; kept out so stray editor or tooling output isn't
|
|
88
|
+
# committed by accident.
|
|
89
|
+
*.js
|
|
90
|
+
*.ts
|
|
91
|
+
*.bat
|
|
92
|
+
*.cmd
|
|
93
|
+
*.ps
|
|
94
|
+
*.ps1
|
|
95
|
+
*.ps2
|
|
96
|
+
*.psd
|
|
97
|
+
*.doc
|
|
98
|
+
*.docx
|
|
99
|
+
*.id
|
|
100
|
+
# Generated by a build (hatch_build.py, or `python -m stdlib_tables`): the
|
|
101
|
+
# standard-library tables, and the tests' list of what only some platforms and
|
|
102
|
+
# Pythons have.
|
|
103
|
+
constricter/fix/tables/
|
|
104
|
+
stdlib_tables/partial.json
|
|
105
|
+
|
|
106
|
+
# The version the build writes from the git tag (pyproject.toml, [tool.hatch.build.hooks.vcs]).
|
|
107
|
+
/constricter/_version.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Ivy Duggan
|
|
3
|
+
Copyright (c) 2026 Ivy Duggan <https://ivylikethevine.com>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: python-constricter
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
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
|
|
@@ -263,6 +263,9 @@ uvx --from python-constricter constricter # or run it without installing: uv's t
|
|
|
263
263
|
pipx run --spec python-constricter constricter # or pipx's
|
|
264
264
|
```
|
|
265
265
|
|
|
266
|
+
On CPython 3.11 to 3.14 for Linux, macOS and Windows, pip installs a build compiled with mypyc,
|
|
267
|
+
about a fifth faster; anywhere else, the pure Python one, which finds and fixes the same.
|
|
268
|
+
|
|
266
269
|
Tools that run flake8 or pylint (VS Code's extensions, python-lsp-server, prospector, MegaLinter,
|
|
267
270
|
Trunk) pick the plugin up once it's installed alongside them.
|
|
268
271
|
|
|
@@ -270,15 +273,18 @@ Without `lint.external`, ruff flags `# noqa: LVA00x` (RUF102) and `--fix` delete
|
|
|
270
273
|
|
|
271
274
|
The CLI defaults to `.`, checks `*.py` and `*.ipynb`, and skips hidden dirs, `__pycache__`, `venv`,
|
|
272
275
|
`site-packages`, `build`, `dist` and `node_modules` by directory name; `--exclude` adds more
|
|
273
|
-
directory names (or globs) to skip the same way, on top of matching whole paths and file names.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
+
directory names (or globs) to skip the same way, on top of matching whole paths and file names.
|
|
277
|
+
Under a directory in a git checkout it checks only what git tracks or would, so nothing a
|
|
278
|
+
`.gitignore` names (a file named on the command line is checked regardless); `--no-gitignore`, or
|
|
279
|
+
`gitignore = false`, walks the directory whole. Exit codes: `0` no errors, `1` errors, `2` an
|
|
280
|
+
unreadable or unparsable file, a `--fix` its encoding can't hold, or a bad `pyproject.toml`. A
|
|
281
|
+
module is read in its PEP 263 declaration's encoding.
|
|
276
282
|
|
|
277
283
|
pre-commit, after ruff's hooks (or `constricter-fix`, which runs `--fix` first):
|
|
278
284
|
|
|
279
285
|
```yaml
|
|
280
286
|
- repo: https://github.com/ivylikethevine/python-constricter
|
|
281
|
-
rev:
|
|
287
|
+
rev: vX.Y.Z # the latest release; `pre-commit autoupdate` sets it
|
|
282
288
|
hooks:
|
|
283
289
|
- id: constricter
|
|
284
290
|
```
|
|
@@ -363,7 +369,7 @@ it to code scanning (the job needs `security-events: write`; the
|
|
|
363
369
|
```yaml
|
|
364
370
|
- run: constricter --format=sarif src > constricter.sarif
|
|
365
371
|
- if: ${{ !cancelled() }} # upload the findings even when the step above failed on them
|
|
366
|
-
uses: github/codeql-action/upload-sarif@
|
|
372
|
+
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
|
|
367
373
|
with:
|
|
368
374
|
sarif_file: constricter.sarif
|
|
369
375
|
category: constricter
|
|
@@ -236,6 +236,9 @@ uvx --from python-constricter constricter # or run it without installing: uv's t
|
|
|
236
236
|
pipx run --spec python-constricter constricter # or pipx's
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
On CPython 3.11 to 3.14 for Linux, macOS and Windows, pip installs a build compiled with mypyc,
|
|
240
|
+
about a fifth faster; anywhere else, the pure Python one, which finds and fixes the same.
|
|
241
|
+
|
|
239
242
|
Tools that run flake8 or pylint (VS Code's extensions, python-lsp-server, prospector, MegaLinter,
|
|
240
243
|
Trunk) pick the plugin up once it's installed alongside them.
|
|
241
244
|
|
|
@@ -243,15 +246,18 @@ Without `lint.external`, ruff flags `# noqa: LVA00x` (RUF102) and `--fix` delete
|
|
|
243
246
|
|
|
244
247
|
The CLI defaults to `.`, checks `*.py` and `*.ipynb`, and skips hidden dirs, `__pycache__`, `venv`,
|
|
245
248
|
`site-packages`, `build`, `dist` and `node_modules` by directory name; `--exclude` adds more
|
|
246
|
-
directory names (or globs) to skip the same way, on top of matching whole paths and file names.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
+
directory names (or globs) to skip the same way, on top of matching whole paths and file names.
|
|
250
|
+
Under a directory in a git checkout it checks only what git tracks or would, so nothing a
|
|
251
|
+
`.gitignore` names (a file named on the command line is checked regardless); `--no-gitignore`, or
|
|
252
|
+
`gitignore = false`, walks the directory whole. Exit codes: `0` no errors, `1` errors, `2` an
|
|
253
|
+
unreadable or unparsable file, a `--fix` its encoding can't hold, or a bad `pyproject.toml`. A
|
|
254
|
+
module is read in its PEP 263 declaration's encoding.
|
|
249
255
|
|
|
250
256
|
pre-commit, after ruff's hooks (or `constricter-fix`, which runs `--fix` first):
|
|
251
257
|
|
|
252
258
|
```yaml
|
|
253
259
|
- repo: https://github.com/ivylikethevine/python-constricter
|
|
254
|
-
rev:
|
|
260
|
+
rev: vX.Y.Z # the latest release; `pre-commit autoupdate` sets it
|
|
255
261
|
hooks:
|
|
256
262
|
- id: constricter
|
|
257
263
|
```
|
|
@@ -336,7 +342,7 @@ it to code scanning (the job needs `security-events: write`; the
|
|
|
336
342
|
```yaml
|
|
337
343
|
- run: constricter --format=sarif src > constricter.sarif
|
|
338
344
|
- if: ${{ !cancelled() }} # upload the findings even when the step above failed on them
|
|
339
|
-
uses: github/codeql-action/upload-sarif@
|
|
345
|
+
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
|
|
340
346
|
with:
|
|
341
347
|
sarif_file: constricter.sarif
|
|
342
348
|
category: constricter
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
|
2
2
|
"""Every local variable typed where it's first bound."""
|
|
3
3
|
|
|
4
|
+
from constricter._version import __version__ # written by the build, from the git tag
|
|
4
5
|
from constricter.offences import (
|
|
5
6
|
COMMENT_TYPED_TARGET,
|
|
6
7
|
DEFAULT_CHECKS,
|
|
@@ -25,7 +26,6 @@ from constricter.offences import (
|
|
|
25
26
|
)
|
|
26
27
|
from constricter.rules.checker import Coverage, annotation_coverage, check_source, check_tree
|
|
27
28
|
|
|
28
|
-
__version__ = "0.3.6"
|
|
29
29
|
__all__ = [
|
|
30
30
|
"COMMENT_TYPED_TARGET",
|
|
31
31
|
"DEFAULT_CHECKS",
|
|
@@ -6,15 +6,21 @@ cyclic. Left to itself, the collector looked through the kept trees again and ag
|
|
|
6
6
|
allocated: a third of the standard library's check (34s, 21s without). So while the command runs,
|
|
7
7
|
it's off: what indexing built is frozen (never looked through again), and after each file, only
|
|
8
8
|
what's been allocated since (the young generations) is collected, which bounds what a cycle can hold.
|
|
9
|
+
Not on a free-threaded build, whose collector has no generations: each collection there looks through
|
|
10
|
+
everything, and one after every file made a check several times as long. Its cycles wait for the run's
|
|
11
|
+
end (a check of pandas held 2.9 GB so, to 2.7 GB).
|
|
9
12
|
"""
|
|
10
13
|
|
|
11
14
|
import contextlib
|
|
12
15
|
import gc
|
|
16
|
+
import sysconfig
|
|
13
17
|
from collections.abc import Generator
|
|
14
18
|
from typing import Final
|
|
15
19
|
|
|
16
20
|
_YOUNG: Final = 1 # the generations collected after each file: 0 and 1, never the frozen or the old
|
|
17
21
|
_FREEZES: Final = hasattr(gc, "freeze") # PyPy's collector has no freezing
|
|
22
|
+
# Whether the collector has generations: a free-threaded build's has none, its GIL on or off.
|
|
23
|
+
_YOUNGER: Final = not sysconfig.get_config_var("Py_GIL_DISABLED")
|
|
18
24
|
|
|
19
25
|
|
|
20
26
|
@contextlib.contextmanager
|
|
@@ -44,4 +50,5 @@ def indexed() -> None:
|
|
|
44
50
|
|
|
45
51
|
def sweep() -> None:
|
|
46
52
|
"""Collect what the file just checked left behind (see the module docstring)."""
|
|
47
|
-
|
|
53
|
+
if _YOUNGER:
|
|
54
|
+
_ = gc.collect(_YOUNG)
|
|
@@ -5,72 +5,43 @@ import codecs
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import difflib
|
|
7
7
|
import importlib
|
|
8
|
-
import io
|
|
9
8
|
import json
|
|
10
9
|
import sys
|
|
11
|
-
import
|
|
12
|
-
from collections.abc import Callable, Mapping, Sequence
|
|
10
|
+
from collections.abc import Callable, Sequence
|
|
13
11
|
from dataclasses import replace
|
|
14
12
|
from functools import partial
|
|
15
13
|
from pathlib import Path
|
|
16
14
|
from typing import TYPE_CHECKING, Final, NamedTuple, TextIO, TypeAlias, cast
|
|
17
15
|
|
|
18
16
|
from constricter import notebook
|
|
19
|
-
from constricter.cli import baseline, collecting, schedule
|
|
17
|
+
from constricter.cli import baseline, collecting, schedule
|
|
18
|
+
from constricter.cli.lsp.protocol import HintError
|
|
20
19
|
from constricter.cli.options import Mode, Options, Output
|
|
21
20
|
from constricter.cli.paths import STDIN, python_files
|
|
22
|
-
from constricter.cli.
|
|
21
|
+
from constricter.cli.reading import encoding_of, read_text
|
|
23
22
|
from constricter.cli.report import Format, Result, fix_reasons, render, statistics
|
|
24
|
-
from constricter.cli.
|
|
25
|
-
from constricter.cli.
|
|
23
|
+
from constricter.cli.rounds import checked_all, settled
|
|
24
|
+
from constricter.cli.runs import Added, BaselineRun, CheckRun, CoverageRun, FileRun
|
|
25
|
+
from constricter.cli.workers import kept
|
|
26
26
|
from constricter.fix.core import fixes
|
|
27
|
-
from constricter.fix.core.known import
|
|
28
|
-
from constricter.fix.index import callers, decorated, installed, loose, plain, project
|
|
27
|
+
from constricter.fix.core.known import Outside, Returns
|
|
29
28
|
from constricter.noqa import lines, unsuppressed
|
|
30
29
|
from constricter.offences import (
|
|
31
30
|
DEFAULT_CHECKS,
|
|
32
31
|
Checks,
|
|
33
32
|
Edit,
|
|
34
|
-
Fix,
|
|
35
33
|
Offence,
|
|
36
34
|
)
|
|
37
35
|
from constricter.rules import parsed
|
|
38
36
|
from constricter.rules.checker import Checked, Coverage, annotation_coverage, checked_source
|
|
39
37
|
|
|
40
38
|
if TYPE_CHECKING: # slow to import, and only needed for `--infer-with`
|
|
41
|
-
from constricter.cli import hints
|
|
39
|
+
from constricter.cli.lsp import hints
|
|
42
40
|
|
|
43
41
|
EXIT_CLEAN: Final = 0
|
|
44
42
|
EXIT_FOUND: Final = 1
|
|
45
43
|
EXIT_ERROR: Final = 2
|
|
46
44
|
_ALL: Final = 100 # percent
|
|
47
|
-
_PY: Final = ".py" # a module the index names (not a notebook)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _encoding(path: Path, data: bytes) -> str:
|
|
51
|
-
"""Find the encoding of `path`, whose bytes are `data`: a notebook's is UTF-8 (it's JSON).
|
|
52
|
-
|
|
53
|
-
Returns:
|
|
54
|
-
A module's, as Python finds it: a PEP 263 declaration (`# -*- coding: latin-1 -*-`), a BOM
|
|
55
|
-
(`utf-8-sig`), or UTF-8. Raises `SyntaxError` for an unknown or contradictory one.
|
|
56
|
-
|
|
57
|
-
"""
|
|
58
|
-
return (
|
|
59
|
-
"utf-8" if path.suffix == notebook.SUFFIX else tokenize.detect_encoding(io.BytesIO(data).readline)[0]
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def _read(path: Path) -> str:
|
|
64
|
-
"""Read `path`, in its encoding; `-` is standard input.
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
Its text.
|
|
68
|
-
|
|
69
|
-
"""
|
|
70
|
-
if path == STDIN:
|
|
71
|
-
return sys.stdin.read()
|
|
72
|
-
data: bytes = path.read_bytes()
|
|
73
|
-
return data.decode(_encoding(path, data))
|
|
74
45
|
|
|
75
46
|
|
|
76
47
|
def _source(raw: str, name: Path) -> tuple[str, list[notebook.Line]]:
|
|
@@ -128,7 +99,6 @@ def _placed(offence: Offence, where: list[notebook.Line]) -> Offence:
|
|
|
128
99
|
|
|
129
100
|
|
|
130
101
|
# One changed part of a fixed file (the file, or a notebook's cell): its label, old and new lines.
|
|
131
|
-
_Hinted: TypeAlias = dict[Path, tuple[Hints, ...]] # each file's hints, a checker's each
|
|
132
102
|
_Change: TypeAlias = tuple[str, list[str], list[str]]
|
|
133
103
|
# One JSON value a coverage report holds, and one file's row of them.
|
|
134
104
|
_Scalar: TypeAlias = str | int | float
|
|
@@ -180,17 +150,11 @@ def fix_file(path: Path, offences: Sequence[Offence]) -> int:
|
|
|
180
150
|
if not (count := sum(1 for o in offences if o.edit is not None)):
|
|
181
151
|
return 0
|
|
182
152
|
data: bytes = path.read_bytes()
|
|
183
|
-
encoding: str =
|
|
153
|
+
encoding: str = encoding_of(path, data)
|
|
184
154
|
_ = path.write_bytes(_fixed(data.decode(encoding), path, offences).text.encode(encoding))
|
|
185
155
|
return count
|
|
186
156
|
|
|
187
157
|
|
|
188
|
-
HINT_ROUNDS: Final = 4 # with `--fix`: how many times a file is fixed, at most (see `_settled`)
|
|
189
|
-
# The fix kinds a scope's check comes to late, once the scope is read: what was bound to such a
|
|
190
|
-
# name's value before had no type yet, and has one once the fix is written.
|
|
191
|
-
_LATE_KINDS: Final = frozenset({"optional", "filled"})
|
|
192
|
-
|
|
193
|
-
|
|
194
158
|
def _shown_lines(raw: str, name: Path) -> dict[tuple[int | None, int], str]:
|
|
195
159
|
"""Map each line of `raw` (a notebook's, in its cells) to its text, as offences are placed.
|
|
196
160
|
|
|
@@ -214,7 +178,7 @@ def _checked(path: Path, name: Path, checks: Checks, outside: Outside) -> tuple[
|
|
|
214
178
|
Its text, and what checking it found.
|
|
215
179
|
|
|
216
180
|
"""
|
|
217
|
-
raw: str =
|
|
181
|
+
raw: str = read_text(path)
|
|
218
182
|
return raw, checked_text(raw, name, checks, outside=outside)
|
|
219
183
|
|
|
220
184
|
|
|
@@ -360,7 +324,7 @@ def _cover_path(path: Path, _outside: Outside, options: Options) -> CoverageRun:
|
|
|
360
324
|
"""
|
|
361
325
|
name: Path = options.input.name(path)
|
|
362
326
|
try:
|
|
363
|
-
return CoverageRun(annotation_coverage(_source(
|
|
327
|
+
return CoverageRun(annotation_coverage(_source(read_text(path), name)[0], options.checks))
|
|
364
328
|
except (OSError, ValueError, SyntaxError) as error:
|
|
365
329
|
return CoverageRun(error=f"{name}: error: {error}")
|
|
366
330
|
|
|
@@ -372,6 +336,7 @@ def _check_all(options: Options) -> tuple[list[Path], list[FileRun]]:
|
|
|
372
336
|
The names, and what each file found.
|
|
373
337
|
|
|
374
338
|
"""
|
|
339
|
+
schedule.forget()
|
|
375
340
|
with kept():
|
|
376
341
|
return _check_rounds(options)
|
|
377
342
|
|
|
@@ -381,14 +346,16 @@ def _check_rounds(options: Options) -> tuple[list[Path], list[FileRun]]:
|
|
|
381
346
|
|
|
382
347
|
With `--infer-with`, the type checker's server runs throughout: it's asked for every file's
|
|
383
348
|
hints first, and with `--fix`, each file a round changed is asked again and checked again, as its
|
|
384
|
-
new annotations change what the checker infers (see `
|
|
349
|
+
new annotations change what the checker infers (see `settled`). A baseline records offences,
|
|
385
350
|
and coverage counts annotations: hints change neither, only what `--fix` offers.
|
|
386
351
|
|
|
387
352
|
Returns:
|
|
388
353
|
The names, and what each file found.
|
|
389
354
|
|
|
390
355
|
"""
|
|
391
|
-
paths: list[Path] = list(
|
|
356
|
+
paths: list[Path] = list(
|
|
357
|
+
python_files(options.input.paths, options.input.exclude, tracked=options.input.gitignore),
|
|
358
|
+
)
|
|
392
359
|
check: Callable[[Path, Outside], FileRun]
|
|
393
360
|
if options.mode is Mode.COVERAGE:
|
|
394
361
|
check = partial(_cover_path, options=options)
|
|
@@ -398,18 +365,19 @@ def _check_rounds(options: Options) -> tuple[list[Path], list[FileRun]]:
|
|
|
398
365
|
check = partial(_check_path, options=options)
|
|
399
366
|
names: list[Path] = [options.input.name(path) for path in paths]
|
|
400
367
|
if options.mode in {Mode.COVERAGE, Mode.WRITE_BASELINE}:
|
|
401
|
-
return names,
|
|
368
|
+
return names, checked_all(paths, check, options)[0]
|
|
402
369
|
if not options.infer_with:
|
|
403
|
-
return names,
|
|
370
|
+
return names, settled(paths, check, options, None)
|
|
404
371
|
session: hints.Session
|
|
405
372
|
# Imported only for `--infer-with`: it's slow to import, for every run's start.
|
|
406
|
-
with cast("type[hints.Session]", importlib.import_module("constricter.cli.hints").Session)(
|
|
373
|
+
with cast("type[hints.Session]", importlib.import_module("constricter.cli.lsp.hints").Session)(
|
|
407
374
|
options.infer_with,
|
|
408
375
|
Path.cwd(),
|
|
409
376
|
options.jobs,
|
|
410
377
|
options.infer_memory,
|
|
378
|
+
kept=not options.infer_fresh,
|
|
411
379
|
) as session:
|
|
412
|
-
runs: list[FileRun] =
|
|
380
|
+
runs: list[FileRun] = settled(paths, check, options, session)
|
|
413
381
|
checker: str
|
|
414
382
|
path: Path
|
|
415
383
|
for checker, path in session.abandoned:
|
|
@@ -418,207 +386,6 @@ def _check_rounds(options: Options) -> tuple[list[Path], list[FileRun]]:
|
|
|
418
386
|
return names, runs
|
|
419
387
|
|
|
420
388
|
|
|
421
|
-
def _settled(
|
|
422
|
-
paths: Sequence[Path],
|
|
423
|
-
check: Callable[[Path, Outside], FileRun],
|
|
424
|
-
options: Options,
|
|
425
|
-
session: "hints.Session | None",
|
|
426
|
-
) -> list[FileRun]:
|
|
427
|
-
"""Check every file, then again each one `--fix` changed in a way a second check would see.
|
|
428
|
-
|
|
429
|
-
Until a round changes nothing, `HINT_ROUNDS` at most: a second pass is then left nothing.
|
|
430
|
-
|
|
431
|
-
Returns:
|
|
432
|
-
What each file found, a file's rounds joined.
|
|
433
|
-
|
|
434
|
-
"""
|
|
435
|
-
runs: list[FileRun]
|
|
436
|
-
modules: project.Index
|
|
437
|
-
runs, modules = _called_again(
|
|
438
|
-
paths,
|
|
439
|
-
_checked_all(paths, check, options, session),
|
|
440
|
-
check,
|
|
441
|
-
options,
|
|
442
|
-
session,
|
|
443
|
-
)
|
|
444
|
-
again: list[int] = [
|
|
445
|
-
index
|
|
446
|
-
for index, run in enumerate(runs)
|
|
447
|
-
if options.mode is Mode.FIX and paths[index] != STDIN and _unsettled(run, session)
|
|
448
|
-
]
|
|
449
|
-
for _ in range(HINT_ROUNDS - 1):
|
|
450
|
-
if not again:
|
|
451
|
-
break
|
|
452
|
-
redone: list[FileRun] = _checked_all([paths[i] for i in again], check, options, session, modules)[0]
|
|
453
|
-
index: int
|
|
454
|
-
run: FileRun
|
|
455
|
-
for index, run in zip(again, redone, strict=True):
|
|
456
|
-
runs[index] = merged(runs[index], run)
|
|
457
|
-
again = [index for index, run in zip(again, redone, strict=True) if _unsettled(run, session)]
|
|
458
|
-
return runs
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
def _unsettled(run: FileRun, session: "hints.Session | None") -> bool:
|
|
462
|
-
"""Check whether a round of `--fix` changed a file in a way that changes what's inferred there.
|
|
463
|
-
|
|
464
|
-
Returns:
|
|
465
|
-
Whether it fixed anything, with a type checker's hints: its new annotations change them.
|
|
466
|
-
Without, whether it made a fix the scope's check comes to late (`_LATE_KINDS`): what's bound
|
|
467
|
-
to such a name's value was read before its type was known.
|
|
468
|
-
|
|
469
|
-
"""
|
|
470
|
-
checked: CheckRun = cast("CheckRun", run)
|
|
471
|
-
return bool(checked.fixed) and (
|
|
472
|
-
session is not None
|
|
473
|
-
or any(
|
|
474
|
-
isinstance(made.offence.edit, Fix) and made.offence.edit.kinds & _LATE_KINDS
|
|
475
|
-
for made in checked.made
|
|
476
|
-
)
|
|
477
|
-
)
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
def _called_again(
|
|
481
|
-
paths: Sequence[Path],
|
|
482
|
-
first: tuple[list[FileRun], project.Index],
|
|
483
|
-
check: Callable[[Path, Outside], FileRun],
|
|
484
|
-
options: Options,
|
|
485
|
-
session: "hints.Session | None" = None,
|
|
486
|
-
) -> tuple[list[FileRun], project.Index]:
|
|
487
|
-
"""Check again the files whose functions' unannotated parameters every call passes one type.
|
|
488
|
-
|
|
489
|
-
Knowing those types (see `callers`), as guesses; a file among several of one module name isn't
|
|
490
|
-
(which its callers call can't be told). Then the other files calling those functions, knowing
|
|
491
|
-
what they now return. With `--fix` a file's rounds are joined; otherwise the last, knowing more,
|
|
492
|
-
stands.
|
|
493
|
-
|
|
494
|
-
Returns:
|
|
495
|
-
What each file found, and the index, with the parameters' types.
|
|
496
|
-
|
|
497
|
-
"""
|
|
498
|
-
runs: list[FileRun]
|
|
499
|
-
modules: project.Index
|
|
500
|
-
runs, modules = first
|
|
501
|
-
found: dict[str, callers.Parameters] = callers.parameters(
|
|
502
|
-
(run.calls for run in runs if isinstance(run, CheckRun)),
|
|
503
|
-
modules,
|
|
504
|
-
)
|
|
505
|
-
names: list[str | None] = [project.module_name(path) if path.suffix == _PY else None for path in paths]
|
|
506
|
-
again: list[int]
|
|
507
|
-
if not (again := [at for at, name in enumerate(names) if name in found and names.count(name) == 1]):
|
|
508
|
-
return runs, modules
|
|
509
|
-
calling: list[int] = _calling(runs, found, again)
|
|
510
|
-
before: project.Index = callers.with_parameters(modules, found)
|
|
511
|
-
modules = _checked_more(paths, runs, again, (check, options, session), before)
|
|
512
|
-
# A caller is checked again only if a function it imports now returns something else.
|
|
513
|
-
calling = [
|
|
514
|
-
at
|
|
515
|
-
for at in calling
|
|
516
|
-
if loose.returned(modules, paths[at], {}) != loose.returned(before, paths[at], {})
|
|
517
|
-
]
|
|
518
|
-
return runs, _checked_more(paths, runs, calling, (check, options, session), modules)
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
def _checked_more(
|
|
522
|
-
paths: Sequence[Path],
|
|
523
|
-
runs: list[FileRun],
|
|
524
|
-
which: Sequence[int],
|
|
525
|
-
how: "tuple[Callable[[Path, Outside], FileRun], Options, hints.Session | None]",
|
|
526
|
-
modules: project.Index,
|
|
527
|
-
) -> project.Index:
|
|
528
|
-
"""Check the files of `paths` at `which` again, knowing `modules`, into `runs`.
|
|
529
|
-
|
|
530
|
-
`how`: the check, the options and the type checkers' session. With `--fix` a file's rounds are
|
|
531
|
-
joined; otherwise the later one stands.
|
|
532
|
-
|
|
533
|
-
Returns:
|
|
534
|
-
The index, with what their functions now return.
|
|
535
|
-
|
|
536
|
-
"""
|
|
537
|
-
redone: list[FileRun]
|
|
538
|
-
redone, modules = _checked_all([paths[at] for at in which], how[0], how[1], how[2], modules)
|
|
539
|
-
at: int
|
|
540
|
-
run: FileRun
|
|
541
|
-
for at, run in zip(which, redone, strict=True):
|
|
542
|
-
runs[at] = merged(runs[at], run) if how[1].mode is Mode.FIX else run
|
|
543
|
-
return modules
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
def _calling(
|
|
547
|
-
runs: Sequence[FileRun],
|
|
548
|
-
found: Mapping[str, callers.Parameters],
|
|
549
|
-
again: Sequence[int],
|
|
550
|
-
) -> list[int]:
|
|
551
|
-
"""Find the files (not among `again`) calling a function whose parameters `found` types.
|
|
552
|
-
|
|
553
|
-
Returns:
|
|
554
|
-
Their indexes.
|
|
555
|
-
|
|
556
|
-
"""
|
|
557
|
-
typed: set[Callee] = {(module, function) for module, functions in found.items() for function in functions}
|
|
558
|
-
return [
|
|
559
|
-
at
|
|
560
|
-
for at, run in enumerate(runs)
|
|
561
|
-
if at not in again and isinstance(run, CheckRun) and typed & run.calls.calls.keys()
|
|
562
|
-
]
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
def _checked_all(
|
|
566
|
-
paths: Sequence[Path],
|
|
567
|
-
check: Callable[[Path, Outside], FileRun],
|
|
568
|
-
options: Options,
|
|
569
|
-
session: "hints.Session | None" = None,
|
|
570
|
-
modules: project.Index | None = None,
|
|
571
|
-
) -> tuple[list[FileRun], project.Index]:
|
|
572
|
-
"""Check `paths` (`--jobs` at a time), with the `session`'s hints, and `modules` (else indexed).
|
|
573
|
-
|
|
574
|
-
Returns:
|
|
575
|
-
What each file found, and the index of every file's module.
|
|
576
|
-
|
|
577
|
-
"""
|
|
578
|
-
# Before a worker starts: one forked while the servers' threads run could wait on a lock one holds.
|
|
579
|
-
hinted: _Hinted = {} if session is None else session.hints(_texts(paths))
|
|
580
|
-
if options.input.trace is not None:
|
|
581
|
-
hinted = traced.merged(hinted, traced.load(options.input.trace), paths)
|
|
582
|
-
coverage: bool = options.mode is Mode.COVERAGE # needs nothing from the other files
|
|
583
|
-
pool: Workers | None = started(paths, options.jobs) if options.jobs != 1 and len(paths) > 1 else None
|
|
584
|
-
if modules is None and pool:
|
|
585
|
-
modules = project.Index({}, []) if coverage else pool.index()
|
|
586
|
-
elif modules is None:
|
|
587
|
-
modules = project.Index({}, []) if coverage else project.index(paths)
|
|
588
|
-
collecting.indexed()
|
|
589
|
-
if not coverage:
|
|
590
|
-
modules = plain.settled(
|
|
591
|
-
decorated.passed(installed.with_installed(modules, installed.search_path())),
|
|
592
|
-
options.checks.plain_bases,
|
|
593
|
-
)
|
|
594
|
-
return schedule.checked(
|
|
595
|
-
paths,
|
|
596
|
-
check,
|
|
597
|
-
pool,
|
|
598
|
-
(modules, hinted),
|
|
599
|
-
merged if options.mode is Mode.FIX else None,
|
|
600
|
-
)
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
def _texts(paths: Sequence[Path]) -> dict[Path, str]:
|
|
604
|
-
"""Read each file to ask the type checker about (not a notebook, or standard input).
|
|
605
|
-
|
|
606
|
-
A file that can't be read is left out: checking it reports that.
|
|
607
|
-
|
|
608
|
-
Returns:
|
|
609
|
-
Each file's text.
|
|
610
|
-
|
|
611
|
-
"""
|
|
612
|
-
texts: dict[Path, str] = {}
|
|
613
|
-
path: Path
|
|
614
|
-
for path in paths:
|
|
615
|
-
if path != STDIN and path.suffix != notebook.SUFFIX:
|
|
616
|
-
# UnicodeDecodeError is a ValueError; a bad encoding declaration, a SyntaxError.
|
|
617
|
-
with contextlib.suppress(OSError, ValueError, SyntaxError):
|
|
618
|
-
texts[path] = _read(path)
|
|
619
|
-
return texts
|
|
620
|
-
|
|
621
|
-
|
|
622
389
|
def _report(options: Options, runs: Sequence[FileRun], files: int) -> int:
|
|
623
390
|
"""Print the results (`runs` is check/fix/diff mode's: every other mode has its own printing).
|
|
624
391
|
|
|
@@ -8,7 +8,7 @@ from functools import partial
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import TYPE_CHECKING, Final, TypeAlias, cast
|
|
10
10
|
|
|
11
|
-
from constricter.cli.protocol import SERVERS
|
|
11
|
+
from constricter.cli.lsp.protocol import SERVERS
|
|
12
12
|
from constricter.jsonc import is_int
|
|
13
13
|
from constricter.offences import FIX_KINDS, LEVELS, MESSAGES, WIDEN_KINDS
|
|
14
14
|
|
|
@@ -276,6 +276,7 @@ _READERS: dict[str, Callable[[_Toml], Default | None]] = {
|
|
|
276
276
|
"fix-plain-bases": _strings,
|
|
277
277
|
"type-comments": _flag,
|
|
278
278
|
"all-scopes": _flag,
|
|
279
|
+
"gitignore": _flag,
|
|
279
280
|
"per-path-levels": _levels,
|
|
280
281
|
"per-file-ignores": partial(_lists, read=_codes),
|
|
281
282
|
"narrower": partial(_lists, read=_strings), # a type hierarchy: each type, and those it's narrower than
|