python-constricter 0.4.1__cp313-cp313-win32.whl

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 (261) hide show
  1. constricter/__init__.py +55 -0
  2. constricter/__main__.py +26 -0
  3. constricter/_version.py +4 -0
  4. constricter/cli/__init__.py +2 -0
  5. constricter/cli/baseline.py +107 -0
  6. constricter/cli/collecting.py +54 -0
  7. constricter/cli/command.py +547 -0
  8. constricter/cli/config.py +322 -0
  9. constricter/cli/explain.py +104 -0
  10. constricter/cli/lsp/__init__.py +2 -0
  11. constricter/cli/lsp/connection.py +475 -0
  12. constricter/cli/lsp/edits.py +188 -0
  13. constricter/cli/lsp/guard.py +133 -0
  14. constricter/cli/lsp/hints.py +391 -0
  15. constricter/cli/lsp/protocol.py +192 -0
  16. constricter/cli/lsp/remembered.py +81 -0
  17. constricter/cli/lsp/stored.py +200 -0
  18. constricter/cli/options.py +799 -0
  19. constricter/cli/paths.py +151 -0
  20. constricter/cli/reading.py +36 -0
  21. constricter/cli/report.py +397 -0
  22. constricter/cli/rounds.py +328 -0
  23. constricter/cli/runs.py +90 -0
  24. constricter/cli/schedule.py +463 -0
  25. constricter/cli/traced.py +401 -0
  26. constricter/cli/workers.py +286 -0
  27. constricter/empty.py +66 -0
  28. constricter/fix/__init__.cp313-win32.pyd +0 -0
  29. constricter/fix/__init__.py +2 -0
  30. constricter/fix/core/__init__.cp313-win32.pyd +0 -0
  31. constricter/fix/core/__init__.py +2 -0
  32. constricter/fix/core/asked.cp313-win32.pyd +0 -0
  33. constricter/fix/core/asked.py +56 -0
  34. constricter/fix/core/fixes.cp313-win32.pyd +0 -0
  35. constricter/fix/core/fixes.py +317 -0
  36. constricter/fix/core/imports.cp313-win32.pyd +0 -0
  37. constricter/fix/core/imports.py +580 -0
  38. constricter/fix/core/inherited.cp313-win32.pyd +0 -0
  39. constricter/fix/core/inherited.py +311 -0
  40. constricter/fix/core/known.cp313-win32.pyd +0 -0
  41. constricter/fix/core/known.py +730 -0
  42. constricter/fix/core/signatures.cp313-win32.pyd +0 -0
  43. constricter/fix/core/signatures.py +132 -0
  44. constricter/fix/index/__init__.cp313-win32.pyd +0 -0
  45. constricter/fix/index/__init__.py +2 -0
  46. constricter/fix/index/awaits.cp313-win32.pyd +0 -0
  47. constricter/fix/index/awaits.py +90 -0
  48. constricter/fix/index/beyond.cp313-win32.pyd +0 -0
  49. constricter/fix/index/beyond.py +149 -0
  50. constricter/fix/index/callers.cp313-win32.pyd +0 -0
  51. constricter/fix/index/callers.py +155 -0
  52. constricter/fix/index/crossed.cp313-win32.pyd +0 -0
  53. constricter/fix/index/crossed.py +327 -0
  54. constricter/fix/index/decorated.cp313-win32.pyd +0 -0
  55. constricter/fix/index/decorated.py +140 -0
  56. constricter/fix/index/fixtures.cp313-win32.pyd +0 -0
  57. constricter/fix/index/fixtures.py +176 -0
  58. constricter/fix/index/linked.cp313-win32.pyd +0 -0
  59. constricter/fix/index/linked.py +142 -0
  60. constricter/fix/index/loose.cp313-win32.pyd +0 -0
  61. constricter/fix/index/loose.py +103 -0
  62. constricter/fix/index/managed.cp313-win32.pyd +0 -0
  63. constricter/fix/index/managed.py +63 -0
  64. constricter/fix/index/modules.cp313-win32.pyd +0 -0
  65. constricter/fix/index/modules.py +741 -0
  66. constricter/fix/index/offers.cp313-win32.pyd +0 -0
  67. constricter/fix/index/offers.py +252 -0
  68. constricter/fix/index/order.cp313-win32.pyd +0 -0
  69. constricter/fix/index/order.py +155 -0
  70. constricter/fix/index/origins.cp313-win32.pyd +0 -0
  71. constricter/fix/index/origins.py +238 -0
  72. constricter/fix/index/own_methods.cp313-win32.pyd +0 -0
  73. constricter/fix/index/own_methods.py +91 -0
  74. constricter/fix/index/own_overloads.cp313-win32.pyd +0 -0
  75. constricter/fix/index/own_overloads.py +70 -0
  76. constricter/fix/index/own_types.cp313-win32.pyd +0 -0
  77. constricter/fix/index/own_types.py +330 -0
  78. constricter/fix/index/params.cp313-win32.pyd +0 -0
  79. constricter/fix/index/params.py +81 -0
  80. constricter/fix/index/plain.cp313-win32.pyd +0 -0
  81. constricter/fix/index/plain.py +221 -0
  82. constricter/fix/index/project.cp313-win32.pyd +0 -0
  83. constricter/fix/index/project.py +797 -0
  84. constricter/fix/index/sides.cp313-win32.pyd +0 -0
  85. constricter/fix/index/sides.py +160 -0
  86. constricter/fix/index/strings.cp313-win32.pyd +0 -0
  87. constricter/fix/index/strings.py +46 -0
  88. constricter/fix/index/stubs/__init__.cp313-win32.pyd +0 -0
  89. constricter/fix/index/stubs/__init__.py +2 -0
  90. constricter/fix/index/stubs/atoms.cp313-win32.pyd +0 -0
  91. constricter/fix/index/stubs/atoms.py +425 -0
  92. constricter/fix/index/stubs/chained.cp313-win32.pyd +0 -0
  93. constricter/fix/index/stubs/chained.py +69 -0
  94. constricter/fix/index/stubs/classnames.cp313-win32.pyd +0 -0
  95. constricter/fix/index/stubs/classnames.py +124 -0
  96. constricter/fix/index/stubs/declared.cp313-win32.pyd +0 -0
  97. constricter/fix/index/stubs/declared.py +508 -0
  98. constricter/fix/index/stubs/installed.cp313-win32.pyd +0 -0
  99. constricter/fix/index/stubs/installed.py +425 -0
  100. constricter/fix/index/stubs/names.cp313-win32.pyd +0 -0
  101. constricter/fix/index/stubs/names.py +200 -0
  102. constricter/fix/index/stubs/read.cp313-win32.pyd +0 -0
  103. constricter/fix/index/stubs/read.py +70 -0
  104. constricter/fix/index/stubs/signatures.cp313-win32.pyd +0 -0
  105. constricter/fix/index/stubs/signatures.py +656 -0
  106. constricter/fix/index/stubs/stubbed.cp313-win32.pyd +0 -0
  107. constricter/fix/index/stubs/stubbed.py +208 -0
  108. constricter/fix/index/taken.cp313-win32.pyd +0 -0
  109. constricter/fix/index/taken.py +45 -0
  110. constricter/fix/index/tuples.cp313-win32.pyd +0 -0
  111. constricter/fix/index/tuples.py +112 -0
  112. constricter/fix/libraries/__init__.cp313-win32.pyd +0 -0
  113. constricter/fix/libraries/__init__.py +2 -0
  114. constricter/fix/libraries/library.cp313-win32.pyd +0 -0
  115. constricter/fix/libraries/library.py +420 -0
  116. constricter/fix/libraries/opened.cp313-win32.pyd +0 -0
  117. constricter/fix/libraries/opened.py +147 -0
  118. constricter/fix/libraries/overloads.cp313-win32.pyd +0 -0
  119. constricter/fix/libraries/overloads.py +960 -0
  120. constricter/fix/libraries/receivers.cp313-win32.pyd +0 -0
  121. constricter/fix/libraries/receivers.py +274 -0
  122. constricter/fix/libraries/stdlib.cp313-win32.pyd +0 -0
  123. constricter/fix/libraries/stdlib.py +752 -0
  124. constricter/fix/libraries/walks.cp313-win32.pyd +0 -0
  125. constricter/fix/libraries/walks.py +49 -0
  126. constricter/fix/tables/aliases.json +147 -0
  127. constricter/fix/tables/attributes.json +700 -0
  128. constricter/fix/tables/awaited.json +69 -0
  129. constricter/fix/tables/awaited_overloads.json +14 -0
  130. constricter/fix/tables/bases.json +888 -0
  131. constricter/fix/tables/classes.json +1941 -0
  132. constricter/fix/tables/elements.json +156 -0
  133. constricter/fix/tables/functions.json +58 -0
  134. constricter/fix/tables/generic_attributes.json +254 -0
  135. constricter/fix/tables/method_overloads.json +461 -0
  136. constricter/fix/tables/method_signatures.json +954 -0
  137. constricter/fix/tables/methods.json +954 -0
  138. constricter/fix/tables/overloads.json +540 -0
  139. constricter/fix/tables/platform.json +37 -0
  140. constricter/fix/tables/returns.json +1362 -0
  141. constricter/fix/tables/scalar_members.json +16 -0
  142. constricter/fix/tables/scalars.json +2707 -0
  143. constricter/fix/tables/source.json +28 -0
  144. constricter/fix/tables/subscriptable.json +77 -0
  145. constricter/fix/tables/type_parameters.json +178 -0
  146. constricter/fix/tables/variables.json +2962 -0
  147. constricter/fix/values/__init__.cp313-win32.pyd +0 -0
  148. constricter/fix/values/__init__.py +2 -0
  149. constricter/fix/values/aliased.cp313-win32.pyd +0 -0
  150. constricter/fix/values/aliased.py +195 -0
  151. constricter/fix/values/bodies.cp313-win32.pyd +0 -0
  152. constricter/fix/values/bodies.py +76 -0
  153. constricter/fix/values/callables.cp313-win32.pyd +0 -0
  154. constricter/fix/values/callables.py +323 -0
  155. constricter/fix/values/called.cp313-win32.pyd +0 -0
  156. constricter/fix/values/called.py +194 -0
  157. constricter/fix/values/classvars.cp313-win32.pyd +0 -0
  158. constricter/fix/values/classvars.py +629 -0
  159. constricter/fix/values/decided.cp313-win32.pyd +0 -0
  160. constricter/fix/values/decided.py +429 -0
  161. constricter/fix/values/displays.cp313-win32.pyd +0 -0
  162. constricter/fix/values/displays.py +101 -0
  163. constricter/fix/values/doubts.cp313-win32.pyd +0 -0
  164. constricter/fix/values/doubts.py +677 -0
  165. constricter/fix/values/entered.cp313-win32.pyd +0 -0
  166. constricter/fix/values/entered.py +332 -0
  167. constricter/fix/values/fills.cp313-win32.pyd +0 -0
  168. constricter/fix/values/fills.py +446 -0
  169. constricter/fix/values/guesses.cp313-win32.pyd +0 -0
  170. constricter/fix/values/guesses.py +376 -0
  171. constricter/fix/values/hinted.cp313-win32.pyd +0 -0
  172. constricter/fix/values/hinted.py +811 -0
  173. constricter/fix/values/inference.cp313-win32.pyd +0 -0
  174. constricter/fix/values/inference.py +698 -0
  175. constricter/fix/values/loops.cp313-win32.pyd +0 -0
  176. constricter/fix/values/loops.py +335 -0
  177. constricter/fix/values/members.cp313-win32.pyd +0 -0
  178. constricter/fix/values/members.py +578 -0
  179. constricter/fix/values/narrowed.cp313-win32.pyd +0 -0
  180. constricter/fix/values/narrowed.py +220 -0
  181. constricter/fix/values/operated.cp313-win32.pyd +0 -0
  182. constricter/fix/values/operated.py +202 -0
  183. constricter/fix/values/ordered.cp313-win32.pyd +0 -0
  184. constricter/fix/values/ordered.py +74 -0
  185. constricter/fix/values/returned.cp313-win32.pyd +0 -0
  186. constricter/fix/values/returned.py +479 -0
  187. constricter/fix/values/returns.cp313-win32.pyd +0 -0
  188. constricter/fix/values/returns.py +237 -0
  189. constricter/fix/values/shapes.cp313-win32.pyd +0 -0
  190. constricter/fix/values/shapes.py +654 -0
  191. constricter/fix/values/stored.cp313-win32.pyd +0 -0
  192. constricter/fix/values/stored.py +564 -0
  193. constricter/fix/values/targets.cp313-win32.pyd +0 -0
  194. constricter/fix/values/targets.py +419 -0
  195. constricter/fix/values/unchecked.cp313-win32.pyd +0 -0
  196. constricter/fix/values/unchecked.py +278 -0
  197. constricter/jsonc.py +50 -0
  198. constricter/noqa.py +47 -0
  199. constricter/notebook.py +115 -0
  200. constricter/offences.py +436 -0
  201. constricter/overrides.py +24 -0
  202. constricter/plugins/__init__.py +2 -0
  203. constricter/plugins/flake8.py +133 -0
  204. constricter/plugins/pylint.py +186 -0
  205. constricter/py.typed +0 -0
  206. constricter/recording.py +660 -0
  207. constricter/rules/__init__.cp313-win32.pyd +0 -0
  208. constricter/rules/__init__.py +2 -0
  209. constricter/rules/annotations.cp313-win32.pyd +0 -0
  210. constricter/rules/annotations.py +296 -0
  211. constricter/rules/binding.cp313-win32.pyd +0 -0
  212. constricter/rules/binding.py +652 -0
  213. constricter/rules/calls.cp313-win32.pyd +0 -0
  214. constricter/rules/calls.py +460 -0
  215. constricter/rules/checker.cp313-win32.pyd +0 -0
  216. constricter/rules/checker.py +536 -0
  217. constricter/rules/declarations.cp313-win32.pyd +0 -0
  218. constricter/rules/declarations.py +730 -0
  219. constricter/rules/decorators.cp313-win32.pyd +0 -0
  220. constricter/rules/decorators.py +351 -0
  221. constricter/rules/flow.cp313-win32.pyd +0 -0
  222. constricter/rules/flow.py +502 -0
  223. constricter/rules/keys.cp313-win32.pyd +0 -0
  224. constricter/rules/keys.py +79 -0
  225. constricter/rules/late.cp313-win32.pyd +0 -0
  226. constricter/rules/late.py +969 -0
  227. constricter/rules/narrowing.cp313-win32.pyd +0 -0
  228. constricter/rules/narrowing.py +98 -0
  229. constricter/rules/parsed.cp313-win32.pyd +0 -0
  230. constricter/rules/parsed.py +136 -0
  231. constricter/rules/quoted.cp313-win32.pyd +0 -0
  232. constricter/rules/quoted.py +141 -0
  233. constricter/rules/rebinding.cp313-win32.pyd +0 -0
  234. constricter/rules/rebinding.py +169 -0
  235. constricter/rules/recorded.cp313-win32.pyd +0 -0
  236. constricter/rules/recorded.py +168 -0
  237. constricter/rules/redundant.cp313-win32.pyd +0 -0
  238. constricter/rules/redundant.py +160 -0
  239. constricter/rules/scope.cp313-win32.pyd +0 -0
  240. constricter/rules/scope.py +802 -0
  241. constricter/rules/scoped.cp313-win32.pyd +0 -0
  242. constricter/rules/scoped.py +232 -0
  243. constricter/rules/scoping.cp313-win32.pyd +0 -0
  244. constricter/rules/scoping.py +346 -0
  245. constricter/rules/settings.cp313-win32.pyd +0 -0
  246. constricter/rules/settings.py +189 -0
  247. constricter/rules/syntax.cp313-win32.pyd +0 -0
  248. constricter/rules/syntax.py +472 -0
  249. constricter/rules/tables.cp313-win32.pyd +0 -0
  250. constricter/rules/tables.py +169 -0
  251. constricter/rules/walked.cp313-win32.pyd +0 -0
  252. constricter/rules/walked.py +127 -0
  253. constricter/rules/widened.cp313-win32.pyd +0 -0
  254. constricter/rules/widened.py +322 -0
  255. constricter/trace.py +374 -0
  256. e45ce06b2fdd5c43f782__mypyc.cp313-win32.pyd +0 -0
  257. python_constricter-0.4.1.dist-info/METADATA +400 -0
  258. python_constricter-0.4.1.dist-info/RECORD +261 -0
  259. python_constricter-0.4.1.dist-info/WHEEL +4 -0
  260. python_constricter-0.4.1.dist-info/entry_points.txt +5 -0
  261. python_constricter-0.4.1.dist-info/licenses/LICENSE.md +21 -0
