python-constricter 0.4.1__cp312-cp312-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__.cp312-win32.pyd +0 -0
  29. constricter/fix/__init__.py +2 -0
  30. constricter/fix/core/__init__.cp312-win32.pyd +0 -0
  31. constricter/fix/core/__init__.py +2 -0
  32. constricter/fix/core/asked.cp312-win32.pyd +0 -0
  33. constricter/fix/core/asked.py +56 -0
  34. constricter/fix/core/fixes.cp312-win32.pyd +0 -0
  35. constricter/fix/core/fixes.py +317 -0
  36. constricter/fix/core/imports.cp312-win32.pyd +0 -0
  37. constricter/fix/core/imports.py +580 -0
  38. constricter/fix/core/inherited.cp312-win32.pyd +0 -0
  39. constricter/fix/core/inherited.py +311 -0
  40. constricter/fix/core/known.cp312-win32.pyd +0 -0
  41. constricter/fix/core/known.py +730 -0
  42. constricter/fix/core/signatures.cp312-win32.pyd +0 -0
  43. constricter/fix/core/signatures.py +132 -0
  44. constricter/fix/index/__init__.cp312-win32.pyd +0 -0
  45. constricter/fix/index/__init__.py +2 -0
  46. constricter/fix/index/awaits.cp312-win32.pyd +0 -0
  47. constricter/fix/index/awaits.py +90 -0
  48. constricter/fix/index/beyond.cp312-win32.pyd +0 -0
  49. constricter/fix/index/beyond.py +149 -0
  50. constricter/fix/index/callers.cp312-win32.pyd +0 -0
  51. constricter/fix/index/callers.py +155 -0
  52. constricter/fix/index/crossed.cp312-win32.pyd +0 -0
  53. constricter/fix/index/crossed.py +327 -0
  54. constricter/fix/index/decorated.cp312-win32.pyd +0 -0
  55. constricter/fix/index/decorated.py +140 -0
  56. constricter/fix/index/fixtures.cp312-win32.pyd +0 -0
  57. constricter/fix/index/fixtures.py +176 -0
  58. constricter/fix/index/linked.cp312-win32.pyd +0 -0
  59. constricter/fix/index/linked.py +142 -0
  60. constricter/fix/index/loose.cp312-win32.pyd +0 -0
  61. constricter/fix/index/loose.py +103 -0
  62. constricter/fix/index/managed.cp312-win32.pyd +0 -0
  63. constricter/fix/index/managed.py +63 -0
  64. constricter/fix/index/modules.cp312-win32.pyd +0 -0
  65. constricter/fix/index/modules.py +741 -0
  66. constricter/fix/index/offers.cp312-win32.pyd +0 -0
  67. constricter/fix/index/offers.py +252 -0
  68. constricter/fix/index/order.cp312-win32.pyd +0 -0
  69. constricter/fix/index/order.py +155 -0
  70. constricter/fix/index/origins.cp312-win32.pyd +0 -0
  71. constricter/fix/index/origins.py +238 -0
  72. constricter/fix/index/own_methods.cp312-win32.pyd +0 -0
  73. constricter/fix/index/own_methods.py +91 -0
  74. constricter/fix/index/own_overloads.cp312-win32.pyd +0 -0
  75. constricter/fix/index/own_overloads.py +70 -0
  76. constricter/fix/index/own_types.cp312-win32.pyd +0 -0
  77. constricter/fix/index/own_types.py +330 -0
  78. constricter/fix/index/params.cp312-win32.pyd +0 -0
  79. constricter/fix/index/params.py +81 -0
  80. constricter/fix/index/plain.cp312-win32.pyd +0 -0
  81. constricter/fix/index/plain.py +221 -0
  82. constricter/fix/index/project.cp312-win32.pyd +0 -0
  83. constricter/fix/index/project.py +797 -0
  84. constricter/fix/index/sides.cp312-win32.pyd +0 -0
  85. constricter/fix/index/sides.py +160 -0
  86. constricter/fix/index/strings.cp312-win32.pyd +0 -0
  87. constricter/fix/index/strings.py +46 -0
  88. constricter/fix/index/stubs/__init__.cp312-win32.pyd +0 -0
  89. constricter/fix/index/stubs/__init__.py +2 -0
  90. constricter/fix/index/stubs/atoms.cp312-win32.pyd +0 -0
  91. constricter/fix/index/stubs/atoms.py +425 -0
  92. constricter/fix/index/stubs/chained.cp312-win32.pyd +0 -0
  93. constricter/fix/index/stubs/chained.py +69 -0
  94. constricter/fix/index/stubs/classnames.cp312-win32.pyd +0 -0
  95. constricter/fix/index/stubs/classnames.py +124 -0
  96. constricter/fix/index/stubs/declared.cp312-win32.pyd +0 -0
  97. constricter/fix/index/stubs/declared.py +508 -0
  98. constricter/fix/index/stubs/installed.cp312-win32.pyd +0 -0
  99. constricter/fix/index/stubs/installed.py +425 -0
  100. constricter/fix/index/stubs/names.cp312-win32.pyd +0 -0
  101. constricter/fix/index/stubs/names.py +200 -0
  102. constricter/fix/index/stubs/read.cp312-win32.pyd +0 -0
  103. constricter/fix/index/stubs/read.py +70 -0
  104. constricter/fix/index/stubs/signatures.cp312-win32.pyd +0 -0
  105. constricter/fix/index/stubs/signatures.py +656 -0
  106. constricter/fix/index/stubs/stubbed.cp312-win32.pyd +0 -0
  107. constricter/fix/index/stubs/stubbed.py +208 -0
  108. constricter/fix/index/taken.cp312-win32.pyd +0 -0
  109. constricter/fix/index/taken.py +45 -0
  110. constricter/fix/index/tuples.cp312-win32.pyd +0 -0
  111. constricter/fix/index/tuples.py +112 -0
  112. constricter/fix/libraries/__init__.cp312-win32.pyd +0 -0
  113. constricter/fix/libraries/__init__.py +2 -0
  114. constricter/fix/libraries/library.cp312-win32.pyd +0 -0
  115. constricter/fix/libraries/library.py +420 -0
  116. constricter/fix/libraries/opened.cp312-win32.pyd +0 -0
  117. constricter/fix/libraries/opened.py +147 -0
  118. constricter/fix/libraries/overloads.cp312-win32.pyd +0 -0
  119. constricter/fix/libraries/overloads.py +960 -0
  120. constricter/fix/libraries/receivers.cp312-win32.pyd +0 -0
  121. constricter/fix/libraries/receivers.py +274 -0
  122. constricter/fix/libraries/stdlib.cp312-win32.pyd +0 -0
  123. constricter/fix/libraries/stdlib.py +752 -0
  124. constricter/fix/libraries/walks.cp312-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__.cp312-win32.pyd +0 -0
  148. constricter/fix/values/__init__.py +2 -0
  149. constricter/fix/values/aliased.cp312-win32.pyd +0 -0
  150. constricter/fix/values/aliased.py +195 -0
  151. constricter/fix/values/bodies.cp312-win32.pyd +0 -0
  152. constricter/fix/values/bodies.py +76 -0
  153. constricter/fix/values/callables.cp312-win32.pyd +0 -0
  154. constricter/fix/values/callables.py +323 -0
  155. constricter/fix/values/called.cp312-win32.pyd +0 -0
  156. constricter/fix/values/called.py +194 -0
  157. constricter/fix/values/classvars.cp312-win32.pyd +0 -0
  158. constricter/fix/values/classvars.py +629 -0
  159. constricter/fix/values/decided.cp312-win32.pyd +0 -0
  160. constricter/fix/values/decided.py +429 -0
  161. constricter/fix/values/displays.cp312-win32.pyd +0 -0
  162. constricter/fix/values/displays.py +101 -0
  163. constricter/fix/values/doubts.cp312-win32.pyd +0 -0
  164. constricter/fix/values/doubts.py +677 -0
  165. constricter/fix/values/entered.cp312-win32.pyd +0 -0
  166. constricter/fix/values/entered.py +332 -0
  167. constricter/fix/values/fills.cp312-win32.pyd +0 -0
  168. constricter/fix/values/fills.py +446 -0
  169. constricter/fix/values/guesses.cp312-win32.pyd +0 -0
  170. constricter/fix/values/guesses.py +376 -0
  171. constricter/fix/values/hinted.cp312-win32.pyd +0 -0
  172. constricter/fix/values/hinted.py +811 -0
  173. constricter/fix/values/inference.cp312-win32.pyd +0 -0
  174. constricter/fix/values/inference.py +698 -0
  175. constricter/fix/values/loops.cp312-win32.pyd +0 -0
  176. constricter/fix/values/loops.py +335 -0
  177. constricter/fix/values/members.cp312-win32.pyd +0 -0
  178. constricter/fix/values/members.py +578 -0
  179. constricter/fix/values/narrowed.cp312-win32.pyd +0 -0
  180. constricter/fix/values/narrowed.py +220 -0
  181. constricter/fix/values/operated.cp312-win32.pyd +0 -0
  182. constricter/fix/values/operated.py +202 -0
  183. constricter/fix/values/ordered.cp312-win32.pyd +0 -0
  184. constricter/fix/values/ordered.py +74 -0
  185. constricter/fix/values/returned.cp312-win32.pyd +0 -0
  186. constricter/fix/values/returned.py +479 -0
  187. constricter/fix/values/returns.cp312-win32.pyd +0 -0
  188. constricter/fix/values/returns.py +237 -0
  189. constricter/fix/values/shapes.cp312-win32.pyd +0 -0
  190. constricter/fix/values/shapes.py +654 -0
  191. constricter/fix/values/stored.cp312-win32.pyd +0 -0
  192. constricter/fix/values/stored.py +564 -0
  193. constricter/fix/values/targets.cp312-win32.pyd +0 -0
  194. constricter/fix/values/targets.py +419 -0
  195. constricter/fix/values/unchecked.cp312-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__.cp312-win32.pyd +0 -0
  208. constricter/rules/__init__.py +2 -0
  209. constricter/rules/annotations.cp312-win32.pyd +0 -0
  210. constricter/rules/annotations.py +296 -0
  211. constricter/rules/binding.cp312-win32.pyd +0 -0
  212. constricter/rules/binding.py +652 -0
  213. constricter/rules/calls.cp312-win32.pyd +0 -0
  214. constricter/rules/calls.py +460 -0
  215. constricter/rules/checker.cp312-win32.pyd +0 -0
  216. constricter/rules/checker.py +536 -0
  217. constricter/rules/declarations.cp312-win32.pyd +0 -0
  218. constricter/rules/declarations.py +730 -0
  219. constricter/rules/decorators.cp312-win32.pyd +0 -0
  220. constricter/rules/decorators.py +351 -0
  221. constricter/rules/flow.cp312-win32.pyd +0 -0
  222. constricter/rules/flow.py +502 -0
  223. constricter/rules/keys.cp312-win32.pyd +0 -0
  224. constricter/rules/keys.py +79 -0
  225. constricter/rules/late.cp312-win32.pyd +0 -0
  226. constricter/rules/late.py +969 -0
  227. constricter/rules/narrowing.cp312-win32.pyd +0 -0
  228. constricter/rules/narrowing.py +98 -0
  229. constricter/rules/parsed.cp312-win32.pyd +0 -0
  230. constricter/rules/parsed.py +136 -0
  231. constricter/rules/quoted.cp312-win32.pyd +0 -0
  232. constricter/rules/quoted.py +141 -0
  233. constricter/rules/rebinding.cp312-win32.pyd +0 -0
  234. constricter/rules/rebinding.py +169 -0
  235. constricter/rules/recorded.cp312-win32.pyd +0 -0
  236. constricter/rules/recorded.py +168 -0
  237. constricter/rules/redundant.cp312-win32.pyd +0 -0
  238. constricter/rules/redundant.py +160 -0
  239. constricter/rules/scope.cp312-win32.pyd +0 -0
  240. constricter/rules/scope.py +802 -0
  241. constricter/rules/scoped.cp312-win32.pyd +0 -0
  242. constricter/rules/scoped.py +232 -0
  243. constricter/rules/scoping.cp312-win32.pyd +0 -0
  244. constricter/rules/scoping.py +346 -0
  245. constricter/rules/settings.cp312-win32.pyd +0 -0
  246. constricter/rules/settings.py +189 -0
  247. constricter/rules/syntax.cp312-win32.pyd +0 -0
  248. constricter/rules/syntax.py +472 -0
  249. constricter/rules/tables.cp312-win32.pyd +0 -0
  250. constricter/rules/tables.py +169 -0
  251. constricter/rules/walked.cp312-win32.pyd +0 -0
  252. constricter/rules/walked.py +127 -0
  253. constricter/rules/widened.cp312-win32.pyd +0 -0
  254. constricter/rules/widened.py +322 -0
  255. constricter/trace.py +374 -0
  256. e45ce06b2fdd5c43f782__mypyc.cp312-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,208 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """Installed packages' functions whose arguments decide their type: their signatures, read for a call.
3
+
4
+ The functions and methods a file calls, and the classes it passes, found through its imports;
5
+ their stubs' overloads read by `constricter.fix.index.stubs.signatures` into the signatures the
6
+ standard library's tables hold (see `constricter.fix.libraries.overloads`), so a call is matched
7
+ the same way, and kept from one file to the next (`constricter.fix.index.stubs.read`).
8
+ """
9
+
10
+ from collections.abc import Mapping
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Final, NamedTuple, TypeAlias, cast
13
+
14
+ from constricter.fix.core.known import Guarded, Origin
15
+ from constricter.fix.core.signatures import Expansion, ReadSignature
16
+ from constricter.fix.index import own_overloads, project
17
+ from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
18
+ from constricter.fix.index.stubs import chained, classnames
19
+ from constricter.fix.index.stubs.names import call_origin
20
+ from constricter.fix.index.stubs.read import MEMO, Expanded, Memo
21
+ from constricter.fix.index.stubs.signatures import Reader, read_all
22
+
23
+ if TYPE_CHECKING:
24
+ from constricter.fix.index.stubs.declared import Class, Declarations, Signature
25
+
26
+ _KIND: Final = "signatures"
27
+
28
+
29
+ _Read: TypeAlias = dict[str, tuple[ReadSignature, ...]] # functions' signatures, by a call's name
30
+
31
+
32
+ def overloaded(catalog: Index, path: Path, guarded: dict[str, Guarded] | None = None) -> _Read:
33
+ """Find the functions the file at `path` calls whose arguments decide their type.
34
+
35
+ An installed package's, and a checked file's defined with `@overload` (see `own_overloads`):
36
+ `guarded` records the names their returns need imported for type checking.
37
+
38
+ Returns:
39
+ Each one's signatures (one variant, see `overloads.chosen`), by the call's name as written
40
+ (`np.empty`); none for a file `catalog` doesn't have.
41
+
42
+ """
43
+ modules: dict[str, Module] = catalog.modules
44
+ target: Module | None
45
+ if path.suffix != SUFFIX or (target := modules.get(module_name(path))) is None:
46
+ return {}
47
+ found: dict[str, tuple[ReadSignature, ...]] = {}
48
+ memo: dict[tuple[str, str], tuple[ReadSignature, ...]] = MEMO.of(modules).read
49
+ key: str
50
+ for key in sorted(target.called):
51
+ origin: Origin | None = call_origin(modules, target, key)
52
+ defined: tuple[Module, str] | None
53
+ if origin is None or (defined := project.definition(modules, origin, _KIND)) is None:
54
+ continue
55
+ if not defined[0].installed:
56
+ # Read each time: what a checked file's parameters take rests on the checked files' classes.
57
+ read: tuple[ReadSignature, ...] = read_signatures(modules, *defined)
58
+ found[key] = own_overloads.spelled(catalog, target, defined, read, guarded)
59
+ continue
60
+ where: tuple[str, str]
61
+ if (where := (defined[0].name, defined[1])) not in memo:
62
+ memo[where] = read_signatures(modules, *defined)
63
+ found[key] = memo[where]
64
+ return {key: read for key, read in found.items() if read}
65
+
66
+
67
+ def classes(catalog: Index, path: Path) -> frozenset[str]:
68
+ """Find the installed classes the file at `path` passes as arguments (`dtype=np.float64`).
69
+
70
+ A class, or an alias of one (`np.int32 = signedinteger[_32Bit]`), through any re-exports.
71
+
72
+ Returns:
73
+ Each as the file writes it; none for a file `catalog` doesn't have.
74
+
75
+ """
76
+ modules: dict[str, Module] = catalog.modules
77
+ target: Module | None
78
+ if path.suffix != SUFFIX or (target := modules.get(module_name(path))) is None:
79
+ return frozenset()
80
+ reader: Reader = Reader(modules)
81
+ return frozenset(name for name in target.passed if reader.is_class(target, name))
82
+
83
+
84
+ class Methods(NamedTuple):
85
+ """Installed classes' methods a file may call whose arguments or instance decide their type.
86
+
87
+ `signatures`: each one's, by the class as the file writes it and the method (`np.ndarray.astype`);
88
+ `parameters`: each such class's type parameters, in order, which a receiver's type binds;
89
+ `lineage`: each installed class the file names, as it writes it, with where it's defined and its
90
+ ancestors' (`numpy.float64`, `numpy.floating`, ...), `?` last where a base can't be followed: a
91
+ receiver's type is matched against a method's `self` by them. `aliases`: each public alias of an
92
+ installed generic class the file names (`npt.NDArray`), whose methods are its class's; the
93
+ classes its expansion names are in `lineage` by their paths (`numpy.dtype`).
94
+ """
95
+
96
+ signatures: dict[str, tuple[ReadSignature, ...]]
97
+ parameters: dict[str, tuple[str, ...]]
98
+ lineage: dict[str, tuple[str, ...]]
99
+ aliases: dict[str, Expansion]
100
+
101
+
102
+ def methods(catalog: Index, path: Path, guarded: Mapping[str, Guarded] | None = None) -> Methods:
103
+ """Find the methods the file at `path` calls that an installed class it names declares (see `Methods`).
104
+
105
+ Named through its imports, those under `if TYPE_CHECKING:` included, and those its fixes will add
106
+ there (`guarded`, see `project.Imported`): a return type spelled through one is a receiver's.
107
+
108
+ Returns:
109
+ Them; none for a file `catalog` doesn't have.
110
+
111
+ """
112
+ modules: dict[str, Module] = catalog.modules
113
+ found: Methods = Methods({}, {}, {}, {})
114
+ target: Module | None
115
+ if path.suffix != SUFFIX or (target := modules.get(module_name(path))) is None or not target.method_calls:
116
+ return found
117
+ reader: Reader = Reader(modules)
118
+ memo: Memo = MEMO.of(modules)
119
+ spelled: str
120
+ origin: Origin
121
+ names: dict[str, Origin] = {
122
+ **{name: found.origin for name, found in (guarded or {}).items()},
123
+ **target.guarded,
124
+ **target.names,
125
+ }
126
+ names.update(_packages(modules, names))
127
+ for spelled, origin in classnames.classes(modules, names, memo.packages):
128
+ _class_methods(found, reader, target, spelled, origin)
129
+ alias: Origin
130
+ for spelled, alias in classnames.classes(modules, names, memo.packages, aliases=True):
131
+ if alias not in memo.expansions:
132
+ memo.expansions[alias] = reader.expansion(alias)
133
+ expanded: Expanded | None
134
+ if (expanded := memo.expansions[alias]) is None:
135
+ continue
136
+ paths: tuple[tuple[str, Origin], ...]
137
+ origin, found.aliases[spelled], paths = expanded
138
+ _class_methods(found, reader, target, spelled, origin)
139
+ path_named: str
140
+ for path_named, origin in paths:
141
+ found.lineage[path_named] = _lineage(reader, origin)
142
+ return found
143
+
144
+
145
+ def _packages(modules: Mapping[str, Module], names: Mapping[str, Origin]) -> dict[str, Origin]:
146
+ """Find the installed packages a file takes names of without importing them (`from grids import make`).
147
+
148
+ Returns:
149
+ Each, by its name, where the file binds it to nothing: a fix may import one's alias, which
150
+ is then a receiver's type, named by its dotted path.
151
+
152
+ """
153
+ tops: set[str] = {origin[0].partition(".")[0] for origin in names.values()} - set(names)
154
+ return {top: (top, None) for top in tops if top in modules and modules[top].installed}
155
+
156
+
157
+ def _lineage(reader: Reader, origin: Origin) -> tuple[str, ...]:
158
+ """Find an installed class's lineage (see `Methods.lineage`), and the methods it has, once.
159
+
160
+ Returns:
161
+ It.
162
+
163
+ """
164
+ memo: Memo = MEMO.of(reader.modules)
165
+ if origin not in memo.lines:
166
+ memo.lines[origin] = tuple(dict.fromkeys(reader.lineage(origin, 0)))
167
+ memo.names[origin] = classnames.method_names(reader.modules, memo.lines[origin])
168
+ return memo.lines[origin]
169
+
170
+
171
+ def _class_methods(found: Methods, reader: Reader, target: Module, spelled: str, origin: Origin) -> None:
172
+ """Add an installed class's methods `target` calls, as it writes the class (`spelled`), to `found`."""
173
+ found.lineage[spelled] = _lineage(reader, origin)
174
+ memo: Memo = MEMO.of(reader.modules)
175
+ module: Module = reader.modules[origin[0]]
176
+ klass: Class = cast("Declarations", module.declared).classes[origin[1] or ""]
177
+ name: str
178
+ for name in sorted(memo.names[origin] & target.method_calls):
179
+ if (origin, name) not in memo.methods:
180
+ # Its lineage has the method: the class or a base declares it.
181
+ memo.methods[origin, name] = cast(
182
+ "tuple[ReadSignature, ...]",
183
+ reader.method(module, klass, name, 0),
184
+ )
185
+ found.signatures[f"{spelled}.{name}"] = memo.methods[origin, name]
186
+ found.parameters[spelled] = tuple(param for param, _ in klass.params)
187
+ attr: str
188
+ each: tuple[Signature, ...]
189
+ # What it reads of the method's return, off the call (see `chained`).
190
+ for attr, each in chained.attributes(module, klass, name, target.attributes).items():
191
+ found.signatures[f"{spelled}.{chained.key(name, attr)}"] = read_all(
192
+ reader,
193
+ module,
194
+ each,
195
+ frozenset(param for param, _ in klass.params),
196
+ dict(klass.params),
197
+ )
198
+
199
+
200
+ def read_signatures(modules: Mapping[str, Module], module: Module, name: str) -> tuple[ReadSignature, ...]:
201
+ """Read an installed function's signatures as the tables hold theirs.
202
+
203
+ Returns:
204
+ Them.
205
+
206
+ """
207
+ # Found by its signatures (`project.definition`): an installed module's, or a checked file's overloads.
208
+ return read_all(Reader(modules), module, own_overloads.written(module, name), frozenset(), {})
@@ -0,0 +1,45 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """Cross-module `--fix` for an empty container passed to another checked file's function.
3
+
4
+ Found through the calling file's imports and the re-exports between them (`add(names, "a")`,
5
+ `util.add(names, "a")`); a parameter the function declares a builtin container (`names: list[str]`)
6
+ types what's passed to it, where the file can write that type (see `project.portable`, and
7
+ `constricter.fix.values.fills`).
8
+ """
9
+
10
+ from pathlib import Path
11
+
12
+ from constricter.fix.core.known import Guarded, Takers
13
+ from constricter.fix.index import project, sides
14
+ from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
15
+
16
+
17
+ def calls(catalog: Index, path: Path, guarded: dict[str, Guarded]) -> dict[str, Takers]:
18
+ """Find the parameters the functions the file at `path` calls declare a builtin container.
19
+
20
+ `guarded`: the names its imported types need imported for type checking (see `Guarded`), which
21
+ these types' are added to.
22
+
23
+ Returns:
24
+ Each call's name as written (`add`, `util.add`), mapped to those parameters (see `Takers`),
25
+ each type as the file can write it; none for a file `catalog` doesn't have.
26
+
27
+ """
28
+ target: Module | None = catalog.modules.get(module_name(path)) if path.suffix == SUFFIX else None
29
+ found: dict[str, Takers] = {}
30
+ if target is None:
31
+ return found
32
+ spelled: str
33
+ defined: tuple[Module, str]
34
+ for spelled, defined in sides.called(catalog.modules, target, lambda module, name: name in module.takers):
35
+ declared: Takers = defined[0].takers[defined[1]]
36
+ kept: dict[str, str] = project.portable(
37
+ catalog.modules,
38
+ (target, defined),
39
+ spelled,
40
+ {name: taken[2] for name, taken in declared.items()},
41
+ guarded,
42
+ )
43
+ if kept:
44
+ found[spelled] = {name: (*declared[name][:2], text) for name, text in kept.items()}
45
+ return found
@@ -0,0 +1,112 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """The tuples a file's names stand for, other checked files': a named tuple's fields, an alias's tuple.
3
+
4
+ What an unpacking splits a value of one of them by (see `targets.named_tuples`), spelled as the file
5
+ can write it; and the unions its names for their aliases stand for (see `targets.aliased_unions`).
6
+ """
7
+
8
+ from collections.abc import Callable, Iterator, Mapping
9
+ from pathlib import Path
10
+
11
+ from constricter.fix.core.known import Guarded, Origin
12
+ from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
13
+ from constricter.fix.index.project import ALIAS, CLASS, definition, portable
14
+
15
+
16
+ def fields(catalog: Index, path: Path, guarded: dict[str, Guarded]) -> dict[str, str]:
17
+ """Find the tuples the file at `path` can name, by how it names them.
18
+
19
+ Those the types written for it name, which it needn't import itself (a property's type, a
20
+ function's return; see `Guarded`), those it imports, and those a module it imports has.
21
+
22
+ Returns:
23
+ Each name, and the tuple unpacking a value of it gives; nothing for a file `catalog` doesn't
24
+ have.
25
+
26
+ """
27
+ target: Module | None
28
+ if path.suffix != SUFFIX or (target := catalog.modules.get(module_name(path))) is None:
29
+ return {}
30
+ found: dict[str, str] = {}
31
+ key: str
32
+ defined: tuple[Module, str]
33
+ for key, defined in _named(catalog.modules, target, guarded, _tuples):
34
+ found.update(_fields(catalog.modules, target, key, defined, guarded))
35
+ return found
36
+
37
+
38
+ def unions(catalog: Index, path: Path, guarded: dict[str, Guarded]) -> dict[str, str]:
39
+ """Find the unions the file at `path` names by other files' type aliases, by how it names them.
40
+
41
+ The aliases `fields` would find, were they tuples'.
42
+
43
+ Returns:
44
+ Each name, and the union as its own module writes it: read for its members, never written.
45
+
46
+ """
47
+ target: Module | None
48
+ if path.suffix != SUFFIX or (target := catalog.modules.get(module_name(path))) is None:
49
+ return {}
50
+ return {
51
+ key: defined[0].unions[defined[1]]
52
+ for key, defined in _named(catalog.modules, target, guarded, _unions)
53
+ if defined[1] in defined[0].unions
54
+ }
55
+
56
+
57
+ def _tuples(module: Module) -> Mapping[str, str]:
58
+ return module.tuples
59
+
60
+
61
+ def _unions(module: Module) -> Mapping[str, str]:
62
+ return module.unions
63
+
64
+
65
+ def _named(
66
+ modules: Mapping[str, Module],
67
+ target: Module,
68
+ guarded: Mapping[str, Guarded],
69
+ held: Callable[[Module], Mapping[str, str]],
70
+ ) -> Iterator[tuple[str, tuple[Module, str]]]:
71
+ """Find the classes and aliases a file (`target`) names, and those of a module it imports that `held` has.
72
+
73
+ Yields:
74
+ Each one's name as the file spells it (`Pair`, or `shapes.Pair` after
75
+ `import pkg.shapes as shapes`), and where it's defined.
76
+
77
+ """
78
+ named: list[tuple[str, Origin]] = [
79
+ *((key, each.origin) for key, each in guarded.items()),
80
+ *target.guarded.items(), # under `if TYPE_CHECKING:`
81
+ *((key, origin) for key, origin in target.names.items() if origin[0] != target.name),
82
+ ]
83
+ key: str
84
+ origin: Origin
85
+ for key, origin in named:
86
+ defined: tuple[Module, str] | None = definition(modules, origin, CLASS) or definition(
87
+ modules,
88
+ origin,
89
+ ALIAS,
90
+ )
91
+ if defined is not None:
92
+ yield key, defined
93
+ module: Module | None
94
+ if origin[1] is None and (module := modules.get(origin[0])) is not None:
95
+ yield from ((f"{key}.{name}", (module, name)) for name in held(module))
96
+
97
+
98
+ def _fields(
99
+ modules: Mapping[str, Module],
100
+ target: Module,
101
+ key: str,
102
+ defined: tuple[Module, str],
103
+ guarded: dict[str, Guarded],
104
+ ) -> dict[str, str]:
105
+ """Spell one tuple type's tuple (see `targets.named_tuples`) for the file (`target`) naming it `key`.
106
+
107
+ Returns:
108
+ `key`, and the tuple; nothing for any other class or alias, or types the file can't write.
109
+
110
+ """
111
+ tuple_type: str | None = defined[0].tuples.get(defined[1])
112
+ return {} if tuple_type is None else portable(modules, (target, defined), key, {key: tuple_type}, guarded)
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """The standard library's and installed packages' types: the tables, and the signature a call matches."""