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,142 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """What the index says of checked modules together: one type's several spellings, and what's checked first.
3
+
4
+ The ways a file spells one class or alias another module defines (`same`), the names it imports
5
+ that are values where they're defined (`values`), the modules whose
6
+ unannotated functions a module calls, checked before it (`needs`), those functions as the file
7
+ spells them (`untyped`), and the index with what a checked module's functions return
8
+ (`with_returned`).
9
+ """
10
+
11
+ from collections.abc import Mapping
12
+ from dataclasses import replace
13
+ from pathlib import Path
14
+
15
+ from constricter.fix.core.known import Guarded, Origin, Returns
16
+ from constricter.fix.index import project
17
+ from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
18
+
19
+
20
+ def same(catalog: Index, path: Path, guarded: Mapping[str, Guarded]) -> tuple[frozenset[str], ...]:
21
+ """Group the ways the file at `path` spells one class or alias another module defines.
22
+
23
+ `CoreSchema` and `core_schema.CoreSchema`, in a file importing the name and its module, the name
24
+ perhaps for type checking alone (its own import, or one `guarded` adds): one type, to value flow.
25
+
26
+ Returns:
27
+ Each group of two or more spellings.
28
+
29
+ """
30
+ target: Module | None
31
+ if path.suffix != SUFFIX or (target := catalog.modules.get(module_name(path))) is None:
32
+ return ()
33
+ # With what it imports for type checking alone: a module so imported spells its types too.
34
+ checking: dict[str, Origin] = {
35
+ **target.guarded,
36
+ **{name: found.origin for name, found in guarded.items()},
37
+ }
38
+ named: Module = replace(target, names={**checking, **target.names})
39
+ groups: dict[Origin, set[str]] = {}
40
+ kind: str
41
+ for kind in (project.CLASS, project.ALIAS):
42
+ name: str
43
+ origin: Origin
44
+ for name, origin in project.spellings(catalog, named, kind):
45
+ defined: tuple[Module, str] | None
46
+ if (defined := project.definition(catalog.modules, origin, kind)) is not None:
47
+ groups.setdefault((defined[0].name, defined[1]), set()).add(name)
48
+ return tuple(frozenset(group) for group in groups.values() if len(group) > 1)
49
+
50
+
51
+ def values(catalog: Index, path: Path) -> frozenset[str]:
52
+ """Find the names the file at `path` imports that another checked module binds by assignment.
53
+
54
+ `F`, by `F = duckdb.FunctionExpression` there: a value, whatever its name looks like, and a call
55
+ of it constructs nothing a type can be written for.
56
+
57
+ Returns:
58
+ Them, as the file binds them; none for a file `catalog` doesn't have.
59
+
60
+ """
61
+ target: Module | None
62
+ if path.suffix != SUFFIX or (target := catalog.modules.get(module_name(path))) is None:
63
+ return frozenset()
64
+ found: set[str] = set()
65
+ local: str
66
+ origin: Origin
67
+ for local, origin in target.names.items():
68
+ where: Origin = project.canonical_origin(catalog.modules, origin)
69
+ defining: Module | None = catalog.modules.get(where[0])
70
+ if defining is not None and where[1] in defining.assigned and where[0] != target.name:
71
+ found.add(local)
72
+ return frozenset(found)
73
+
74
+
75
+ def with_returned(catalog: Index, found: Mapping[str, Returns]) -> Index:
76
+ """Record what checked modules' unannotated functions return (`found`, by module name).
77
+
78
+ A module's names from an earlier check are kept: once `--fix` has written an import it added,
79
+ a later check no longer exports the name, and the index doesn't have the new import.
80
+
81
+ Returns:
82
+ The index, with them.
83
+
84
+ """
85
+ modules: dict[str, Module] = dict(catalog.modules)
86
+ name: str
87
+ returns: Returns
88
+ for name, returns in found.items():
89
+ if name in modules:
90
+ names: dict[str, Origin] = {**modules[name].returned.names, **returns.names}
91
+ modules[name] = replace(modules[name], returned=returns._replace(names=names))
92
+ return catalog._replace(modules=modules)
93
+
94
+
95
+ def needs(catalog: Index, module: Module) -> set[str]:
96
+ """Find the other modules whose unannotated functions `module` calls (see `_spelled`).
97
+
98
+ Returns:
99
+ Their names.
100
+
101
+ """
102
+ found: set[str] = set()
103
+ key: str
104
+ origin: Origin
105
+ for key, origin in project.spellings(catalog, module, project.UNANNOTATED):
106
+ defined: tuple[Module, str] | None
107
+ if (
108
+ key in module.called
109
+ and (defined := project.definition(catalog.modules, origin, project.UNANNOTATED)) is not None
110
+ ):
111
+ found.add(defined[0].name)
112
+ found.discard(module.name)
113
+ return found
114
+
115
+
116
+ def untyped(catalog: Index, path: Path) -> frozenset[str]:
117
+ """Find the other checked files' functions the file at `path` calls that declare no return.
118
+
119
+ A plain `def` (see `returned.unannotated`), no `# type:` comment declaring its return, and no
120
+ fixture: what a type checker reading no body takes each call of for anything.
121
+
122
+ Returns:
123
+ Each, as the file spells its call (`helper`, `u.helper`); none for a file `catalog` doesn't have.
124
+
125
+ """
126
+ target: Module | None
127
+ if path.suffix != SUFFIX or (target := catalog.modules.get(module_name(path))) is None:
128
+ return frozenset()
129
+ found: set[str] = set()
130
+ key: str
131
+ origin: Origin
132
+ for key, origin in project.spellings(catalog, target, project.UNANNOTATED):
133
+ defined: tuple[Module, str] | None
134
+ if (
135
+ key in target.called
136
+ and (defined := project.definition(catalog.modules, origin, project.UNANNOTATED)) is not None
137
+ and not defined[0].installed
138
+ and defined[1] not in defined[0].returns
139
+ and defined[1] not in defined[0].fixtures
140
+ ):
141
+ found.add(key)
142
+ return frozenset(found)
@@ -0,0 +1,103 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """What another checked file's unannotated methods return, for the files calling them.
3
+
4
+ A module's functions' `return`s type their calls in the files importing them (see
5
+ `project.returned`); its classes' methods' type `self.method()` under a base of another file's, and
6
+ a call on an instance of an imported class, the same way: as guesses, since a subclass may
7
+ override one. A file is checked after the modules whose classes it names that have such a method
8
+ it calls (`needs`).
9
+ """
10
+
11
+ from collections.abc import Iterator
12
+ from pathlib import Path
13
+
14
+ from constricter.fix.core.known import Guarded, Returns
15
+ from constricter.fix.core.signatures import AWAIT
16
+ from constricter.fix.index import crossed, project
17
+ from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
18
+
19
+
20
+ def _classes(catalog: Index, target: Module) -> Iterator[tuple[str, tuple[Module, str]]]:
21
+ """Find the classes `target` names whose module has a method it calls that only `return`s could type.
22
+
23
+ Yields:
24
+ Each, as it spells it, and the module defining it and its name there.
25
+
26
+ """
27
+ called: frozenset[str] = _called(target) # once: a file may name thousands of classes
28
+ key: str
29
+ defined: tuple[Module, str]
30
+ for key, defined in crossed.spelled_classes(catalog, target, (target.names, target.attributes), set()):
31
+ if defined[0].name != target.name and not defined[0].loose.isdisjoint(called):
32
+ yield key, defined
33
+
34
+
35
+ def _called(target: Module) -> frozenset[str]:
36
+ """Name the methods `target` calls on anything, and (after `AWAIT`) those it awaits a call of.
37
+
38
+ Returns:
39
+ Them, as `Module.loose` names a class's.
40
+
41
+ """
42
+ return target.method_calls | {name for name in target.attributes if name.startswith(AWAIT)}
43
+
44
+
45
+ def needs(catalog: Index, module: Module) -> set[str]:
46
+ """Find the other modules whose classes' unannotated methods `module` may call.
47
+
48
+ Returns:
49
+ Their names.
50
+
51
+ """
52
+ return {defined[0].name for _, defined in _classes(catalog, module)}
53
+
54
+
55
+ def returned(
56
+ catalog: Index,
57
+ path: Path,
58
+ guarded: dict[str, Guarded] | None = None,
59
+ functions: Returns | None = None,
60
+ ) -> Returns:
61
+ """Return, for the file at `path`, what `project.returned` does, and the methods it calls too.
62
+
63
+ Those of the classes it names, in modules already checked, by each class as it spells it; a
64
+ guessed one's origins as `C.m`. `functions`: `project.returned`'s, if it's worked out already.
65
+
66
+ Returns:
67
+ Them; `project.returned`'s alone for a file `catalog` doesn't have.
68
+
69
+ """
70
+ found: Returns = functions or project.returned(catalog, path, guarded)
71
+ target: Module | None
72
+ if (target := catalog.modules.get(module_name(path)) if path.suffix == SUFFIX else None) is None:
73
+ return found
74
+ methods: dict[str, dict[str, str]] = {}
75
+ guesses: dict[str, frozenset[str]] = dict(found.guesses)
76
+ called: frozenset[str] = _called(target)
77
+ key: str
78
+ defined: tuple[Module, str]
79
+ for key, defined in _classes(catalog, target):
80
+ wanted: dict[str, str] = {
81
+ name: each
82
+ for name, each in defined[0].returned.methods.get(defined[1], {}).items()
83
+ if name in called
84
+ }
85
+ kept: dict[str, str] = (
86
+ project.portable(
87
+ catalog.modules,
88
+ (target, defined),
89
+ key,
90
+ wanted,
91
+ {} if guarded is None else guarded,
92
+ )
93
+ if wanted
94
+ else {}
95
+ )
96
+ if kept:
97
+ methods[key] = kept
98
+ name: str
99
+ for name in kept:
100
+ rests: frozenset[str] | None
101
+ if (rests := defined[0].returned.guesses.get(f"{defined[1]}.{name}")) is not None:
102
+ guesses[f"{key}.{name}"] = rests
103
+ return found._replace(guesses=guesses, methods=methods) if methods else found
@@ -0,0 +1,63 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """Cross-module `--fix` for `with` on a call to another checked file's `@contextmanager` function.
3
+
4
+ Found through the calling file's imports and the re-exports between them (`with temp_dir() as d`,
5
+ `with helper.temp_dir() as d`); what it declares it yields types the target where the file can
6
+ write it (see `project.portable`), or else what its `yield`s give, once its file is checked (see
7
+ `constricter.fix.values.entered`).
8
+ """
9
+
10
+ from pathlib import Path
11
+
12
+ from constricter.fix.core.known import Guarded, Passed
13
+ from constricter.fix.index import project, sides
14
+ from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
15
+ from constricter.fix.values.entered import ENTERED
16
+
17
+
18
+ def calls(catalog: Index, path: Path, guarded: dict[str, Guarded]) -> dict[str, Passed]:
19
+ """Type what `with` gives of the calls the file at `path` makes to other checked files' managers.
20
+
21
+ `guarded`: the names its imported types need imported for type checking (see `Guarded`), which
22
+ these types' are added to.
23
+
24
+ Returns:
25
+ Each call's name as written (`temp_dir`, `helper.temp_dir`), mapped to its type and what that
26
+ rests on if it's a guess; none for a file `catalog` doesn't have.
27
+
28
+ """
29
+ found: dict[str, Passed] = {}
30
+ target: Module | None
31
+ if (target := catalog.modules.get(module_name(path)) if path.suffix == SUFFIX else None) is None:
32
+ return found
33
+ spelled: str
34
+ defined: tuple[Module, str]
35
+ for spelled, defined in sides.called(catalog.modules, target, _manages):
36
+ key: str = f"{ENTERED}{defined[1]}"
37
+ yielded: str | None = defined[0].managers[defined[1]] or defined[0].returned.calls.get(key)
38
+ kept: dict[str, str] = (
39
+ {}
40
+ if yielded is None
41
+ else project.portable(catalog.modules, (target, defined), spelled, {spelled: yielded}, guarded)
42
+ )
43
+ if kept:
44
+ found[spelled] = (kept[spelled], defined[0].returned.guesses.get(key, frozenset()))
45
+ return found
46
+
47
+
48
+ def needs(catalog: Index, module: Module) -> set[str]:
49
+ """Find the other modules whose managers `module` calls that only their `yield`s could type.
50
+
51
+ Returns:
52
+ Their names: checked before it, what they yield is known.
53
+
54
+ """
55
+ return {
56
+ defined[0].name
57
+ for _, defined in sides.called(catalog.modules, module, _manages)
58
+ if not defined[0].managers[defined[1]]
59
+ } - {module.name}
60
+
61
+
62
+ def _manages(module: Module, name: str) -> bool: # whether it defines a manager of the name
63
+ return name in module.managers