@@ -0,0 +1,730 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """What `--fix` knows: a module's declarations it infers from (`Known`), and what it infers (`Inference`)."""
3
+
4
+ import ast
5
+ import builtins
6
+ import copy
7
+ import json
8
+ import re
9
+ from collections.abc import Mapping
10
+ from dataclasses import dataclass, field, fields
11
+ from pathlib import Path
12
+ from types import MappingProxyType
13
+ from typing import TYPE_CHECKING, Final, NamedTuple, TypeAlias, cast
14
+
15
+ from constricter.empty import Empty
16
+ from constricter.fix.core.inherited import Beyond, Lineage
17
+ from constricter.fix.core.signatures import Expansion, ReadSignature
18
+ from constricter.offences import MAX_LENGTH, VAGUE
19
+ from constricter.overrides import override
20
+ from constricter.rules.annotations import free_of, free_of_all, roots
21
+
22
+ if TYPE_CHECKING:
23
+ from _typeshed import DataclassInstance
24
+
25
+ _BUILTINS: Final = frozenset(dir(builtins))
26
+ _DOT: Final = "."
27
+ # The standard library's modules only some platforms have: a checker on another finds nothing in
28
+ # one. Generated from typeshed (`stdlib_tables`).
29
+ _PLATFORM: Final = frozenset(
30
+ cast(
31
+ "dict[str, str]",
32
+ json.loads(Path(__file__).parents[1].joinpath("tables", "platform.json").read_bytes()),
33
+ ),
34
+ )
35
+ _NAME: Final = re.compile(r"[A-Za-z_]\w*") # each name in a return's template
36
+ # What a name refers to: a module and an attribute of it (`None`: the module itself).
37
+ Origin: TypeAlias = tuple[str, str | None]
38
+ # How a return template starts that's the type itself, as the module calling it writes it: a
39
+ # checked file's overload's (see `constricter.fix.index.stubs.stubbed.overloaded`).
40
+ SPELLED: Final = "="
41
+ # After a fixture's `request` parameter's name: the key its `request.param` is typed under (see `Typed`).
42
+ PARAM: Final = ".param"
43
+
44
+
45
+ # A `dict` itself, not a `UserDict`: every inference reads it, as fast as a `dict` is read.
46
+ class Typed(dict[str, str]): # ruff: ignore[subclass-builtin]
47
+ """A scope's names' types so far, counting each change: what's inferred of a value holds till then.
48
+
49
+ `held`: the names bound to a call whose type no annotation can write (pytest's `CaptureResult`),
50
+ each with the call, which an attribute read of the name is typed by (see `stood`).
51
+ """
52
+
53
+ def __init__(self, types: Mapping[str, str] | None = None, /) -> None:
54
+ """Start with `types`, counted as none."""
55
+ super().__init__(types or {})
56
+ self.version: int = 0
57
+ self.held: Mapping[str, ast.expr] = MappingProxyType({})
58
+
59
+ @override
60
+ def __setitem__(self, name: str, annotation: str) -> None:
61
+ """Type `name`, and count it."""
62
+ self.version += 1
63
+ super().__setitem__(name, annotation)
64
+
65
+ @override
66
+ def setdefault(self, name: str, annotation: str = "", /) -> str:
67
+ """Type `name` if nothing has, and count it.
68
+
69
+ Returns:
70
+ Its type.
71
+
72
+ """
73
+ self.version += 1
74
+ return super().setdefault(name, annotation)
75
+
76
+ def taking(self, others: Mapping[str, str]) -> None:
77
+ """Type each of `others`' names, and count it."""
78
+ self.version += 1
79
+ super().update(others)
80
+
81
+ def forget(self, name: str) -> None:
82
+ """Leave `name` with no type, and count it."""
83
+ self.version += 1
84
+ _ = super().pop(name, None)
85
+
86
+ def hold(self, name: str, call: ast.expr | None) -> None:
87
+ """Take `name` to stand for `call`, whose type no annotation can write; for nothing, with `None`."""
88
+ if call is None and name not in self.held:
89
+ return
90
+ self.version += 1
91
+ kept: dict[str, ast.expr] = {key: value for key, value in self.held.items() if key != name}
92
+ self.held = kept if call is None else {**kept, name: call}
93
+
94
+
95
+ def _held(node: ast.AST, held: Mapping[str, ast.expr]) -> ast.expr | None:
96
+ name: str
97
+ match node:
98
+ case ast.Attribute(value=ast.Name(id=name)) if name in held:
99
+ return held[name]
100
+ case _:
101
+ return None
102
+
103
+
104
+ def stood(value: ast.expr, declared: Mapping[str, str]) -> ast.expr:
105
+ """Write `value` with the call each name `declared` holds stands for, where it reads an attribute of one.
106
+
107
+ `both.out` as `capsys.readouterr().out`, after `both = capsys.readouterr()` (see `Typed.held`).
108
+
109
+ Returns:
110
+ It: `value` itself where it reads none (most values), else a copy.
111
+
112
+ """
113
+ held: Mapping[str, ast.expr] = declared.held if isinstance(declared, Typed) else {}
114
+ if not held or all(_held(node, held) is None for node in ast.walk(value)):
115
+ return value
116
+ copied: ast.expr = copy.deepcopy(value)
117
+ node: ast.AST
118
+ for node in ast.walk(copied):
119
+ call: ast.expr | None
120
+ if (call := _held(node, held)) is not None:
121
+ cast("ast.Attribute", node).value = call
122
+ return copied
123
+
124
+
125
+ class Guarded(NamedTuple):
126
+ """A name a file can write only in an annotation: imported under `if TYPE_CHECKING:` (see `project`).
127
+
128
+ `origin`: what it refers to; `statement`: the import to add for it, or `None` if the file has it.
129
+ """
130
+
131
+ origin: Origin
132
+ statement: str | None
133
+
134
+
135
+ class Checking(NamedTuple):
136
+ """A module's imports for type checking alone: where they go, whose types need one, and its own.
137
+
138
+ `block`: the first and last line of the body of the `if TYPE_CHECKING:` among its leading
139
+ imports, if it has one (else zeros); `lazy`: the top-level packages only its functions import,
140
+ which may not be there to import when the module is (another platform's `pwd`); `bound`: what
141
+ the imports under its top-level `if TYPE_CHECKING:`s bind, each name's dotted origin.
142
+ """
143
+
144
+ block: tuple[int, int] = (0, 0)
145
+ lazy: frozenset[str] = frozenset()
146
+ bound: Mapping[str, str] = MappingProxyType({})
147
+
148
+
149
+ class Classes(NamedTuple):
150
+ """Classes' instance attributes (and properties) and methods' returns, by the class's name as spelled.
151
+
152
+ What `classes` and `method_returns` read from a module, and `crossed.imported` adds for the
153
+ classes a file imports from other checked files.
154
+ """
155
+
156
+ attributes: Mapping[str, Mapping[str, str]]
157
+ methods: Mapping[str, Mapping[str, str]]
158
+
159
+
160
+ @dataclass
161
+ class ImportPlan:
162
+ """How a module can name a library type, and the imports that takes (see `fix.core.imports.plan`).
163
+
164
+ `bound`: each name its imports bind, and what that is (`io`, `io.BytesIO`); `taken`: every name
165
+ bound anywhere in it; `after`: the line added imports go after; `defined`: each name it binds
166
+ at its top level (an import, a class, a function, an assignment), and the line it's first bound
167
+ on; `added`: each name an added import binds, and that import's statement, as `spell` chose them.
168
+ `guarded`: the names other checked files' types and a type checker's hints are written with
169
+ that the module imports (or is to import) under `if TYPE_CHECKING:` alone; `checking`: its own
170
+ such imports, where one goes, and whose types need one (see `Checking`); `postponed`: whether
171
+ it has `from __future__ import annotations`, so none of its annotations is evaluated.
172
+ """
173
+
174
+ bound: Mapping[str, str]
175
+ taken: frozenset[str]
176
+ after: int
177
+ defined: Mapping[str, int] = field(default_factory=dict[str, int])
178
+ added: dict[str, str] = field(default_factory=dict[str, str])
179
+ guarded: Mapping[str, Guarded] = field(default_factory=dict[str, Guarded])
180
+ checking: Checking = field(default_factory=Checking)
181
+ postponed: bool = False
182
+ values: frozenset[str] = frozenset() # names it binds as values somewhere (see `imports.taken_names`)
183
+
184
+ def type_checking(self) -> str | None:
185
+ """Name `typing.TYPE_CHECKING` for a new block, by an import that runs: the module reads it.
186
+
187
+ Returns:
188
+ The name, or `None` if every way to write it is taken.
189
+
190
+ """
191
+ return self.spell("typing.TYPE_CHECKING", runs=True)
192
+
193
+ def spell(self, qualified: str, *, runs: bool = False) -> str | None:
194
+ """Name `qualified` (`io.BufferedReader`) in this module, adding an import if it has to.
195
+
196
+ Through an import it has (see `named`), for type checking alone too (`checking`, or one
197
+ `guarded` already), else a new `from io import BufferedReader`, else a new `import io`, but
198
+ only binding a name nothing in the module binds. Under `if TYPE_CHECKING:` for a module in
199
+ `checking.lazy`, and not at all for one only some platforms have. With `runs`, only through
200
+ an import that runs: the name is read as the module is imported (`TYPE_CHECKING` itself).
201
+
202
+ Returns:
203
+ The name, or `None` if every way to write it is taken.
204
+
205
+ """
206
+ found: str | None
207
+ if (found := self.named(qualified) or (None if runs else self._checked(qualified))) is not None:
208
+ return found
209
+ module: str
210
+ name: str
211
+ module, _, name = qualified.rpartition(".")
212
+ statement: str = f"from {module} import {name}"
213
+ top: str = module.partition(_DOT)[0]
214
+ if not runs and top in self.checking.lazy:
215
+ if top in _PLATFORM or module in _PLATFORM:
216
+ return None
217
+ return name if self.guard(name, (module, name), statement) else None
218
+ if self._free(name, statement):
219
+ self.added[name] = statement
220
+ return name
221
+ statement = f"import {module}"
222
+ if _DOT not in module and self._free(module, statement):
223
+ self.added[module] = statement
224
+ return qualified
225
+ return None
226
+
227
+ def named(self, qualified: str) -> str | None:
228
+ """Name `qualified` (`io.BufferedReader`) through an import the module has.
229
+
230
+ `io.BufferedReader` after `import io`, `BufferedReader` after `from io import BufferedReader`.
231
+
232
+ Returns:
233
+ The name, or `None` if no import of its names it.
234
+
235
+ """
236
+ module: str
237
+ name: str
238
+ module, _, name = qualified.rpartition(".")
239
+ bound: str
240
+ origin: str
241
+ for bound, origin in self.bound.items():
242
+ if origin == qualified:
243
+ return bound
244
+ for bound, origin in self.bound.items():
245
+ if origin == module:
246
+ return f"{bound}.{name}"
247
+ return None
248
+
249
+ def _checked(self, qualified: str) -> str | None:
250
+ """Name `qualified` through an import for type checking alone: one it has, or is to have.
251
+
252
+ Returns:
253
+ The name, or `None` if none binds it, or the module binds that name to a value too.
254
+
255
+ """
256
+ origins: dict[str, str] = dict(self.checking.bound)
257
+ bound: str
258
+ each: Guarded
259
+ for bound, each in self.guarded.items():
260
+ origins[bound] = _DOT.join(part for part in each.origin if part)
261
+ return next(
262
+ (bound for bound, origin in origins.items() if origin == qualified and bound not in self.values),
263
+ None,
264
+ )
265
+
266
+ def guard(self, name: str, origin: Origin, statement: str) -> bool:
267
+ """Let annotations be written with `name`, bound to `origin` for type checking alone.
268
+
269
+ By the import `statement`, to add under `if TYPE_CHECKING:`. A name already guarded must be
270
+ so by the same statement; a new import must bind a name nothing in the module binds.
271
+
272
+ Returns:
273
+ Whether they can.
274
+
275
+ """
276
+ found: Guarded | None
277
+ if (found := self.guarded.get(name)) is not None:
278
+ return found.statement == statement
279
+ if name in self.added or not self._free(name, statement):
280
+ return False
281
+ # A new mapping: the one it had is the file's `Outside.guarded`, which other files' types read.
282
+ self.guarded = {**self.guarded, name: Guarded(origin, statement)}
283
+ return True
284
+
285
+ def _free(self, name: str, statement: str) -> bool:
286
+ """Check that `statement` may bind `name`: it already does, or nothing (not a builtin) does.
287
+
288
+ Returns:
289
+ Whether it may.
290
+
291
+ """
292
+ if name in self.added:
293
+ return self.added[name] == statement
294
+ return name not in self.taken and name not in _BUILTINS and name not in self.guarded
295
+
296
+
297
+ class LibraryNames(NamedTuple):
298
+ """How the module names the library functions `--fix` knows.
299
+
300
+ `typing.cast` (see `casts`), and the standard library's (see `stdlib.origins`). `installed`: the
301
+ installed packages' functions it calls whose arguments decide their type, by the call's name as
302
+ written, each with its signatures (their classes' methods too, as `np.ndarray.astype`), and
303
+ `classes` the installed classes it passes as arguments, which may bind their type variables;
304
+ `parameters`: those methods' classes' type parameters, which a receiver's type binds; `lineage`:
305
+ the installed classes it names, each with where it and its ancestors are defined, which a
306
+ receiver's type is matched by; `aliases`: the public aliases of installed generic classes it
307
+ names, whose methods are their classes' (see `constricter.fix.index.stubs.stubbed`). `starred`: whether
308
+ every Python the module runs on parses an unpacked tuple in a subscript (`tuple[int, *Ts]`,
309
+ 3.11's syntax), so an annotation may be written with one. `aliased`: whether `typing` has
310
+ `TypeAlias` on every Python the module runs on (3.10's; yes where that isn't known), so a new
311
+ import may name it.
312
+ """
313
+
314
+ casts: frozenset[str] = frozenset()
315
+ stdlib: Mapping[str, str] = MappingProxyType({})
316
+ plan: ImportPlan | None = None # how to name a type the module doesn't import yet
317
+ installed: Mapping[str, tuple[ReadSignature, ...]] = MappingProxyType({})
318
+ classes: frozenset[str] = frozenset()
319
+ parameters: Mapping[str, tuple[str, ...]] = MappingProxyType({})
320
+ lineage: Mapping[str, tuple[str, ...]] = MappingProxyType({})
321
+ aliases: Mapping[str, Expansion] = MappingProxyType({})
322
+ starred: bool = False
323
+ aliased: bool = True
324
+
325
+
326
+ class Returned(NamedTuple):
327
+ """Return types read from unannotated functions' own `return` statements (see `returned`).
328
+
329
+ `calls`: the module's functions', by name (certain); `methods`: its classes' methods', by class
330
+ (guesses: a subclass may override one). `guesses`: for a function (by name) or method (`C.m`)
331
+ whose `return`s are themselves guesses, what they rest on (`FIX_KINDS`), which its calls do too.
332
+ `attributes`: its classes' unannotated instance attributes typed by their every `self.x = value`,
333
+ by class (guesses: a subclass or outside code may assign one too); a guessed value's origins are
334
+ in `guesses` as `C.x`.
335
+ """
336
+
337
+ calls: Mapping[str, str] = MappingProxyType({})
338
+ methods: Mapping[str, Mapping[str, str]] = MappingProxyType({})
339
+ guesses: Mapping[str, frozenset[str]] = MappingProxyType({})
340
+ attributes: Mapping[str, Mapping[str, str]] = MappingProxyType({})
341
+
342
+
343
+ class Partial(NamedTuple):
344
+ """Declared returns that have a vague part (`tuple[Row, dict[str, Any]]`, `Any`).
345
+
346
+ One types a call only as far as `vague` allows (see `Limits.vague`); an unpacking takes its
347
+ parts. `calls`: functions', by the call's name as written; `methods`: classes' methods', by the
348
+ class's name as spelled.
349
+ """
350
+
351
+ # Plain `dict`s, not `MappingProxyType`s: the CLI's worker processes are sent them, pickled.
352
+ calls: Mapping[str, str] = Empty()
353
+ methods: Mapping[str, Mapping[str, str]] = Empty()
354
+
355
+
356
+ # A function's parameters declared a builtin container (`names: list[str]`), by name: each one's
357
+ # position (`None`: keyword only), whether a keyword can pass it, and its type as the calling module
358
+ # writes it. What an empty container passed to one is taken as (see `constricter.fix.values.fills`).
359
+ Takers: TypeAlias = Mapping[str, tuple[int | None, bool, str]]
360
+
361
+
362
+ class Indirect(NamedTuple):
363
+ """Declared returns that type something other than a plain call.
364
+
365
+ `awaits`: what awaiting a call to each of the module's `async def`s gives (see
366
+ `awaited_returns`); `partial`: those only an unpacking can use (see `Partial`), other checked
367
+ files' too. `tuples`: the named tuples the module names, its own and other checked files', each
368
+ with the tuple unpacking one gives (see `targets.named_tuples`). `unions`: the type aliases of a
369
+ union it names, its own and other checked files', each with the union as its module writes it
370
+ (see `targets.aliased_unions`). `takers`: the functions it calls that declare a parameter a
371
+ builtin container, its own and other checked files', each with those parameters (see `Takers`).
372
+ """
373
+
374
+ awaits: Mapping[str, str] = MappingProxyType({})
375
+ partial: Partial = Partial()
376
+ tuples: Mapping[str, str] = MappingProxyType({})
377
+ unions: Mapping[str, str] = MappingProxyType({})
378
+ takers: Mapping[str, Takers] = MappingProxyType({})
379
+
380
+
381
+ class ClassSide(NamedTuple):
382
+ """What each class the module defines offers beyond its instances' own members.
383
+
384
+ On the class itself: `class_attributes`, `class_methods`. From its bases: `lineage`, which base
385
+ an instance takes a method from (see `Lineage`). In its body: `variables`, the plain classes'
386
+ typed by their values, the module's own and those it imports (see `constricter.fix.values.classvars`);
387
+ `plain`: which of its own classes are plain.
388
+ """
389
+
390
+ attributes: Mapping[str, Mapping[str, str]]
391
+ methods: Mapping[str, Mapping[str, str]]
392
+ lineage: Lineage = Lineage()
393
+ variables: Mapping[str, Mapping[str, str]] = MappingProxyType({})
394
+ plain: frozenset[str] = frozenset()
395
+
396
+
397
+ class Limits(NamedTuple):
398
+ """What the rules let an annotation be, which `--fix` writes no further than.
399
+
400
+ `max_length`: the longest tuple display typed element by element (LVA011's); `vague`: how vague a
401
+ type may be (LVA005's level, see `annotations.vague_fits`). `marks`: the lines a widening marked,
402
+ whose annotations are vaguer by design, each with its mark's columns (see `rules.widened`).
403
+ """
404
+
405
+ max_length: int = MAX_LENGTH
406
+ vague: int = VAGUE
407
+ marks: Mapping[int, tuple[int, int]] = MappingProxyType({})
408
+
409
+
410
+ @dataclass(frozen=True)
411
+ class Known: # pylint: disable=too-many-instance-attributes
412
+ """What a module declares that `--fix` can infer a value's type from.
413
+
414
+ `calls`: its functions' return types (`returns`, plus other modules', see `project.calls`).
415
+ `factories`: names that build a class or special form rather than an instance of it (see
416
+ `factories`), so a call to one is never guessed to construct one. `classes` and `methods`: each
417
+ class's annotated attributes (see `classes`) and methods' return types (see `method_returns`).
418
+ `indirect`: its declared returns no plain call has (see `Indirect`).
419
+ `class_side`: what `cls.x` and `cls.method()` give in a classmethod, where `cls` is `type[C]`
420
+ (see `ClassSide`). `names`: how it names library functions (see `LibraryNames`). `returned`: what
421
+ its unannotated functions return (see `Returned`).
422
+ """
423
+
424
+ calls: Mapping[str, str]
425
+ factories: frozenset[str]
426
+ classes: Mapping[str, Mapping[str, str]]
427
+ methods: Mapping[str, Mapping[str, str]]
428
+ indirect: Indirect = field(default_factory=Indirect)
429
+ class_side: "ClassSide" = field(default_factory=lambda: ClassSide({}, {}))
430
+ names: LibraryNames = field(default_factory=LibraryNames)
431
+ limits: Limits = field(default_factory=Limits)
432
+ returned: Returned = field(default_factory=Returned)
433
+ # The decorators that give a function back, as the module spells them (see `rules.decorators`).
434
+ vouched: frozenset[str] = frozenset()
435
+
436
+ def is_builtin(self, name: str) -> bool:
437
+ """Check that `name` still means the builtin: nothing in the module binds it (a parameter, say).
438
+
439
+ Returns:
440
+ Whether it does; with no import plan (a module not read for one), whether it's a builtin.
441
+
442
+ """
443
+ return name in _BUILTINS and (self.names.plan is None or name not in self.names.plan.taken)
444
+
445
+
446
+ class Returns(NamedTuple):
447
+ """What a module's unannotated functions return (`Returned.calls`), for the files importing them.
448
+
449
+ `calls`: each function's type, by its name (or, imported, as the importing file spells it: `f`,
450
+ `u.f`); `guesses`: for one whose `return`s are guesses, what they rest on (`FIX_KINDS`);
451
+ `names`: what each name their types use that the module imports for type checking alone
452
+ (see `Guarded`) refers to. `methods`: its classes' methods' (`Returned.methods`), by class (as
453
+ the importing file spells it), a guessed one's origins in `guesses` as `C.m`.
454
+ """
455
+
456
+ # Plain `dict`s, not `MappingProxyType`s: the CLI's worker processes are sent them, pickled.
457
+ calls: Mapping[str, str] = Empty()
458
+ guesses: Mapping[str, frozenset[str]] = Empty()
459
+ names: Mapping[str, Origin] = Empty()
460
+ methods: Mapping[str, Mapping[str, str]] = Empty()
461
+
462
+
463
+ class Offered(NamedTuple):
464
+ """What a type checker's hint would write if its editor accepted it (the hint's own edits).
465
+
466
+ `text`: the annotation as it would insert it, which may spell a class through a module the file
467
+ imports (`collections.Counter[str]`, where the hint shows `Counter[str]`); `imports`: the
468
+ imports it would add for the names in it, each a statement binding one name
469
+ (`from shapes import Shape`, `import shapes`).
470
+ """
471
+
472
+ text: str
473
+ imports: tuple[str, ...] = ()
474
+
475
+
476
+ class Hints(NamedTuple):
477
+ """A type checker's inlay hints for one file (`--infer-with`): which checker, and each type.
478
+
479
+ Or a traced run's types for it (`--infer-from`), of `kind` `traced`: what its fixes rest on.
480
+
481
+ Each hint's type is its text as the checker printed it (`int`, `list[str]`), by where the name
482
+ it types ends: its line (from 1) and UTF-8 byte column, as `ast`'s `end_col_offset`.
483
+ `offered`: what each hint that has edits would write (see `Offered`), by the same place.
484
+ """
485
+
486
+ checker: str = ""
487
+ # Plain `dict`s, not `MappingProxyType`s: the CLI's worker processes are sent them, pickled.
488
+ types: Mapping[tuple[int, int], str] = Empty()
489
+ offered: Mapping[tuple[int, int], Offered] = Empty()
490
+ kind: str = "checker"
491
+
492
+
493
+ # An argument's type, and what it rests on if it's a guess (`FIX_KINDS`; none: it's certain).
494
+ Passed: TypeAlias = tuple[str, frozenset[str]]
495
+ # A module's functions' parameters every call passes one type: each one's, by name, by function.
496
+ Seeds: TypeAlias = Mapping[str, Mapping[str, Passed]]
497
+
498
+
499
+ class Call(NamedTuple):
500
+ """One call to a checked file's function: what `--fix` knows each argument's type to be (`None`: unknown).
501
+
502
+ `unpacked`: whether it passes `*args` or `**kwargs`, whose arguments can't be matched.
503
+ """
504
+
505
+ positional: tuple[Passed | None, ...] = ()
506
+ keywords: tuple[tuple[str, Passed | None], ...] = ()
507
+ unpacked: bool = False
508
+
509
+
510
+ Callee: TypeAlias = tuple[str, str] # a checked file's function: its module and name
511
+
512
+
513
+ class Observed(NamedTuple):
514
+ """What a module does with checked files' functions whose parameters aren't all annotated.
515
+
516
+ `calls`: each call to one, by the function; `escaped`: those it uses other than by calling them
517
+ (a callback, a stored reference), whose callers can't be known.
518
+ """
519
+
520
+ # Plain `dict`s and tuples, not `MappingProxyType`s: the CLI's worker processes send them back, pickled.
521
+ calls: Mapping[Callee, tuple[Call, ...]] = Empty()
522
+ escaped: frozenset[Callee] = frozenset()
523
+
524
+
525
+ def pickled(frozen: "DataclassInstance") -> dict[str, object]:
526
+ """Give a frozen dataclass's fields, to pickle it by: a class mypyc compiled has no other.
527
+
528
+ Returns:
529
+ Each field's value, by its name.
530
+
531
+ """
532
+ return {each.name: getattr(frozen, each.name) for each in fields(frozen)}
533
+
534
+
535
+ def unpickled(frozen: "DataclassInstance", state: Mapping[str, object]) -> None:
536
+ """Set a frozen dataclass's fields from what `pickled` gave, past the class's own refusal."""
537
+ name: str
538
+ for name in state:
539
+ # Past the class's own `__setattr__`, which refuses.
540
+ object.__setattr__(frozen, name, state[name]) # ruff: ignore[unnecessary-dunder-call]
541
+
542
+
543
+ @dataclass(frozen=True, slots=True, kw_only=True)
544
+ class Outside: # pylint: disable=too-many-instance-attributes
545
+ """What the CLI knows of a file from outside it, for `--fix`.
546
+
547
+ `calls`: the return types of functions other checked files define, `returned` those of their
548
+ unannotated functions (see `Returns`), and `classes` their classes' attributes and methods'
549
+ returns, as the file spells them (see `crossed.imported`); `hints`, a
550
+ type checker's types for what `--fix` can't type itself (`--infer-with`); `type_vars`, the names
551
+ it imports that are type variables where they're defined (see `project.type_vars`), which a
552
+ type its own functions declare can't be written with outside them; `guarded`, the names those
553
+ types are written with that it can use in an annotation alone (see `Guarded`); `generics`, the
554
+ generic classes it imports from them, which a fix mustn't write bare. `callees`: the functions of
555
+ checked files it may call whose parameters aren't all annotated, as it spells them (`f`, `u.f`);
556
+ `parameters`: for its own such functions, each parameter every call passes one type (see
557
+ `constricter.fix.index.callers`). `plain`: which of its own classes are plain, as the index of checked
558
+ files settles it (`None`: as the file alone sees, see `constricter.fix.values.classvars`); `members`:
559
+ the variables of the plain classes it imports from them, typed by their values, as it spells
560
+ each class. `same`: each group of ways it spells one class or alias another module defines
561
+ (`CoreSchema`, `core_schema.CoreSchema`; see `linked.same`). `partial`: the returns of other
562
+ checked files' functions and methods that only an unpacking can use (see `Partial`). `tuples`:
563
+ the named tuples it imports from them, as it spells each (see `Indirect.tuples`), and `unions`
564
+ their type aliases of a union (see `Indirect.unions`). `untyped`: the functions it calls of
565
+ theirs that declare no return, as it spells each call (see `linked.untyped`).
566
+ """
567
+
568
+ calls: Mapping[str, str] = field(default_factory=dict[str, str])
569
+ classes: Classes | None = None
570
+ hints: tuple[Hints, ...] = () # each checker's, in the order they were named
571
+ type_vars: frozenset[str] = frozenset()
572
+ returned: Returns = field(default_factory=Returns)
573
+ guarded: Mapping[str, Guarded] = field(default_factory=dict[str, Guarded])
574
+ generics: frozenset[str] = frozenset() # other checked files' generic classes, as it spells them
575
+ callees: Mapping[str, Callee] = field(default_factory=dict[str, Callee])
576
+ # See `Seeds`
577
+ parameters: Mapping[str, Mapping[str, Passed]] = field(
578
+ default_factory=dict[str, Mapping[str, Passed]],
579
+ )
580
+ # See `LibraryNames.installed`
581
+ overloaded: Mapping[str, tuple[ReadSignature, ...]] = field(
582
+ default_factory=dict[str, tuple[ReadSignature, ...]],
583
+ )
584
+ installed_classes: frozenset[str] = frozenset() # see `LibraryNames.classes`
585
+ # See `LibraryNames.parameters`
586
+ installed_parameters: Mapping[str, tuple[str, ...]] = field(default_factory=dict[str, tuple[str, ...]])
587
+ # See `LibraryNames.lineage`
588
+ installed_lineage: Mapping[str, tuple[str, ...]] = field(default_factory=dict[str, tuple[str, ...]])
589
+ # See `LibraryNames.aliases`
590
+ installed_aliases: Mapping[str, Expansion] = field(
591
+ default_factory=dict[str, Expansion],
592
+ )
593
+ # The classes it imports under `if TYPE_CHECKING:` alone, which an annotation can name (see `offers.own`).
594
+ checking: Mapping[str, Guarded] = field(default_factory=dict[str, Guarded])
595
+ plain: frozenset[str] | None = None
596
+ members: Mapping[str, Mapping[str, str]] = field(default_factory=dict[str, Mapping[str, str]])
597
+ same: tuple[frozenset[str], ...] = ()
598
+ partial: Partial = field(default_factory=Partial)
599
+ tuples: Mapping[str, str] = field(default_factory=dict[str, str])
600
+ # The pytest fixtures its tests can take: each one's value's type (see `constricter.fix.index.fixtures`).
601
+ fixtures: Mapping[str, Passed] = field(default_factory=dict[str, Passed])
602
+ # Its classes' bases other checked files define: where each one's own end (see `Lineage.beyond`).
603
+ beyond: Mapping[str, Beyond] = field(default_factory=dict[str, Beyond])
604
+ # What awaiting a call of each `async def` it imports from them gives, as it spells the call.
605
+ awaits: Mapping[str, str] = field(default_factory=dict[str, str])
606
+ # The names it imports that another checked module binds by assignment (see `linked.values`).
607
+ values: frozenset[str] = frozenset()
608
+ unions: Mapping[str, str] = field(default_factory=dict[str, str])
609
+ untyped: frozenset[str] = frozenset()
610
+ # The attributes its classes take bound to an empty container from their bases (see `beyond.emptied`).
611
+ emptied: Mapping[str, Mapping[str, str]] = field(default_factory=dict[str, Mapping[str, str]])
612
+ # Their functions it calls that declare a parameter a builtin container (see `Indirect.takers`).
613
+ takers: Mapping[str, Takers] = field(default_factory=dict[str, Takers])
614
+ # What `with` gives of each call it makes to their `@contextmanager` functions (see `index.managed`).
615
+ managers: Mapping[str, Passed] = field(default_factory=dict[str, Passed])
616
+ # The decorators it uses that give a function back, by the index (see `index.decorated.vouched`).
617
+ vouched: frozenset[str] = frozenset()
618
+
619
+ @override
620
+ def __getstate__(self) -> dict[str, object]:
621
+ """Pickle it by its fields (see `pickled`).
622
+
623
+ Returns:
624
+ Them.
625
+
626
+ """
627
+ return pickled(self)
628
+
629
+ def __setstate__(self, state: dict[str, object]) -> None:
630
+ """Unpickle it (see `unpickled`)."""
631
+ unpickled(self, state)
632
+
633
+ def usable(self, taken: frozenset[str], present: frozenset[str]) -> "Outside":
634
+ """Drop what other files offer whose type needs a name imported that the module binds already.
635
+
636
+ That's a name to import under `if TYPE_CHECKING:` (see `Guarded`) that the module binds anywhere
637
+ else, a function's local or parameter included: the import would shadow it, or it the import.
638
+ A checked file's overloaded function is dropped whole, if any of its signatures returns such a
639
+ type: which one a call takes isn't known here.
640
+ `present`: those it imports so already (see `imports.present`), which `--fix` wrote since the
641
+ files were indexed: each is one the file has.
642
+
643
+ Returns:
644
+ What's left.
645
+
646
+ """
647
+ guarded: dict[str, Guarded] = {
648
+ name: Guarded(found.origin, None) if name in present else found
649
+ for name, found in self.guarded.items()
650
+ }
651
+ clashing: frozenset[str] = frozenset(
652
+ name for name, found in guarded.items() if found.statement is not None and name in taken
653
+ )
654
+ if not clashing and guarded == self.guarded:
655
+ return self
656
+ members: Classes | None = self.classes
657
+ return Outside(
658
+ calls=free_of(self.calls, clashing),
659
+ classes=None
660
+ if members is None
661
+ else Classes(free_of_all(members.attributes, clashing), free_of_all(members.methods, clashing)),
662
+ hints=self.hints,
663
+ type_vars=self.type_vars,
664
+ returned=Returns(
665
+ free_of(self.returned.calls, clashing),
666
+ self.returned.guesses,
667
+ self.returned.names,
668
+ free_of_all(self.returned.methods, clashing),
669
+ ),
670
+ guarded={name: found for name, found in guarded.items() if name not in clashing},
671
+ generics=self.generics,
672
+ callees=self.callees,
673
+ parameters=self.parameters,
674
+ overloaded={
675
+ callee: signatures
676
+ for callee, signatures in self.overloaded.items()
677
+ if not any(clashing.intersection(_NAME.findall(each.returns or "")) for each in signatures)
678
+ },
679
+ installed_classes=self.installed_classes,
680
+ installed_parameters=self.installed_parameters,
681
+ installed_lineage=self.installed_lineage,
682
+ installed_aliases=self.installed_aliases,
683
+ checking=self.checking,
684
+ plain=self.plain,
685
+ members=self.members,
686
+ same=self.same,
687
+ partial=Partial(
688
+ free_of(self.partial.calls, clashing),
689
+ free_of_all(self.partial.methods, clashing),
690
+ ),
691
+ tuples=free_of(self.tuples, clashing),
692
+ fixtures={name: typed for name, typed in self.fixtures.items() if not roots(typed[0]) & clashing},
693
+ beyond=self.beyond,
694
+ awaits=free_of(self.awaits, clashing),
695
+ values=self.values,
696
+ unions=self.unions,
697
+ untyped=self.untyped,
698
+ managers={name: typed for name, typed in self.managers.items() if not roots(typed[0]) & clashing},
699
+ emptied=self.emptied,
700
+ takers={
701
+ name: {param: taken for param, taken in takers.items() if not roots(taken[2]) & clashing}
702
+ for name, takers in self.takers.items()
703
+ },
704
+ vouched=self.vouched,
705
+ )
706
+
707
+
708
+ class Inference(NamedTuple):
709
+ """An annotation `--fix` would add, how the value decided it (`--show-fixes`), and by which means.
710
+
711
+ `kinds` are the `FIX_KINDS` ids of every mechanism that decided it, parts included (`[1, 2]`
712
+ is a `container` of `literal`s), for `fix-select` and `fix-ignore`. `reads`: the names,
713
+ attributes and subscripts (as source text) whose own types it takes as they are (`deque([x])`'s
714
+ `x`), which a type checker sees narrowed where the function tests them.
715
+ """
716
+
717
+ annotation: str
718
+ reason: str
719
+ kinds: frozenset[str] = frozenset()
720
+ reads: tuple[str, ...] = ()
721
+
722
+
723
+ def kinds_of(*parts: Inference | None, kind: str) -> frozenset[str]:
724
+ """Collect `kind` and the kinds of each part an inference was built from.
725
+
726
+ Returns:
727
+ Them all.
728
+
729
+ """
730
+ return frozenset({kind}).union(*(part.kinds for part in parts if part is not None))