python-constricter 0.4.1__cp311-cp311-win_amd64.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.
- constricter/__init__.py +55 -0
- constricter/__main__.py +26 -0
- constricter/_version.py +4 -0
- constricter/cli/__init__.py +2 -0
- constricter/cli/baseline.py +107 -0
- constricter/cli/collecting.py +54 -0
- constricter/cli/command.py +547 -0
- constricter/cli/config.py +322 -0
- constricter/cli/explain.py +104 -0
- constricter/cli/lsp/__init__.py +2 -0
- constricter/cli/lsp/connection.py +475 -0
- constricter/cli/lsp/edits.py +188 -0
- constricter/cli/lsp/guard.py +133 -0
- constricter/cli/lsp/hints.py +391 -0
- constricter/cli/lsp/protocol.py +192 -0
- constricter/cli/lsp/remembered.py +81 -0
- constricter/cli/lsp/stored.py +200 -0
- constricter/cli/options.py +799 -0
- constricter/cli/paths.py +151 -0
- constricter/cli/reading.py +36 -0
- constricter/cli/report.py +397 -0
- constricter/cli/rounds.py +328 -0
- constricter/cli/runs.py +90 -0
- constricter/cli/schedule.py +463 -0
- constricter/cli/traced.py +401 -0
- constricter/cli/workers.py +286 -0
- constricter/empty.py +66 -0
- constricter/fix/__init__.cp311-win_amd64.pyd +0 -0
- constricter/fix/__init__.py +2 -0
- constricter/fix/core/__init__.cp311-win_amd64.pyd +0 -0
- constricter/fix/core/__init__.py +2 -0
- constricter/fix/core/asked.cp311-win_amd64.pyd +0 -0
- constricter/fix/core/asked.py +56 -0
- constricter/fix/core/fixes.cp311-win_amd64.pyd +0 -0
- constricter/fix/core/fixes.py +317 -0
- constricter/fix/core/imports.cp311-win_amd64.pyd +0 -0
- constricter/fix/core/imports.py +580 -0
- constricter/fix/core/inherited.cp311-win_amd64.pyd +0 -0
- constricter/fix/core/inherited.py +311 -0
- constricter/fix/core/known.cp311-win_amd64.pyd +0 -0
- constricter/fix/core/known.py +730 -0
- constricter/fix/core/signatures.cp311-win_amd64.pyd +0 -0
- constricter/fix/core/signatures.py +132 -0
- constricter/fix/index/__init__.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/__init__.py +2 -0
- constricter/fix/index/awaits.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/awaits.py +90 -0
- constricter/fix/index/beyond.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/beyond.py +149 -0
- constricter/fix/index/callers.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/callers.py +155 -0
- constricter/fix/index/crossed.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/crossed.py +327 -0
- constricter/fix/index/decorated.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/decorated.py +140 -0
- constricter/fix/index/fixtures.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/fixtures.py +176 -0
- constricter/fix/index/linked.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/linked.py +142 -0
- constricter/fix/index/loose.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/loose.py +103 -0
- constricter/fix/index/managed.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/managed.py +63 -0
- constricter/fix/index/modules.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/modules.py +741 -0
- constricter/fix/index/offers.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/offers.py +252 -0
- constricter/fix/index/order.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/order.py +155 -0
- constricter/fix/index/origins.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/origins.py +238 -0
- constricter/fix/index/own_methods.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/own_methods.py +91 -0
- constricter/fix/index/own_overloads.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/own_overloads.py +70 -0
- constricter/fix/index/own_types.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/own_types.py +330 -0
- constricter/fix/index/params.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/params.py +81 -0
- constricter/fix/index/plain.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/plain.py +221 -0
- constricter/fix/index/project.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/project.py +797 -0
- constricter/fix/index/sides.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/sides.py +160 -0
- constricter/fix/index/strings.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/strings.py +46 -0
- constricter/fix/index/stubs/__init__.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/__init__.py +2 -0
- constricter/fix/index/stubs/atoms.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/atoms.py +425 -0
- constricter/fix/index/stubs/chained.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/chained.py +69 -0
- constricter/fix/index/stubs/classnames.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/classnames.py +124 -0
- constricter/fix/index/stubs/declared.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/declared.py +508 -0
- constricter/fix/index/stubs/installed.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/installed.py +425 -0
- constricter/fix/index/stubs/names.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/names.py +200 -0
- constricter/fix/index/stubs/read.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/read.py +70 -0
- constricter/fix/index/stubs/signatures.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/signatures.py +656 -0
- constricter/fix/index/stubs/stubbed.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/stubbed.py +208 -0
- constricter/fix/index/taken.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/taken.py +45 -0
- constricter/fix/index/tuples.cp311-win_amd64.pyd +0 -0
- constricter/fix/index/tuples.py +112 -0
- constricter/fix/libraries/__init__.cp311-win_amd64.pyd +0 -0
- constricter/fix/libraries/__init__.py +2 -0
- constricter/fix/libraries/library.cp311-win_amd64.pyd +0 -0
- constricter/fix/libraries/library.py +420 -0
- constricter/fix/libraries/opened.cp311-win_amd64.pyd +0 -0
- constricter/fix/libraries/opened.py +147 -0
- constricter/fix/libraries/overloads.cp311-win_amd64.pyd +0 -0
- constricter/fix/libraries/overloads.py +960 -0
- constricter/fix/libraries/receivers.cp311-win_amd64.pyd +0 -0
- constricter/fix/libraries/receivers.py +274 -0
- constricter/fix/libraries/stdlib.cp311-win_amd64.pyd +0 -0
- constricter/fix/libraries/stdlib.py +752 -0
- constricter/fix/libraries/walks.cp311-win_amd64.pyd +0 -0
- constricter/fix/libraries/walks.py +49 -0
- constricter/fix/tables/aliases.json +147 -0
- constricter/fix/tables/attributes.json +700 -0
- constricter/fix/tables/awaited.json +69 -0
- constricter/fix/tables/awaited_overloads.json +14 -0
- constricter/fix/tables/bases.json +888 -0
- constricter/fix/tables/classes.json +1941 -0
- constricter/fix/tables/elements.json +156 -0
- constricter/fix/tables/functions.json +58 -0
- constricter/fix/tables/generic_attributes.json +254 -0
- constricter/fix/tables/method_overloads.json +461 -0
- constricter/fix/tables/method_signatures.json +954 -0
- constricter/fix/tables/methods.json +954 -0
- constricter/fix/tables/overloads.json +540 -0
- constricter/fix/tables/platform.json +37 -0
- constricter/fix/tables/returns.json +1362 -0
- constricter/fix/tables/scalar_members.json +16 -0
- constricter/fix/tables/scalars.json +2707 -0
- constricter/fix/tables/source.json +28 -0
- constricter/fix/tables/subscriptable.json +77 -0
- constricter/fix/tables/type_parameters.json +178 -0
- constricter/fix/tables/variables.json +2962 -0
- constricter/fix/values/__init__.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/__init__.py +2 -0
- constricter/fix/values/aliased.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/aliased.py +195 -0
- constricter/fix/values/bodies.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/bodies.py +76 -0
- constricter/fix/values/callables.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/callables.py +323 -0
- constricter/fix/values/called.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/called.py +194 -0
- constricter/fix/values/classvars.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/classvars.py +629 -0
- constricter/fix/values/decided.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/decided.py +429 -0
- constricter/fix/values/displays.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/displays.py +101 -0
- constricter/fix/values/doubts.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/doubts.py +677 -0
- constricter/fix/values/entered.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/entered.py +332 -0
- constricter/fix/values/fills.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/fills.py +446 -0
- constricter/fix/values/guesses.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/guesses.py +376 -0
- constricter/fix/values/hinted.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/hinted.py +811 -0
- constricter/fix/values/inference.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/inference.py +698 -0
- constricter/fix/values/loops.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/loops.py +335 -0
- constricter/fix/values/members.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/members.py +578 -0
- constricter/fix/values/narrowed.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/narrowed.py +220 -0
- constricter/fix/values/operated.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/operated.py +202 -0
- constricter/fix/values/ordered.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/ordered.py +74 -0
- constricter/fix/values/returned.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/returned.py +479 -0
- constricter/fix/values/returns.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/returns.py +237 -0
- constricter/fix/values/shapes.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/shapes.py +654 -0
- constricter/fix/values/stored.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/stored.py +564 -0
- constricter/fix/values/targets.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/targets.py +419 -0
- constricter/fix/values/unchecked.cp311-win_amd64.pyd +0 -0
- constricter/fix/values/unchecked.py +278 -0
- constricter/jsonc.py +50 -0
- constricter/noqa.py +47 -0
- constricter/notebook.py +115 -0
- constricter/offences.py +436 -0
- constricter/overrides.py +24 -0
- constricter/plugins/__init__.py +2 -0
- constricter/plugins/flake8.py +133 -0
- constricter/plugins/pylint.py +186 -0
- constricter/py.typed +0 -0
- constricter/recording.py +660 -0
- constricter/rules/__init__.cp311-win_amd64.pyd +0 -0
- constricter/rules/__init__.py +2 -0
- constricter/rules/annotations.cp311-win_amd64.pyd +0 -0
- constricter/rules/annotations.py +296 -0
- constricter/rules/binding.cp311-win_amd64.pyd +0 -0
- constricter/rules/binding.py +652 -0
- constricter/rules/calls.cp311-win_amd64.pyd +0 -0
- constricter/rules/calls.py +460 -0
- constricter/rules/checker.cp311-win_amd64.pyd +0 -0
- constricter/rules/checker.py +536 -0
- constricter/rules/declarations.cp311-win_amd64.pyd +0 -0
- constricter/rules/declarations.py +730 -0
- constricter/rules/decorators.cp311-win_amd64.pyd +0 -0
- constricter/rules/decorators.py +351 -0
- constricter/rules/flow.cp311-win_amd64.pyd +0 -0
- constricter/rules/flow.py +502 -0
- constricter/rules/keys.cp311-win_amd64.pyd +0 -0
- constricter/rules/keys.py +79 -0
- constricter/rules/late.cp311-win_amd64.pyd +0 -0
- constricter/rules/late.py +969 -0
- constricter/rules/narrowing.cp311-win_amd64.pyd +0 -0
- constricter/rules/narrowing.py +98 -0
- constricter/rules/parsed.cp311-win_amd64.pyd +0 -0
- constricter/rules/parsed.py +136 -0
- constricter/rules/quoted.cp311-win_amd64.pyd +0 -0
- constricter/rules/quoted.py +141 -0
- constricter/rules/rebinding.cp311-win_amd64.pyd +0 -0
- constricter/rules/rebinding.py +169 -0
- constricter/rules/recorded.cp311-win_amd64.pyd +0 -0
- constricter/rules/recorded.py +168 -0
- constricter/rules/redundant.cp311-win_amd64.pyd +0 -0
- constricter/rules/redundant.py +160 -0
- constricter/rules/scope.cp311-win_amd64.pyd +0 -0
- constricter/rules/scope.py +802 -0
- constricter/rules/scoped.cp311-win_amd64.pyd +0 -0
- constricter/rules/scoped.py +232 -0
- constricter/rules/scoping.cp311-win_amd64.pyd +0 -0
- constricter/rules/scoping.py +346 -0
- constricter/rules/settings.cp311-win_amd64.pyd +0 -0
- constricter/rules/settings.py +189 -0
- constricter/rules/syntax.cp311-win_amd64.pyd +0 -0
- constricter/rules/syntax.py +472 -0
- constricter/rules/tables.cp311-win_amd64.pyd +0 -0
- constricter/rules/tables.py +169 -0
- constricter/rules/walked.cp311-win_amd64.pyd +0 -0
- constricter/rules/walked.py +127 -0
- constricter/rules/widened.cp311-win_amd64.pyd +0 -0
- constricter/rules/widened.py +322 -0
- constricter/trace.py +374 -0
- e45ce06b2fdd5c43f782__mypyc.cp311-win_amd64.pyd +0 -0
- python_constricter-0.4.1.dist-info/METADATA +400 -0
- python_constricter-0.4.1.dist-info/RECORD +261 -0
- python_constricter-0.4.1.dist-info/WHEEL +4 -0
- python_constricter-0.4.1.dist-info/entry_points.txt +5 -0
- 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
|
+
)
|
|
Binary file
|