python-constricter 0.4.1__cp314-cp314-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__.cp314-win_amd64.pyd +0 -0
- constricter/fix/__init__.py +2 -0
- constricter/fix/core/__init__.cp314-win_amd64.pyd +0 -0
- constricter/fix/core/__init__.py +2 -0
- constricter/fix/core/asked.cp314-win_amd64.pyd +0 -0
- constricter/fix/core/asked.py +56 -0
- constricter/fix/core/fixes.cp314-win_amd64.pyd +0 -0
- constricter/fix/core/fixes.py +317 -0
- constricter/fix/core/imports.cp314-win_amd64.pyd +0 -0
- constricter/fix/core/imports.py +580 -0
- constricter/fix/core/inherited.cp314-win_amd64.pyd +0 -0
- constricter/fix/core/inherited.py +311 -0
- constricter/fix/core/known.cp314-win_amd64.pyd +0 -0
- constricter/fix/core/known.py +730 -0
- constricter/fix/core/signatures.cp314-win_amd64.pyd +0 -0
- constricter/fix/core/signatures.py +132 -0
- constricter/fix/index/__init__.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/__init__.py +2 -0
- constricter/fix/index/awaits.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/awaits.py +90 -0
- constricter/fix/index/beyond.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/beyond.py +149 -0
- constricter/fix/index/callers.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/callers.py +155 -0
- constricter/fix/index/crossed.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/crossed.py +327 -0
- constricter/fix/index/decorated.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/decorated.py +140 -0
- constricter/fix/index/fixtures.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/fixtures.py +176 -0
- constricter/fix/index/linked.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/linked.py +142 -0
- constricter/fix/index/loose.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/loose.py +103 -0
- constricter/fix/index/managed.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/managed.py +63 -0
- constricter/fix/index/modules.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/modules.py +741 -0
- constricter/fix/index/offers.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/offers.py +252 -0
- constricter/fix/index/order.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/order.py +155 -0
- constricter/fix/index/origins.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/origins.py +238 -0
- constricter/fix/index/own_methods.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/own_methods.py +91 -0
- constricter/fix/index/own_overloads.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/own_overloads.py +70 -0
- constricter/fix/index/own_types.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/own_types.py +330 -0
- constricter/fix/index/params.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/params.py +81 -0
- constricter/fix/index/plain.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/plain.py +221 -0
- constricter/fix/index/project.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/project.py +797 -0
- constricter/fix/index/sides.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/sides.py +160 -0
- constricter/fix/index/strings.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/strings.py +46 -0
- constricter/fix/index/stubs/__init__.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/__init__.py +2 -0
- constricter/fix/index/stubs/atoms.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/atoms.py +425 -0
- constricter/fix/index/stubs/chained.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/chained.py +69 -0
- constricter/fix/index/stubs/classnames.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/classnames.py +124 -0
- constricter/fix/index/stubs/declared.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/declared.py +508 -0
- constricter/fix/index/stubs/installed.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/installed.py +425 -0
- constricter/fix/index/stubs/names.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/names.py +200 -0
- constricter/fix/index/stubs/read.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/read.py +70 -0
- constricter/fix/index/stubs/signatures.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/signatures.py +656 -0
- constricter/fix/index/stubs/stubbed.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/stubs/stubbed.py +208 -0
- constricter/fix/index/taken.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/taken.py +45 -0
- constricter/fix/index/tuples.cp314-win_amd64.pyd +0 -0
- constricter/fix/index/tuples.py +112 -0
- constricter/fix/libraries/__init__.cp314-win_amd64.pyd +0 -0
- constricter/fix/libraries/__init__.py +2 -0
- constricter/fix/libraries/library.cp314-win_amd64.pyd +0 -0
- constricter/fix/libraries/library.py +420 -0
- constricter/fix/libraries/opened.cp314-win_amd64.pyd +0 -0
- constricter/fix/libraries/opened.py +147 -0
- constricter/fix/libraries/overloads.cp314-win_amd64.pyd +0 -0
- constricter/fix/libraries/overloads.py +960 -0
- constricter/fix/libraries/receivers.cp314-win_amd64.pyd +0 -0
- constricter/fix/libraries/receivers.py +274 -0
- constricter/fix/libraries/stdlib.cp314-win_amd64.pyd +0 -0
- constricter/fix/libraries/stdlib.py +752 -0
- constricter/fix/libraries/walks.cp314-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__.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/__init__.py +2 -0
- constricter/fix/values/aliased.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/aliased.py +195 -0
- constricter/fix/values/bodies.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/bodies.py +76 -0
- constricter/fix/values/callables.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/callables.py +323 -0
- constricter/fix/values/called.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/called.py +194 -0
- constricter/fix/values/classvars.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/classvars.py +629 -0
- constricter/fix/values/decided.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/decided.py +429 -0
- constricter/fix/values/displays.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/displays.py +101 -0
- constricter/fix/values/doubts.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/doubts.py +677 -0
- constricter/fix/values/entered.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/entered.py +332 -0
- constricter/fix/values/fills.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/fills.py +446 -0
- constricter/fix/values/guesses.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/guesses.py +376 -0
- constricter/fix/values/hinted.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/hinted.py +811 -0
- constricter/fix/values/inference.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/inference.py +698 -0
- constricter/fix/values/loops.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/loops.py +335 -0
- constricter/fix/values/members.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/members.py +578 -0
- constricter/fix/values/narrowed.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/narrowed.py +220 -0
- constricter/fix/values/operated.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/operated.py +202 -0
- constricter/fix/values/ordered.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/ordered.py +74 -0
- constricter/fix/values/returned.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/returned.py +479 -0
- constricter/fix/values/returns.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/returns.py +237 -0
- constricter/fix/values/shapes.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/shapes.py +654 -0
- constricter/fix/values/stored.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/stored.py +564 -0
- constricter/fix/values/targets.cp314-win_amd64.pyd +0 -0
- constricter/fix/values/targets.py +419 -0
- constricter/fix/values/unchecked.cp314-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__.cp314-win_amd64.pyd +0 -0
- constricter/rules/__init__.py +2 -0
- constricter/rules/annotations.cp314-win_amd64.pyd +0 -0
- constricter/rules/annotations.py +296 -0
- constricter/rules/binding.cp314-win_amd64.pyd +0 -0
- constricter/rules/binding.py +652 -0
- constricter/rules/calls.cp314-win_amd64.pyd +0 -0
- constricter/rules/calls.py +460 -0
- constricter/rules/checker.cp314-win_amd64.pyd +0 -0
- constricter/rules/checker.py +536 -0
- constricter/rules/declarations.cp314-win_amd64.pyd +0 -0
- constricter/rules/declarations.py +730 -0
- constricter/rules/decorators.cp314-win_amd64.pyd +0 -0
- constricter/rules/decorators.py +351 -0
- constricter/rules/flow.cp314-win_amd64.pyd +0 -0
- constricter/rules/flow.py +502 -0
- constricter/rules/keys.cp314-win_amd64.pyd +0 -0
- constricter/rules/keys.py +79 -0
- constricter/rules/late.cp314-win_amd64.pyd +0 -0
- constricter/rules/late.py +969 -0
- constricter/rules/narrowing.cp314-win_amd64.pyd +0 -0
- constricter/rules/narrowing.py +98 -0
- constricter/rules/parsed.cp314-win_amd64.pyd +0 -0
- constricter/rules/parsed.py +136 -0
- constricter/rules/quoted.cp314-win_amd64.pyd +0 -0
- constricter/rules/quoted.py +141 -0
- constricter/rules/rebinding.cp314-win_amd64.pyd +0 -0
- constricter/rules/rebinding.py +169 -0
- constricter/rules/recorded.cp314-win_amd64.pyd +0 -0
- constricter/rules/recorded.py +168 -0
- constricter/rules/redundant.cp314-win_amd64.pyd +0 -0
- constricter/rules/redundant.py +160 -0
- constricter/rules/scope.cp314-win_amd64.pyd +0 -0
- constricter/rules/scope.py +802 -0
- constricter/rules/scoped.cp314-win_amd64.pyd +0 -0
- constricter/rules/scoped.py +232 -0
- constricter/rules/scoping.cp314-win_amd64.pyd +0 -0
- constricter/rules/scoping.py +346 -0
- constricter/rules/settings.cp314-win_amd64.pyd +0 -0
- constricter/rules/settings.py +189 -0
- constricter/rules/syntax.cp314-win_amd64.pyd +0 -0
- constricter/rules/syntax.py +472 -0
- constricter/rules/tables.cp314-win_amd64.pyd +0 -0
- constricter/rules/tables.py +169 -0
- constricter/rules/walked.cp314-win_amd64.pyd +0 -0
- constricter/rules/walked.py +127 -0
- constricter/rules/widened.cp314-win_amd64.pyd +0 -0
- constricter/rules/widened.py +322 -0
- constricter/trace.py +374 -0
- e45ce06b2fdd5c43f782__mypyc.cp314-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
|
Binary file
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""An installed method's receiver, matched against the `self` its signature declares (`matches`).
|
|
3
|
+
|
|
4
|
+
For `constricter.fix.libraries.overloads`, which picks such a signature only where its receiver's
|
|
5
|
+
type fits: part by part, a type variable bound to what it stands against (left unbound where it's
|
|
6
|
+
bound two ways), a union by either member, a subscript by its arguments, and a class by where it's
|
|
7
|
+
defined and its ancestors. Each match is `YES`, `NO` or `MAYBE`.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import ast
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
from typing import Final, NamedTuple, cast
|
|
13
|
+
|
|
14
|
+
from constricter.fix.core.known import Known
|
|
15
|
+
from constricter.fix.core.signatures import (
|
|
16
|
+
ReadSignature,
|
|
17
|
+
)
|
|
18
|
+
from constricter.fix.libraries import stdlib
|
|
19
|
+
|
|
20
|
+
YES: Final = "y"
|
|
21
|
+
NO: Final = "n"
|
|
22
|
+
MAYBE: Final = "?"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
SELF_TYPE: Final = "Self" # a method's receiver's type, in `stdlib.Method.types`
|
|
26
|
+
_ANY_PATH: Final = "typing.Any" # a receiver pattern's anything
|
|
27
|
+
_TUPLE_PATH: Final = "builtins.tuple"
|
|
28
|
+
_BUILTINS_PATH: Final = "builtins."
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
UNFOLLOWED: Final = "?" # a lineage's base that can't be followed
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_CLASHING: Final = "?" # a type variable a receiver's type binds two ways
|
|
35
|
+
_REPEATED: Final = 2 # `tuple[int, ...]`'s arguments
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def both(first: str, second: str) -> str:
|
|
39
|
+
"""Combine two verdicts that must both hold.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
`NO` if either is, `MAYBE` if either is, else `YES`.
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
return NO if NO in {first, second} else MAYBE if MAYBE in {first, second} else YES
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Receiver(NamedTuple):
|
|
49
|
+
"""A method call's receiver, matched against an installed method's `self` (`ReadSignature.receiver`).
|
|
50
|
+
|
|
51
|
+
Its type (`Self` in `stdlib.Method.types`), as the module writes it, or as the class an alias
|
|
52
|
+
stands for (`Method.matched`); and what the module knows, whose `LibraryNames.lineage` a class
|
|
53
|
+
it names is compared with a pattern's by (where each is defined, and its ancestors). `own`, for
|
|
54
|
+
an alias's: what a type variable may be bound to, the parts the module wrote; the alias's own
|
|
55
|
+
are paths, which it can't.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
text: str | None
|
|
59
|
+
known: Known
|
|
60
|
+
own: frozenset[str] | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def receiver_of(method: stdlib.Method, known: Known) -> Receiver:
|
|
64
|
+
"""Describe a method call's receiver (see `Receiver`).
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
It.
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
written: str | None = method.types.get(SELF_TYPE)
|
|
71
|
+
if method.matched is None or written is None:
|
|
72
|
+
return Receiver(written, known)
|
|
73
|
+
return Receiver(
|
|
74
|
+
method.matched,
|
|
75
|
+
known,
|
|
76
|
+
frozenset(ast.unparse(node) for node in ast.walk(parsed(written)) if isinstance(node, ast.expr)),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def matches(receiver: Receiver, signature: ReadSignature, bound: dict[str, str]) -> str:
|
|
81
|
+
"""Match the receiver's type against `signature`'s `self`, binding its type variables into `bound`.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
`YES`, `NO`, or `MAYBE`.
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
found: dict[str, str] = {}
|
|
88
|
+
verdict: str = _unified(
|
|
89
|
+
receiver,
|
|
90
|
+
parsed(cast("str", signature.receiver)), # `_picked` asks only where there's one
|
|
91
|
+
parsed(cast("str", receiver.text)), # an installed method's receiver always has a type
|
|
92
|
+
dict(signature.bounds),
|
|
93
|
+
found,
|
|
94
|
+
)
|
|
95
|
+
# A variable bound two ways (by two parts, or by the arguments too) is left unbound: a type
|
|
96
|
+
# checker would widen it, or reject the call. So is one bound to what an alias wrote.
|
|
97
|
+
clashing: set[str] = {
|
|
98
|
+
name
|
|
99
|
+
for name, text in found.items()
|
|
100
|
+
if text == _CLASHING
|
|
101
|
+
or bound.get(name, text) != text
|
|
102
|
+
or (receiver.own is not None and text not in receiver.own)
|
|
103
|
+
}
|
|
104
|
+
name: str
|
|
105
|
+
for name in clashing:
|
|
106
|
+
_ = bound.pop(name, None)
|
|
107
|
+
del found[name]
|
|
108
|
+
bound.update(found)
|
|
109
|
+
return MAYBE if clashing and verdict == YES else verdict
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _unified(
|
|
113
|
+
receiver: Receiver,
|
|
114
|
+
pattern: ast.expr,
|
|
115
|
+
actual: ast.expr,
|
|
116
|
+
bounds: dict[str, str],
|
|
117
|
+
found: dict[str, str],
|
|
118
|
+
) -> str:
|
|
119
|
+
"""Match one part of the receiver's type against a pattern's.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
The verdict.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
left: ast.expr
|
|
126
|
+
right: ast.expr
|
|
127
|
+
name: str
|
|
128
|
+
match pattern:
|
|
129
|
+
case ast.BinOp(left=left, op=ast.BitOr(), right=right):
|
|
130
|
+
return _either(receiver, [left, right], actual, bounds, found)
|
|
131
|
+
case ast.Name(id=name): # a type variable: bound to it (see `matches` for two ways)
|
|
132
|
+
text: str = ast.unparse(actual)
|
|
133
|
+
if found.setdefault(name, text) != text:
|
|
134
|
+
found[name] = _CLASHING
|
|
135
|
+
return YES if name not in bounds else _either(receiver, [parsed(bounds[name])], actual, {}, {})
|
|
136
|
+
case ast.Subscript():
|
|
137
|
+
return _subscript(receiver, pattern, actual, bounds, found)
|
|
138
|
+
case _:
|
|
139
|
+
return _class(receiver, ast.unparse(pattern), actual)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _either(
|
|
143
|
+
receiver: Receiver,
|
|
144
|
+
members: Sequence[ast.expr],
|
|
145
|
+
actual: ast.expr,
|
|
146
|
+
bounds: dict[str, str],
|
|
147
|
+
found: dict[str, str],
|
|
148
|
+
) -> str:
|
|
149
|
+
"""Match against a union: `YES` if a member matches (its bindings kept), `NO` if none can.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
The verdict.
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
verdicts: set[str] = set()
|
|
156
|
+
member: ast.expr
|
|
157
|
+
for member in [part for each in members for part in _union(each)]:
|
|
158
|
+
bound: dict[str, str] = dict(found)
|
|
159
|
+
verdict: str
|
|
160
|
+
if (verdict := _unified(receiver, member, actual, bounds, bound)) == YES:
|
|
161
|
+
found.update(bound)
|
|
162
|
+
return YES
|
|
163
|
+
verdicts.add(verdict)
|
|
164
|
+
return NO if verdicts == {NO} else MAYBE
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _subscript(
|
|
168
|
+
receiver: Receiver,
|
|
169
|
+
pattern: ast.Subscript,
|
|
170
|
+
actual: ast.expr,
|
|
171
|
+
bounds: dict[str, str],
|
|
172
|
+
found: dict[str, str],
|
|
173
|
+
) -> str:
|
|
174
|
+
"""Match a generic class's pattern (`ndarray[tuple[Any, ...], dtype[ScalarT]]`) argument by argument.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
The verdict: `MAYBE` for a subclass, or the receiver's type without arguments.
|
|
178
|
+
|
|
179
|
+
"""
|
|
180
|
+
related: str = _class(
|
|
181
|
+
receiver,
|
|
182
|
+
ast.unparse(pattern.value),
|
|
183
|
+
actual.value if isinstance(actual, ast.Subscript) else actual,
|
|
184
|
+
)
|
|
185
|
+
if (
|
|
186
|
+
related != YES
|
|
187
|
+
or not isinstance(actual, ast.Subscript)
|
|
188
|
+
or ast.unparse(pattern.value) != _defined(receiver, actual.value)
|
|
189
|
+
):
|
|
190
|
+
return MAYBE if related == YES else related
|
|
191
|
+
wanted: list[ast.expr] = _arguments_of(pattern)
|
|
192
|
+
given: list[ast.expr] = _arguments_of(actual)
|
|
193
|
+
if _repeated(wanted) and _repeated(given): # `tuple[int, ...]` both: their elements
|
|
194
|
+
wanted, given = wanted[:1], given[:1]
|
|
195
|
+
elif _repeated(given):
|
|
196
|
+
return MAYBE # its length unknown
|
|
197
|
+
elif _repeated(wanted):
|
|
198
|
+
wanted = [wanted[0]] * len(given)
|
|
199
|
+
if len(wanted) != len(given):
|
|
200
|
+
return NO if ast.unparse(pattern.value) == _TUPLE_PATH else MAYBE
|
|
201
|
+
verdict: str = YES
|
|
202
|
+
part: ast.expr
|
|
203
|
+
other: ast.expr
|
|
204
|
+
for part, other in zip(wanted, given, strict=True):
|
|
205
|
+
verdict = both(verdict, _unified(receiver, part, other, bounds, found))
|
|
206
|
+
return verdict
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _repeated(args: Sequence[ast.expr]) -> bool:
|
|
210
|
+
return len(args) == _REPEATED and isinstance(args[1], ast.Constant) and args[1].value is Ellipsis
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _class(receiver: Receiver, path: str, actual: ast.expr) -> str:
|
|
214
|
+
"""Compare a pattern's class (`numpy.floating`) with the receiver's (`np.float64`), by its lineage.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
`YES` if it's that class or a subclass, `NO` if its lineage is known and doesn't have it.
|
|
218
|
+
|
|
219
|
+
"""
|
|
220
|
+
if path == _ANY_PATH:
|
|
221
|
+
return YES
|
|
222
|
+
lineage: tuple[str, ...] = _lineage(
|
|
223
|
+
receiver,
|
|
224
|
+
actual.value if isinstance(actual, ast.Subscript) else actual,
|
|
225
|
+
)
|
|
226
|
+
if path in lineage:
|
|
227
|
+
return YES
|
|
228
|
+
return MAYBE if not lineage or UNFOLLOWED in lineage else NO
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _defined(receiver: Receiver, actual: ast.expr) -> str:
|
|
232
|
+
lineage: tuple[str, ...] = _lineage(receiver, actual)
|
|
233
|
+
return lineage[0] if lineage else ""
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _lineage(receiver: Receiver, actual: ast.expr) -> tuple[str, ...]:
|
|
237
|
+
"""Find where a class the receiver's type names is defined, and its ancestors.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Them; none for anything else. A builtin's, just it and `object`.
|
|
241
|
+
|
|
242
|
+
"""
|
|
243
|
+
text: str = ast.unparse(actual)
|
|
244
|
+
if isinstance(actual, ast.Name) and receiver.known.is_builtin(text):
|
|
245
|
+
return (f"builtins.{text}", "builtins.object")
|
|
246
|
+
if text.startswith(_BUILTINS_PATH): # an alias's expansion's (`builtins.tuple`)
|
|
247
|
+
return (text, "builtins.object")
|
|
248
|
+
return tuple(receiver.known.names.lineage.get(text, ()))
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _union(pattern: ast.expr) -> list[ast.expr]:
|
|
252
|
+
"""Split a union pattern into its members.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Them.
|
|
256
|
+
|
|
257
|
+
"""
|
|
258
|
+
if isinstance(pattern, ast.BinOp) and isinstance(pattern.op, ast.BitOr):
|
|
259
|
+
return [*_union(pattern.left), *_union(pattern.right)]
|
|
260
|
+
return [pattern]
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _arguments_of(node: ast.Subscript) -> list[ast.expr]:
|
|
264
|
+
return list(node.slice.elts) if isinstance(node.slice, ast.Tuple) else [node.slice]
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def parsed(text: str) -> ast.expr:
|
|
268
|
+
"""Parse a type's text.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Its expression.
|
|
272
|
+
|
|
273
|
+
"""
|
|
274
|
+
return ast.parse(text, mode="eval").body
|
|
Binary file
|