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,194 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """`--fix` for a call of a value whose type says what calling it gives.
3
+
4
+ A local, an attribute or anything else typed `Callable[..., R]` gives `R` (`handler(schema)`,
5
+ `self.handler(schema)`); one typed `type[C]` constructs a `C` (`cls()` in a classmethod,
6
+ `type(self)()`), as does `__new__` given one (`cls.__new__(cls)`, `object.__new__(cls)`); and an
7
+ instance of a class that declares `__call__`, what that returns. Not a callable that may be `None`
8
+ (a union's call is narrowed first), nor an `R` that is `None`.
9
+
10
+ And a call typed by its callee's name alone (`typed_call`): a checked file's function's declared
11
+ return, a fixed-return builtin's, or a class it's taken to construct, by its capitalised name
12
+ (`constructs`).
13
+ """
14
+
15
+ import ast
16
+ from collections.abc import Callable, Mapping
17
+ from typing import Final, TypeAlias
18
+
19
+ from constricter.fix.core.known import ImportPlan, Inference, Known
20
+ from constricter.fix.libraries import stdlib
21
+ from constricter.fix.values.members import member, parsed
22
+ from constricter.fix.values.returns import BUILTIN_RETURNS
23
+ from constricter.offences import CONSTRUCTOR
24
+ from constricter.rules.annotations import GENERICS, dotted, node_name
25
+
26
+ _Infer: TypeAlias = Callable[[ast.expr], Inference | None]
27
+ _CALLABLE: Final = "Callable"
28
+ _TYPE: Final = frozenset({"type", "Type"})
29
+ _CALL: Final = "__call__"
30
+ NEW: Final = "__new__" # given a class, makes one of it, whatever it's called on
31
+ _KIND: Final = frozenset({"call"}) # the fix kind of a declared return
32
+
33
+
34
+ def result(call: ast.Call, known: Known, declared: Mapping[str, str], infer: _Infer) -> Inference | None:
35
+ """Infer a call of a value whose type is known, by what that type's call gives.
36
+
37
+ `declared`: the scope's typed names; `infer` types any other callee (`self.handler`, `type(self)`).
38
+
39
+ Returns:
40
+ The inference, what typed a callee that isn't a plain local among its kinds; or `None` for a
41
+ callee of no known type, or a type whose call says nothing.
42
+
43
+ """
44
+ func: ast.expr = call.func
45
+ if isinstance(func, ast.Name):
46
+ return None if func.id not in declared else _returned(declared[func.id], func.id, call, known)
47
+ if isinstance(func, ast.Attribute) and func.attr == NEW and call.args:
48
+ return _made(call.args[0], known, infer)
49
+ callee: Inference | None = infer(func)
50
+ found: Inference | None = (
51
+ None if callee is None else _returned(callee.annotation, ast.unparse(func), call, known)
52
+ )
53
+ return None if callee is None or found is None else found._replace(kinds=found.kinds | callee.kinds)
54
+
55
+
56
+ def _made(given: ast.expr, known: Known, infer: _Infer) -> Inference | None:
57
+ """Infer `__new__(given, ...)`: an instance of the class `given` is.
58
+
59
+ Returns:
60
+ A `type[C]`'s `C`, or the class `given` names (one the module knows, and binds no other way);
61
+ `None` for anything else.
62
+
63
+ """
64
+ spelled: str = ast.unparse(given)
65
+ plan: ImportPlan | None = known.names.plan
66
+ if spelled in known.classes and plan is not None and spelled.partition(".")[0] not in plan.values:
67
+ return Inference(spelled, f"`{NEW}` of `{spelled}`", _KIND)
68
+ found: Inference | None = infer(given)
69
+ made: ast.expr | None = None if found is None else _constructed(parsed(found.annotation))
70
+ if found is None or made is None:
71
+ return None
72
+ reason: str = f"`{NEW}` of `{spelled}`, a `{found.annotation}`"
73
+ return Inference(ast.unparse(made), reason, found.kinds - {"copy"} | _KIND)
74
+
75
+
76
+ def _constructed(callee: ast.expr) -> ast.expr | None:
77
+ """Read the class a `type[C]` constructs.
78
+
79
+ Returns:
80
+ `C`, or `None` for any other type, or a `C` in quotes.
81
+
82
+ """
83
+ head: ast.expr
84
+ made: ast.expr
85
+ match callee:
86
+ case ast.Subscript(value=head, slice=made) if node_name(head) in _TYPE:
87
+ return None if isinstance(made, ast.Constant) else made
88
+ case _:
89
+ return None
90
+
91
+
92
+ def _returned(callee: str, spelled: str, call: ast.Call, known: Known) -> Inference | None:
93
+ """Infer what calling a value typed `callee` (written `spelled`) gives.
94
+
95
+ Returns:
96
+ A `Callable`'s return, a `type[C]`'s `C`, or its class's `__call__`'s return; `None` for a
97
+ `None` or quoted one, or any other type.
98
+
99
+ """
100
+ head: ast.expr
101
+ returns: ast.expr
102
+ made: ast.expr | None
103
+ match parsed(callee):
104
+ case ast.Subscript(value=head, slice=ast.Tuple(elts=[_, returns])) if node_name(head) == _CALLABLE:
105
+ if isinstance(returns, ast.Constant):
106
+ return None # `None`, or a name in quotes
107
+ return Inference(ast.unparse(returns), f"what `{spelled}`, a `{callee}`, returns", _KIND)
108
+ case ast.Subscript(value=head) if node_name(head) in _TYPE:
109
+ made = _constructed(parsed(callee))
110
+ reason: str = f"a call of `{spelled}`, a `{callee}`"
111
+ return None if made is None else Inference(ast.unparse(made), reason, _KIND)
112
+ case _:
113
+ return member(callee, _CALL, call, known)
114
+
115
+
116
+ # Calls that return a class or a special form, not an instance of what they're named.
117
+ _FACTORIES: Final = frozenset(
118
+ {
119
+ "Enum",
120
+ "Flag",
121
+ "IntEnum",
122
+ "IntFlag",
123
+ "NamedTuple",
124
+ "NewType",
125
+ "ParamSpec",
126
+ "StrEnum",
127
+ "TypeAliasType",
128
+ "TypeVar",
129
+ "TypeVarTuple",
130
+ "TypedDict",
131
+ },
132
+ )
133
+
134
+
135
+ def typed_call(value: ast.expr, known: Known) -> Inference | None:
136
+ """Infer a call by its callee's name alone (see the module docstring).
137
+
138
+ Returns:
139
+ The inference, or `None` for a callee typed no such way.
140
+
141
+ """
142
+ func: ast.expr
143
+ name: str
144
+ callee: str | None
145
+ match value:
146
+ case ast.Call(func=ast.Name() | ast.Attribute() as func) if (callee := dotted(func)) in known.calls:
147
+ return Inference(
148
+ known.calls[callee or ""],
149
+ f"`{callee}`'s declared return type",
150
+ frozenset({"call"}),
151
+ )
152
+ case ast.Call(func=ast.Name(id=name)) if name in BUILTIN_RETURNS and known.is_builtin(name):
153
+ return Inference(BUILTIN_RETURNS[name], f"`{name}`'s fixed return type", frozenset({"builtin"}))
154
+ case ast.Call(func=ast.Name() | ast.Attribute() as func) if (
155
+ (constructs(node_name(func), known.factories) or dotted(func) in known.classes)
156
+ and _type_expression(func, known)
157
+ and stdlib.resolved(func, known.names.stdlib) not in stdlib.FUNCTIONS
158
+ ):
159
+ return Inference(
160
+ ast.unparse(func),
161
+ f"a call to `{ast.unparse(func)}`, taken to construct one",
162
+ frozenset({CONSTRUCTOR}),
163
+ )
164
+ case _:
165
+ return None
166
+
167
+
168
+ def _type_expression(func: ast.Name | ast.Attribute, known: Known) -> bool:
169
+ """Check that a callee can be written as a type: a (dotted) name starting with a class or module.
170
+
171
+ Not a call's attribute (`_tables().Filters`), nor through a name the module binds as a value
172
+ somewhere (`Klass = ...`, `jinja2 = import_optional_dependency("jinja2")`, `self`): a type
173
+ checker rejects a variable in a type.
174
+
175
+ Returns:
176
+ Whether it can.
177
+
178
+ """
179
+ path: str | None = dotted(func)
180
+ plan: ImportPlan | None = known.names.plan
181
+ return path is not None and (plan is None or path.partition(".")[0] not in plan.values)
182
+
183
+
184
+ def constructs(name: str, known_factories: frozenset[str]) -> bool:
185
+ """Check whether a call to `name` constructs a class, by its capitalised name.
186
+
187
+ Returns:
188
+ Whether it does, and is worth annotating: `name` isn't a known factory, by import
189
+ (`known_factories`) or by its bare name (`_FACTORIES`, for one imported some other way).
190
+
191
+ """
192
+ return (
193
+ name[:1].isupper() and name not in known_factories and name not in _FACTORIES and name not in GENERICS
194
+ )