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,376 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """Which of `--fix`'s inferences are guesses (`--unsafe-fixes`), and what each rests on."""
3
+
4
+ import ast
5
+ from collections.abc import Iterator, Mapping
6
+ from typing import Final, cast
7
+
8
+ from constricter.fix.core.known import PARAM, Known, stood
9
+ from constricter.fix.libraries import stdlib, walks
10
+ from constricter.fix.libraries.library import installed_method, library_class
11
+ from constricter.fix.libraries.opened import opened, opened_path
12
+ from constricter.fix.values import called, decided, displays, shapes
13
+ from constricter.fix.values.inference import (
14
+ CONTAINER_BUILDERS,
15
+ RETURNED,
16
+ inference,
17
+ inferred,
18
+ joined,
19
+ scalar,
20
+ )
21
+ from constricter.fix.values.loops import COMPREHENSIONS, dict_view, targets_typed
22
+ from constricter.fix.values.members import (
23
+ assigned_owner,
24
+ awaited,
25
+ awaited_origins,
26
+ class_variable,
27
+ member,
28
+ partial_method,
29
+ present,
30
+ returned_method,
31
+ )
32
+ from constricter.fix.values.returns import BUILTIN_RETURNS
33
+ from constricter.fix.values.targets import DICT_VIEWS, ITERATORS
34
+ from constricter.offences import CONSTRUCTOR, MEMBER
35
+ from constricter.rules.annotations import dotted
36
+ from constricter.rules.walked import children
37
+
38
+ # Builtins whose call is certain (when the module doesn't rebind the name): see `_is_guess`.
39
+ _CERTAIN_BUILTINS: Final = frozenset(
40
+ BUILTIN_RETURNS.keys() | CONTAINER_BUILDERS.keys() | ITERATORS | {"type", "getattr"},
41
+ )
42
+
43
+
44
+ def guessed(
45
+ value: ast.expr,
46
+ known: Known,
47
+ guesses: frozenset[str],
48
+ declared: Mapping[str, str],
49
+ ) -> bool:
50
+ """Whether `inferred`'s annotation for `value` is a guess (`--unsafe-fixes`): it calls a class.
51
+
52
+ A capitalised call may construct a generic class (`Box(1)` is really `Box[int]`) or be a factory
53
+ function; literals, calls to a module function, a fixed-return builtin (`len`, `isinstance`,
54
+ ...) or a method `members.member` types on a value whose type is known, and another local this
55
+ scope already typed, are certain. Copying a local `inferred` itself only
56
+ guessed (`guesses`) is no more certain than the guess it copies, unless the value's type is the
57
+ same without it (see `_rests`).
58
+
59
+ Returns:
60
+ Whether any call in `value` is to something other than such a certain callee, or its type
61
+ rests on a name in it that copies such a guess.
62
+
63
+ """
64
+ return guessing(value, known, guesses, {}, declared)[0]
65
+
66
+
67
+ def _deciding(value: ast.AST, known: Known, declared: Mapping[str, str]) -> Iterator[ast.AST]:
68
+ """Walk what decides `value`'s type: all of it, but not the arguments of a call they can't change.
69
+
70
+ `open(path, "rb")` is a file object by its mode, `logging.getLogger(name)` a `Logger`, whatever
71
+ `path` or `name` are: a guess there doesn't make the call's type one; nor in `x.kind is None`, a
72
+ `bool` whatever `x.kind` is, or an f-string. Nor does it in a call to a
73
+ fixed-return builtin (`len(Box())`), a function declaring its return, or a method a certain
74
+ source types (`"{}".format(Box())`, `self.items.get(key())`), though its receiver still counts.
75
+
76
+ Yields:
77
+ Each node.
78
+
79
+ """
80
+ # A stack, not a recursion: a nested generator passes each node up through every level above it.
81
+ waiting: list[ast.AST | None] = [None, value] # `None` at its bottom ends it
82
+ node: ast.AST
83
+ for node in iter(waiting.pop, None):
84
+ yield node
85
+ if isinstance(node, ast.expr) and scalar(node) is not None:
86
+ continue
87
+ if isinstance(node, ast.Call) and (opened(node, known) or library_class(node, known)):
88
+ continue
89
+ read: ast.Attribute | None = shapes.named_read(node, known) if isinstance(node, ast.Call) else None
90
+ if isinstance(node, ast.Call) and fixed_by_callee(node, known, declared):
91
+ waiting.append(node.func)
92
+ elif read is not None:
93
+ # Typed as the read it makes, `obj.name`: an attribute nothing declares is a guess there too.
94
+ waiting.extend(reversed([read, *cast("ast.Call", node).args[2:]]))
95
+ else:
96
+ waiting.extend(reversed(children(node)))
97
+
98
+
99
+ def fixed_by_callee(call: ast.Call, known: Known, declared: Mapping[str, str]) -> bool:
100
+ """Check whether `call`'s type is its callee's alone, whatever its arguments are.
101
+
102
+ Returns:
103
+ Whether it is: a fixed-return builtin the module doesn't rebind, a function declaring its
104
+ return (`Known.calls`), or a method a certain source types (see `certain_method`).
105
+
106
+ """
107
+ name: str
108
+ func: ast.expr
109
+ match call:
110
+ case ast.Call(func=ast.Name(id=name)) if name in BUILTIN_RETURNS and known.is_builtin(name):
111
+ return True
112
+ case ast.Call(func=ast.Name() | ast.Attribute() as func) if (
113
+ dotted(func) in known.calls or dotted(func) in known.indirect.partial.calls
114
+ ):
115
+ return True
116
+ case _:
117
+ return certain_method(call, known, declared)
118
+
119
+
120
+ def guessing(
121
+ value: ast.expr,
122
+ known: Known,
123
+ guesses: frozenset[str],
124
+ origins: Mapping[str, frozenset[str]],
125
+ declared: Mapping[str, str],
126
+ ) -> tuple[bool, frozenset[str]]:
127
+ """Work out, in one pass, whether `value`'s type is a guess (see `guessed`), and what it rests on.
128
+
129
+ `origins` holds what each guessed local (of `guesses`) rests on.
130
+
131
+ Returns:
132
+ Whether it's a guess; and if it is, its guessing mechanisms (`FIX_KINDS`): `returned` for a
133
+ method typed only by its `return`s, `constructor` for any other guessed call, and each guessed
134
+ local's own.
135
+
136
+ """
137
+ # One walk, for both: the comprehensions whose targets the rest may use, then each node.
138
+ walked: list[ast.AST] = list(_deciding(stood(value, declared), known, declared))
139
+ inside: Mapping[str, str] = targets_typed(
140
+ [node for node in walked if isinstance(node, COMPREHENSIONS)],
141
+ known,
142
+ declared,
143
+ inference,
144
+ )
145
+ unsafe: bool = False
146
+ named: bool = True # only names are guesses, so far
147
+ found: set[str] = set()
148
+ node: ast.AST
149
+ for node in walked:
150
+ if joined(node, known, declared):
151
+ unsafe, named = True, False
152
+ found.add(displays.JOINED)
153
+ if _is_guess(node, known, guesses, inside):
154
+ unsafe = True
155
+ named = named and isinstance(node, ast.Name)
156
+ if isinstance(node, ast.Call):
157
+ found.update(_guessed_by(node, known, inside))
158
+ elif isinstance(node, ast.Attribute):
159
+ found.update(_assigned_origins(node, known, inside) or ())
160
+ found.update(origins.get(_read(node), ()))
161
+ if not unsafe or (named and not _rests(value, known, guesses, declared)):
162
+ return False, frozenset()
163
+ return True, frozenset(found)
164
+
165
+
166
+ def _rests(value: ast.expr, known: Known, guesses: frozenset[str], declared: Mapping[str, str]) -> bool:
167
+ """Check whether `value`'s type rests on the guessed names in it.
168
+
169
+ `os.path.join(root, "x")` is a `str` by its literal, whatever `root` is: a guessed `root` doesn't
170
+ make it a guess.
171
+
172
+ Returns:
173
+ Whether it has another type, or none, with those names' types unknown.
174
+
175
+ """
176
+ sure: dict[str, str] = {name: text for name, text in declared.items() if name not in guesses}
177
+ return inferred(value, known, sure) != inferred(value, known, declared)
178
+
179
+
180
+ def _receiving(receiver: ast.expr, method: str, known: Known, declared: Mapping[str, str]) -> str | None:
181
+ """Type the receiver of a call of `method`, as its member is looked up on it (see `members.present`).
182
+
183
+ Returns:
184
+ Its type, or `None` if it isn't known.
185
+
186
+ """
187
+ typed: str | None = inferred(receiver, known, declared)
188
+ return None if typed is None else present(typed, method)
189
+
190
+
191
+ def certain_method(call: ast.expr, known: Known, declared: Mapping[str, str]) -> bool:
192
+ """Check whether `call` is a method call a certain source types on its receiver's type.
193
+
194
+ A member `members.member` knows (or one declared to return a vague part, see `Partial`), or a
195
+ `dict`'s `.keys()`, `.values()` or `.items()`; its arguments can't change it (the receiver
196
+ itself may still be a guess).
197
+
198
+ Returns:
199
+ Whether it is.
200
+
201
+ """
202
+ receiver: ast.expr
203
+ method: str
204
+ match call:
205
+ case ast.Call(func=ast.Attribute(value=receiver, attr=method)):
206
+ typed: str | None = _receiving(receiver, method, known, declared)
207
+ return typed is not None and (
208
+ member(typed, method, call, known) is not None
209
+ or partial_method(typed, method, known) is not None
210
+ or (
211
+ method in DICT_VIEWS
212
+ and dict_view(receiver, method, known, declared, inference) is not None
213
+ )
214
+ )
215
+ case _:
216
+ return False
217
+
218
+
219
+ def _overloaded_method(call: ast.Call, known: Known, declared: Mapping[str, str]) -> bool:
220
+ """Check whether `call` calls a method its arguments type: the standard library's, or `dict.get`.
221
+
222
+ See `stdlib.overloaded_method` and `shapes.defaulted`. Its arguments are walked as its parts: a
223
+ guessed one makes it a guess.
224
+
225
+ Returns:
226
+ Whether it does.
227
+
228
+ """
229
+ receiver: ast.expr
230
+ method: str
231
+ match call:
232
+ case ast.Call(func=ast.Attribute(value=receiver, attr=method)):
233
+ typed: str | None = _receiving(receiver, method, known, declared)
234
+ base: str = known.class_side.lineage.definer(typed or "", method) or typed or ""
235
+ return typed is not None and (
236
+ stdlib.overloaded_method(typed, method, known) is not None
237
+ or stdlib.overloaded_method(base, method, known) is not None # a library base's
238
+ or installed_method(typed, method, known) is not None
239
+ or installed_method(base, method, known) is not None
240
+ or shapes.defaulted(typed, call, lambda arg: inference(arg, known, declared), known)
241
+ is not None
242
+ or opened_path(typed, call, known) is not None
243
+ )
244
+ case _:
245
+ return False
246
+
247
+
248
+ def _guessed_by(call: ast.Call, known: Known, declared: Mapping[str, str]) -> frozenset[str]:
249
+ """Name what makes one guessed call a guess.
250
+
251
+ Returns:
252
+ A method typed by its `return`s: `returned`, and what those rest on; a function whose
253
+ `return`s are guesses: what they rest on; anything else: `constructor`.
254
+
255
+ """
256
+ func: ast.expr
257
+ callee: str | None
258
+ receiver: ast.expr
259
+ method: str
260
+ typed: str | None
261
+ defined: tuple[str, str] | None
262
+ rests: frozenset[str] | None
263
+ match call:
264
+ case _ if rests := awaited_origins(call, known, lambda arg: inference(arg, known, declared)):
265
+ return rests
266
+ case ast.Call(func=ast.Name() | ast.Attribute() as func) if (
267
+ callee := dotted(func)
268
+ ) in known.returned.guesses:
269
+ return known.returned.guesses[callee or ""]
270
+ case ast.Call(func=ast.Attribute(value=receiver, attr=method)) if (
271
+ typed := _receiving(receiver, method, known, declared)
272
+ ) is not None and (defined := returned_method(typed, method, known)) is not None:
273
+ return frozenset({RETURNED}) | known.returned.guesses.get(f"{defined[0]}.{method}", frozenset())
274
+ case _:
275
+ return frozenset({CONSTRUCTOR})
276
+
277
+
278
+ def _assigned_origins(
279
+ node: ast.Attribute,
280
+ known: Known,
281
+ declared: Mapping[str, str],
282
+ ) -> frozenset[str] | None:
283
+ """Name what an attribute nothing declares rests on: its assignments, or its class's body.
284
+
285
+ One typed only by its assignments (see `Returned.attributes`), or a plain class's variable typed
286
+ by its value (see `ClassSide.variables`).
287
+
288
+ Returns:
289
+ `assigned`, and what its values' guesses rest on, or `member`; or `None` if it isn't one (a
290
+ certain source types it, or nothing does).
291
+
292
+ """
293
+ if not any(
294
+ node.attr in attributes
295
+ for attributes in (*known.returned.attributes.values(), *known.class_side.variables.values())
296
+ ):
297
+ return None # most attributes: no receiver to type
298
+ typed: str | None = inferred(node.value, known, declared)
299
+ if typed is None or member(typed, node.attr, None, known) is not None:
300
+ return None
301
+ owner: str | None
302
+ if (owner := assigned_owner(typed, node.attr, known)) is not None:
303
+ return known.returned.guesses[f"{owner}.{node.attr}"]
304
+ return None if class_variable(typed, node.attr, known) is None else frozenset({MEMBER})
305
+
306
+
307
+ def _is_guess(
308
+ node: ast.AST,
309
+ known: Known,
310
+ guesses: frozenset[str],
311
+ declared: Mapping[str, str],
312
+ ) -> bool:
313
+ name: str
314
+ func: ast.expr
315
+ match node:
316
+ case ast.Call(func=ast.Name(id=name)) if (
317
+ name in _CERTAIN_BUILTINS and known.is_builtin(name)
318
+ ) or name in known.indirect.awaits:
319
+ return False
320
+ # But an `async def`'s call typed by `return`s that are guesses, awaited: as they are.
321
+ case ast.Call(func=func) if not awaited_origins(
322
+ node,
323
+ known,
324
+ lambda arg: inference(arg, known, declared),
325
+ ) and (
326
+ _returned_certainly(func, known)
327
+ or decided.decides(func, known)
328
+ or dotted(func) in known.names.casts
329
+ or stdlib.resolved(func, known.names.stdlib) in stdlib.KNOWN
330
+ or dotted(func) in known.names.installed
331
+ or opened(node, known) is not None
332
+ or walks.walked(node, known, lambda arg: inference(arg, known, declared)) is not None
333
+ or certain_method(node, known, declared)
334
+ or _overloaded_method(node, known, declared)
335
+ or called.result(node, known, declared, lambda arg: inference(arg, known, declared)) is not None
336
+ or shapes.partly(node, known, lambda arg: inference(arg, known, declared)) is not None
337
+ # A standard-library coroutine's: awaited, what it declares.
338
+ or awaited(ast.Await(node), known, lambda arg: inference(arg, known, declared)) is not None
339
+ ):
340
+ return False
341
+ case ast.Call(func=func):
342
+ return dotted(func) not in known.calls
343
+ case ast.Name(id=name):
344
+ return name in guesses
345
+ case ast.Attribute():
346
+ return _read(node) in guesses or _assigned_origins(node, known, declared) is not None
347
+ case _:
348
+ return False
349
+
350
+
351
+ def _read(node: ast.AST) -> str:
352
+ """Name what a node reads that a scope types by name: a local, or a fixture's `request.param`.
353
+
354
+ Returns:
355
+ The key its type is under (see `Typed`); `""` for any other node.
356
+
357
+ """
358
+ name: str
359
+ match node:
360
+ case ast.Name(id=name):
361
+ return name
362
+ case ast.Attribute(value=ast.Name(id=name), attr="param"):
363
+ return f"{name}{PARAM}"
364
+ case _:
365
+ return ""
366
+
367
+
368
+ def _returned_certainly(func: ast.expr, known: Known) -> bool:
369
+ """Check whether `func` is an unannotated function (own or imported) certain of its `return`s' type.
370
+
371
+ Returns:
372
+ Whether it is.
373
+
374
+ """
375
+ callee: str | None = dotted(func)
376
+ return callee in known.returned.calls and callee not in known.returned.guesses