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.
Files changed (184) hide show
  1. python_constricter-0.4.1/.gitignore +107 -0
  2. {python_constricter-0.3.6 → python_constricter-0.4.1}/LICENSE.md +1 -1
  3. {python_constricter-0.3.6 → python_constricter-0.4.1}/PKG-INFO +12 -6
  4. {python_constricter-0.3.6 → python_constricter-0.4.1}/README.md +11 -5
  5. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/__init__.py +1 -1
  6. python_constricter-0.4.1/constricter/_version.py +4 -0
  7. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/collecting.py +8 -1
  8. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/command.py +22 -255
  9. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/config.py +2 -1
  10. python_constricter-0.4.1/constricter/cli/lsp/__init__.py +2 -0
  11. python_constricter-0.3.6/constricter/cli/hints.py → python_constricter-0.4.1/constricter/cli/lsp/connection.py +16 -362
  12. {python_constricter-0.3.6/constricter/cli → python_constricter-0.4.1/constricter/cli/lsp}/edits.py +1 -1
  13. {python_constricter-0.3.6/constricter/cli → python_constricter-0.4.1/constricter/cli/lsp}/guard.py +5 -1
  14. python_constricter-0.4.1/constricter/cli/lsp/hints.py +391 -0
  15. python_constricter-0.4.1/constricter/cli/lsp/remembered.py +81 -0
  16. python_constricter-0.4.1/constricter/cli/lsp/stored.py +200 -0
  17. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/options.py +27 -6
  18. python_constricter-0.4.1/constricter/cli/paths.py +151 -0
  19. python_constricter-0.4.1/constricter/cli/reading.py +36 -0
  20. python_constricter-0.4.1/constricter/cli/rounds.py +328 -0
  21. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/schedule.py +84 -36
  22. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/workers.py +78 -8
  23. python_constricter-0.4.1/constricter/empty.py +66 -0
  24. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/imports.py +66 -13
  25. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/inherited.py +2 -1
  26. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/known.py +83 -53
  27. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/signatures.py +14 -7
  28. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/beyond.py +3 -2
  29. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/callers.py +3 -2
  30. python_constricter-0.4.1/constricter/fix/index/crossed.py +327 -0
  31. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/decorated.py +29 -10
  32. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/loose.py +11 -6
  33. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/modules.py +90 -198
  34. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/offers.py +123 -9
  35. python_constricter-0.4.1/constricter/fix/index/origins.py +238 -0
  36. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/own_methods.py +31 -7
  37. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/own_overloads.py +2 -2
  38. python_constricter-0.4.1/constricter/fix/index/own_types.py +330 -0
  39. python_constricter-0.4.1/constricter/fix/index/params.py +81 -0
  40. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/plain.py +109 -13
  41. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/project.py +86 -269
  42. python_constricter-0.4.1/constricter/fix/index/stubs/__init__.py +2 -0
  43. {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/atoms.py +3 -3
  44. {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/chained.py +2 -2
  45. {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/classnames.py +2 -2
  46. {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/declared.py +8 -6
  47. {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/installed.py +84 -7
  48. python_constricter-0.4.1/constricter/fix/index/stubs/names.py +200 -0
  49. {python_constricter-0.3.6/constricter/fix/index → python_constricter-0.4.1/constricter/fix/index/stubs}/read.py +2 -2
  50. python_constricter-0.3.6/constricter/fix/index/stubbed.py → python_constricter-0.4.1/constricter/fix/index/stubs/signatures.py +28 -372
  51. python_constricter-0.4.1/constricter/fix/index/stubs/stubbed.py +208 -0
  52. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/library.py +60 -14
  53. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/overloads.py +315 -323
  54. python_constricter-0.4.1/constricter/fix/libraries/receivers.py +274 -0
  55. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/stdlib.py +14 -2
  56. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/attributes.json +8 -8
  57. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/functions.json +15 -0
  58. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/method_overloads.json +91 -91
  59. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/method_signatures.json +60 -2
  60. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/overloads.json +5 -2
  61. python_constricter-0.4.1/constricter/fix/tables/platform.json +37 -0
  62. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/source.json +5 -3
  63. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/variables.json +3 -0
  64. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/callables.py +4 -2
  65. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/called.py +89 -1
  66. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/classvars.py +100 -12
  67. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/decided.py +39 -6
  68. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/displays.py +19 -5
  69. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/doubts.py +10 -6
  70. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/entered.py +2 -1
  71. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/fills.py +18 -7
  72. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/guesses.py +11 -5
  73. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/hinted.py +175 -9
  74. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/inference.py +21 -319
  75. python_constricter-0.4.1/constricter/fix/values/loops.py +335 -0
  76. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/members.py +9 -6
  77. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/operated.py +10 -8
  78. python_constricter-0.4.1/constricter/fix/values/returned.py +479 -0
  79. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/shapes.py +85 -10
  80. python_constricter-0.3.6/constricter/fix/values/returned.py → python_constricter-0.4.1/constricter/fix/values/stored.py +47 -450
  81. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/targets.py +49 -13
  82. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/notebook.py +6 -8
  83. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/offences.py +49 -4
  84. python_constricter-0.4.1/constricter/overrides.py +24 -0
  85. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/plugins/pylint.py +20 -17
  86. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/recording.py +93 -16
  87. python_constricter-0.4.1/constricter/rules/annotations.py +296 -0
  88. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/binding.py +11 -6
  89. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/calls.py +3 -2
  90. python_constricter-0.4.1/constricter/rules/checker.py +536 -0
  91. python_constricter-0.3.6/constricter/rules/annotations.py → python_constricter-0.4.1/constricter/rules/declarations.py +42 -286
  92. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/decorators.py +158 -28
  93. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/keys.py +1 -1
  94. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/late.py +319 -20
  95. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/parsed.py +9 -3
  96. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/rebinding.py +5 -3
  97. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/recorded.py +13 -12
  98. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/scope.py +31 -224
  99. python_constricter-0.4.1/constricter/rules/scoped.py +232 -0
  100. python_constricter-0.4.1/constricter/rules/scoping.py +346 -0
  101. python_constricter-0.4.1/constricter/rules/settings.py +189 -0
  102. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/tables.py +1 -1
  103. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/widened.py +1 -1
  104. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/trace.py +5 -9
  105. {python_constricter-0.3.6 → python_constricter-0.4.1}/hatch_build.py +6 -7
  106. python_constricter-0.4.1/mypyc.txt +280 -0
  107. {python_constricter-0.3.6 → python_constricter-0.4.1}/pyproject.toml +97 -16
  108. python_constricter-0.3.6/stdlib_tables/overloads.py → python_constricter-0.4.1/stdlib_tables/accepting.py +118 -472
  109. python_constricter-0.4.1/stdlib_tables/configured.py +464 -0
  110. {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/elements.py +2 -1
  111. python_constricter-0.4.1/stdlib_tables/forms.py +222 -0
  112. python_constricter-0.4.1/stdlib_tables/generate.py +552 -0
  113. python_constricter-0.4.1/stdlib_tables/overloads.py +507 -0
  114. {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/partial.json +0 -1
  115. {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/reading.py +46 -229
  116. python_constricter-0.4.1/stdlib_tables/shed.py +95 -0
  117. {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/stubs.py +77 -249
  118. {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/templates.py +2 -1
  119. python_constricter-0.4.1/stdlib_tables/written.py +98 -0
  120. python_constricter-0.3.6/.gitignore +0 -48
  121. python_constricter-0.3.6/constricter/cli/paths.py +0 -82
  122. python_constricter-0.3.6/constricter/cli/remembered.py +0 -52
  123. python_constricter-0.3.6/constricter/fix/index/own_types.py +0 -161
  124. python_constricter-0.3.6/constricter/rules/checker.py +0 -950
  125. python_constricter-0.3.6/stdlib_tables/generate.py +0 -996
  126. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/__main__.py +0 -0
  127. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/__init__.py +0 -0
  128. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/baseline.py +0 -0
  129. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/explain.py +0 -0
  130. {python_constricter-0.3.6/constricter/cli → python_constricter-0.4.1/constricter/cli/lsp}/protocol.py +0 -0
  131. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/report.py +0 -0
  132. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/runs.py +0 -0
  133. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/cli/traced.py +0 -0
  134. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/__init__.py +0 -0
  135. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/__init__.py +0 -0
  136. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/asked.py +0 -0
  137. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/core/fixes.py +0 -0
  138. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/__init__.py +0 -0
  139. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/awaits.py +0 -0
  140. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/fixtures.py +0 -0
  141. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/linked.py +0 -0
  142. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/managed.py +0 -0
  143. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/order.py +0 -0
  144. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/sides.py +0 -0
  145. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/strings.py +0 -0
  146. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/taken.py +0 -0
  147. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/index/tuples.py +0 -0
  148. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/__init__.py +0 -0
  149. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/opened.py +0 -0
  150. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/libraries/walks.py +0 -0
  151. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/aliases.json +0 -0
  152. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/awaited.json +0 -0
  153. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/awaited_overloads.json +0 -0
  154. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/bases.json +0 -0
  155. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/classes.json +0 -0
  156. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/elements.json +0 -0
  157. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/generic_attributes.json +0 -0
  158. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/methods.json +0 -0
  159. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/returns.json +0 -0
  160. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/scalar_members.json +0 -0
  161. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/scalars.json +0 -0
  162. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/subscriptable.json +0 -0
  163. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/tables/type_parameters.json +0 -0
  164. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/__init__.py +0 -0
  165. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/aliased.py +0 -0
  166. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/bodies.py +0 -0
  167. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/narrowed.py +0 -0
  168. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/ordered.py +0 -0
  169. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/returns.py +0 -0
  170. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/fix/values/unchecked.py +0 -0
  171. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/jsonc.py +0 -0
  172. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/noqa.py +0 -0
  173. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/plugins/__init__.py +0 -0
  174. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/plugins/flake8.py +0 -0
  175. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/py.typed +0 -0
  176. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/__init__.py +0 -0
  177. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/flow.py +0 -0
  178. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/narrowing.py +0 -0
  179. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/quoted.py +0 -0
  180. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/redundant.py +0 -0
  181. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/syntax.py +0 -0
  182. {python_constricter-0.3.6 → python_constricter-0.4.1}/constricter/rules/walked.py +0 -0
  183. {python_constricter-0.3.6 → python_constricter-0.4.1}/stdlib_tables/__init__.py +0 -0
  184. {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.6
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. Exit
274
- codes: `0` no errors, `1` errors, `2` an unreadable or unparsable file, a `--fix` its encoding can't
275
- hold, or a bad `pyproject.toml`. A module is read in its PEP 263 declaration's encoding.
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: v0.3.6
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@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
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. Exit
247
- codes: `0` no errors, `1` errors, `2` an unreadable or unparsable file, a `--fix` its encoding can't
248
- hold, or a bad `pyproject.toml`. A module is read in its PEP 263 declaration's encoding.
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: v0.3.6
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@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
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",
@@ -0,0 +1,4 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """The version this was built as, from its git tag (written by the build, not tracked)."""
3
+
4
+ __version__ = "0.4.1"
@@ -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
- _ = gc.collect(_YOUNG)
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 tokenize
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, traced
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.protocol import HintError
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.runs import Added, BaselineRun, CheckRun, CoverageRun, FileRun, merged
25
- from constricter.cli.workers import Workers, kept, started
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 Callee, Hints, Outside, Returns
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 = _encoding(path, data)
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 = _read(path)
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(_read(path), name)[0], options.checks))
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 `_settled`). A baseline records offences,
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(python_files(options.input.paths, options.input.exclude))
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, _checked_all(paths, check, options)[0]
368
+ return names, checked_all(paths, check, options)[0]
402
369
  if not options.infer_with:
403
- return names, _settled(paths, check, options, None)
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] = _settled(paths, check, options, session)
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
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """constricter.cli.lsp."""