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.
- 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__.cp313-win32.pyd +0 -0
- constricter/fix/__init__.py +2 -0
- constricter/fix/core/__init__.cp313-win32.pyd +0 -0
- constricter/fix/core/__init__.py +2 -0
- constricter/fix/core/asked.cp313-win32.pyd +0 -0
- constricter/fix/core/asked.py +56 -0
- constricter/fix/core/fixes.cp313-win32.pyd +0 -0
- constricter/fix/core/fixes.py +317 -0
- constricter/fix/core/imports.cp313-win32.pyd +0 -0
- constricter/fix/core/imports.py +580 -0
- constricter/fix/core/inherited.cp313-win32.pyd +0 -0
- constricter/fix/core/inherited.py +311 -0
- constricter/fix/core/known.cp313-win32.pyd +0 -0
- constricter/fix/core/known.py +730 -0
- constricter/fix/core/signatures.cp313-win32.pyd +0 -0
- constricter/fix/core/signatures.py +132 -0
- constricter/fix/index/__init__.cp313-win32.pyd +0 -0
- constricter/fix/index/__init__.py +2 -0
- constricter/fix/index/awaits.cp313-win32.pyd +0 -0
- constricter/fix/index/awaits.py +90 -0
- constricter/fix/index/beyond.cp313-win32.pyd +0 -0
- constricter/fix/index/beyond.py +149 -0
- constricter/fix/index/callers.cp313-win32.pyd +0 -0
- constricter/fix/index/callers.py +155 -0
- constricter/fix/index/crossed.cp313-win32.pyd +0 -0
- constricter/fix/index/crossed.py +327 -0
- constricter/fix/index/decorated.cp313-win32.pyd +0 -0
- constricter/fix/index/decorated.py +140 -0
- constricter/fix/index/fixtures.cp313-win32.pyd +0 -0
- constricter/fix/index/fixtures.py +176 -0
- constricter/fix/index/linked.cp313-win32.pyd +0 -0
- constricter/fix/index/linked.py +142 -0
- constricter/fix/index/loose.cp313-win32.pyd +0 -0
- constricter/fix/index/loose.py +103 -0
- constricter/fix/index/managed.cp313-win32.pyd +0 -0
- constricter/fix/index/managed.py +63 -0
- constricter/fix/index/modules.cp313-win32.pyd +0 -0
- constricter/fix/index/modules.py +741 -0
- constricter/fix/index/offers.cp313-win32.pyd +0 -0
- constricter/fix/index/offers.py +252 -0
- constricter/fix/index/order.cp313-win32.pyd +0 -0
- constricter/fix/index/order.py +155 -0
- constricter/fix/index/origins.cp313-win32.pyd +0 -0
- constricter/fix/index/origins.py +238 -0
- constricter/fix/index/own_methods.cp313-win32.pyd +0 -0
- constricter/fix/index/own_methods.py +91 -0
- constricter/fix/index/own_overloads.cp313-win32.pyd +0 -0
- constricter/fix/index/own_overloads.py +70 -0
- constricter/fix/index/own_types.cp313-win32.pyd +0 -0
- constricter/fix/index/own_types.py +330 -0
- constricter/fix/index/params.cp313-win32.pyd +0 -0
- constricter/fix/index/params.py +81 -0
- constricter/fix/index/plain.cp313-win32.pyd +0 -0
- constricter/fix/index/plain.py +221 -0
- constricter/fix/index/project.cp313-win32.pyd +0 -0
- constricter/fix/index/project.py +797 -0
- constricter/fix/index/sides.cp313-win32.pyd +0 -0
- constricter/fix/index/sides.py +160 -0
- constricter/fix/index/strings.cp313-win32.pyd +0 -0
- constricter/fix/index/strings.py +46 -0
- constricter/fix/index/stubs/__init__.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/__init__.py +2 -0
- constricter/fix/index/stubs/atoms.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/atoms.py +425 -0
- constricter/fix/index/stubs/chained.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/chained.py +69 -0
- constricter/fix/index/stubs/classnames.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/classnames.py +124 -0
- constricter/fix/index/stubs/declared.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/declared.py +508 -0
- constricter/fix/index/stubs/installed.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/installed.py +425 -0
- constricter/fix/index/stubs/names.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/names.py +200 -0
- constricter/fix/index/stubs/read.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/read.py +70 -0
- constricter/fix/index/stubs/signatures.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/signatures.py +656 -0
- constricter/fix/index/stubs/stubbed.cp313-win32.pyd +0 -0
- constricter/fix/index/stubs/stubbed.py +208 -0
- constricter/fix/index/taken.cp313-win32.pyd +0 -0
- constricter/fix/index/taken.py +45 -0
- constricter/fix/index/tuples.cp313-win32.pyd +0 -0
- constricter/fix/index/tuples.py +112 -0
- constricter/fix/libraries/__init__.cp313-win32.pyd +0 -0
- constricter/fix/libraries/__init__.py +2 -0
- constricter/fix/libraries/library.cp313-win32.pyd +0 -0
- constricter/fix/libraries/library.py +420 -0
- constricter/fix/libraries/opened.cp313-win32.pyd +0 -0
- constricter/fix/libraries/opened.py +147 -0
- constricter/fix/libraries/overloads.cp313-win32.pyd +0 -0
- constricter/fix/libraries/overloads.py +960 -0
- constricter/fix/libraries/receivers.cp313-win32.pyd +0 -0
- constricter/fix/libraries/receivers.py +274 -0
- constricter/fix/libraries/stdlib.cp313-win32.pyd +0 -0
- constricter/fix/libraries/stdlib.py +752 -0
- constricter/fix/libraries/walks.cp313-win32.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__.cp313-win32.pyd +0 -0
- constricter/fix/values/__init__.py +2 -0
- constricter/fix/values/aliased.cp313-win32.pyd +0 -0
- constricter/fix/values/aliased.py +195 -0
- constricter/fix/values/bodies.cp313-win32.pyd +0 -0
- constricter/fix/values/bodies.py +76 -0
- constricter/fix/values/callables.cp313-win32.pyd +0 -0
- constricter/fix/values/callables.py +323 -0
- constricter/fix/values/called.cp313-win32.pyd +0 -0
- constricter/fix/values/called.py +194 -0
- constricter/fix/values/classvars.cp313-win32.pyd +0 -0
- constricter/fix/values/classvars.py +629 -0
- constricter/fix/values/decided.cp313-win32.pyd +0 -0
- constricter/fix/values/decided.py +429 -0
- constricter/fix/values/displays.cp313-win32.pyd +0 -0
- constricter/fix/values/displays.py +101 -0
- constricter/fix/values/doubts.cp313-win32.pyd +0 -0
- constricter/fix/values/doubts.py +677 -0
- constricter/fix/values/entered.cp313-win32.pyd +0 -0
- constricter/fix/values/entered.py +332 -0
- constricter/fix/values/fills.cp313-win32.pyd +0 -0
- constricter/fix/values/fills.py +446 -0
- constricter/fix/values/guesses.cp313-win32.pyd +0 -0
- constricter/fix/values/guesses.py +376 -0
- constricter/fix/values/hinted.cp313-win32.pyd +0 -0
- constricter/fix/values/hinted.py +811 -0
- constricter/fix/values/inference.cp313-win32.pyd +0 -0
- constricter/fix/values/inference.py +698 -0
- constricter/fix/values/loops.cp313-win32.pyd +0 -0
- constricter/fix/values/loops.py +335 -0
- constricter/fix/values/members.cp313-win32.pyd +0 -0
- constricter/fix/values/members.py +578 -0
- constricter/fix/values/narrowed.cp313-win32.pyd +0 -0
- constricter/fix/values/narrowed.py +220 -0
- constricter/fix/values/operated.cp313-win32.pyd +0 -0
- constricter/fix/values/operated.py +202 -0
- constricter/fix/values/ordered.cp313-win32.pyd +0 -0
- constricter/fix/values/ordered.py +74 -0
- constricter/fix/values/returned.cp313-win32.pyd +0 -0
- constricter/fix/values/returned.py +479 -0
- constricter/fix/values/returns.cp313-win32.pyd +0 -0
- constricter/fix/values/returns.py +237 -0
- constricter/fix/values/shapes.cp313-win32.pyd +0 -0
- constricter/fix/values/shapes.py +654 -0
- constricter/fix/values/stored.cp313-win32.pyd +0 -0
- constricter/fix/values/stored.py +564 -0
- constricter/fix/values/targets.cp313-win32.pyd +0 -0
- constricter/fix/values/targets.py +419 -0
- constricter/fix/values/unchecked.cp313-win32.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__.cp313-win32.pyd +0 -0
- constricter/rules/__init__.py +2 -0
- constricter/rules/annotations.cp313-win32.pyd +0 -0
- constricter/rules/annotations.py +296 -0
- constricter/rules/binding.cp313-win32.pyd +0 -0
- constricter/rules/binding.py +652 -0
- constricter/rules/calls.cp313-win32.pyd +0 -0
- constricter/rules/calls.py +460 -0
- constricter/rules/checker.cp313-win32.pyd +0 -0
- constricter/rules/checker.py +536 -0
- constricter/rules/declarations.cp313-win32.pyd +0 -0
- constricter/rules/declarations.py +730 -0
- constricter/rules/decorators.cp313-win32.pyd +0 -0
- constricter/rules/decorators.py +351 -0
- constricter/rules/flow.cp313-win32.pyd +0 -0
- constricter/rules/flow.py +502 -0
- constricter/rules/keys.cp313-win32.pyd +0 -0
- constricter/rules/keys.py +79 -0
- constricter/rules/late.cp313-win32.pyd +0 -0
- constricter/rules/late.py +969 -0
- constricter/rules/narrowing.cp313-win32.pyd +0 -0
- constricter/rules/narrowing.py +98 -0
- constricter/rules/parsed.cp313-win32.pyd +0 -0
- constricter/rules/parsed.py +136 -0
- constricter/rules/quoted.cp313-win32.pyd +0 -0
- constricter/rules/quoted.py +141 -0
- constricter/rules/rebinding.cp313-win32.pyd +0 -0
- constricter/rules/rebinding.py +169 -0
- constricter/rules/recorded.cp313-win32.pyd +0 -0
- constricter/rules/recorded.py +168 -0
- constricter/rules/redundant.cp313-win32.pyd +0 -0
- constricter/rules/redundant.py +160 -0
- constricter/rules/scope.cp313-win32.pyd +0 -0
- constricter/rules/scope.py +802 -0
- constricter/rules/scoped.cp313-win32.pyd +0 -0
- constricter/rules/scoped.py +232 -0
- constricter/rules/scoping.cp313-win32.pyd +0 -0
- constricter/rules/scoping.py +346 -0
- constricter/rules/settings.cp313-win32.pyd +0 -0
- constricter/rules/settings.py +189 -0
- constricter/rules/syntax.cp313-win32.pyd +0 -0
- constricter/rules/syntax.py +472 -0
- constricter/rules/tables.cp313-win32.pyd +0 -0
- constricter/rules/tables.py +169 -0
- constricter/rules/walked.cp313-win32.pyd +0 -0
- constricter/rules/walked.py +127 -0
- constricter/rules/widened.cp313-win32.pyd +0 -0
- constricter/rules/widened.py +322 -0
- constricter/trace.py +374 -0
- e45ce06b2fdd5c43f782__mypyc.cp313-win32.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,208 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Installed packages' functions whose arguments decide their type: their signatures, read for a call.
|
|
3
|
+
|
|
4
|
+
The functions and methods a file calls, and the classes it passes, found through its imports;
|
|
5
|
+
their stubs' overloads read by `constricter.fix.index.stubs.signatures` into the signatures the
|
|
6
|
+
standard library's tables hold (see `constricter.fix.libraries.overloads`), so a call is matched
|
|
7
|
+
the same way, and kept from one file to the next (`constricter.fix.index.stubs.read`).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from collections.abc import Mapping
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING, Final, NamedTuple, TypeAlias, cast
|
|
13
|
+
|
|
14
|
+
from constricter.fix.core.known import Guarded, Origin
|
|
15
|
+
from constricter.fix.core.signatures import Expansion, ReadSignature
|
|
16
|
+
from constricter.fix.index import own_overloads, project
|
|
17
|
+
from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
|
|
18
|
+
from constricter.fix.index.stubs import chained, classnames
|
|
19
|
+
from constricter.fix.index.stubs.names import call_origin
|
|
20
|
+
from constricter.fix.index.stubs.read import MEMO, Expanded, Memo
|
|
21
|
+
from constricter.fix.index.stubs.signatures import Reader, read_all
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from constricter.fix.index.stubs.declared import Class, Declarations, Signature
|
|
25
|
+
|
|
26
|
+
_KIND: Final = "signatures"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_Read: TypeAlias = dict[str, tuple[ReadSignature, ...]] # functions' signatures, by a call's name
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def overloaded(catalog: Index, path: Path, guarded: dict[str, Guarded] | None = None) -> _Read:
|
|
33
|
+
"""Find the functions the file at `path` calls whose arguments decide their type.
|
|
34
|
+
|
|
35
|
+
An installed package's, and a checked file's defined with `@overload` (see `own_overloads`):
|
|
36
|
+
`guarded` records the names their returns need imported for type checking.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Each one's signatures (one variant, see `overloads.chosen`), by the call's name as written
|
|
40
|
+
(`np.empty`); none for a file `catalog` doesn't have.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
modules: dict[str, Module] = catalog.modules
|
|
44
|
+
target: Module | None
|
|
45
|
+
if path.suffix != SUFFIX or (target := modules.get(module_name(path))) is None:
|
|
46
|
+
return {}
|
|
47
|
+
found: dict[str, tuple[ReadSignature, ...]] = {}
|
|
48
|
+
memo: dict[tuple[str, str], tuple[ReadSignature, ...]] = MEMO.of(modules).read
|
|
49
|
+
key: str
|
|
50
|
+
for key in sorted(target.called):
|
|
51
|
+
origin: Origin | None = call_origin(modules, target, key)
|
|
52
|
+
defined: tuple[Module, str] | None
|
|
53
|
+
if origin is None or (defined := project.definition(modules, origin, _KIND)) is None:
|
|
54
|
+
continue
|
|
55
|
+
if not defined[0].installed:
|
|
56
|
+
# Read each time: what a checked file's parameters take rests on the checked files' classes.
|
|
57
|
+
read: tuple[ReadSignature, ...] = read_signatures(modules, *defined)
|
|
58
|
+
found[key] = own_overloads.spelled(catalog, target, defined, read, guarded)
|
|
59
|
+
continue
|
|
60
|
+
where: tuple[str, str]
|
|
61
|
+
if (where := (defined[0].name, defined[1])) not in memo:
|
|
62
|
+
memo[where] = read_signatures(modules, *defined)
|
|
63
|
+
found[key] = memo[where]
|
|
64
|
+
return {key: read for key, read in found.items() if read}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def classes(catalog: Index, path: Path) -> frozenset[str]:
|
|
68
|
+
"""Find the installed classes the file at `path` passes as arguments (`dtype=np.float64`).
|
|
69
|
+
|
|
70
|
+
A class, or an alias of one (`np.int32 = signedinteger[_32Bit]`), through any re-exports.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Each as the file writes it; none for a file `catalog` doesn't have.
|
|
74
|
+
|
|
75
|
+
"""
|
|
76
|
+
modules: dict[str, Module] = catalog.modules
|
|
77
|
+
target: Module | None
|
|
78
|
+
if path.suffix != SUFFIX or (target := modules.get(module_name(path))) is None:
|
|
79
|
+
return frozenset()
|
|
80
|
+
reader: Reader = Reader(modules)
|
|
81
|
+
return frozenset(name for name in target.passed if reader.is_class(target, name))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Methods(NamedTuple):
|
|
85
|
+
"""Installed classes' methods a file may call whose arguments or instance decide their type.
|
|
86
|
+
|
|
87
|
+
`signatures`: each one's, by the class as the file writes it and the method (`np.ndarray.astype`);
|
|
88
|
+
`parameters`: each such class's type parameters, in order, which a receiver's type binds;
|
|
89
|
+
`lineage`: each installed class the file names, as it writes it, with where it's defined and its
|
|
90
|
+
ancestors' (`numpy.float64`, `numpy.floating`, ...), `?` last where a base can't be followed: a
|
|
91
|
+
receiver's type is matched against a method's `self` by them. `aliases`: each public alias of an
|
|
92
|
+
installed generic class the file names (`npt.NDArray`), whose methods are its class's; the
|
|
93
|
+
classes its expansion names are in `lineage` by their paths (`numpy.dtype`).
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
signatures: dict[str, tuple[ReadSignature, ...]]
|
|
97
|
+
parameters: dict[str, tuple[str, ...]]
|
|
98
|
+
lineage: dict[str, tuple[str, ...]]
|
|
99
|
+
aliases: dict[str, Expansion]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def methods(catalog: Index, path: Path, guarded: Mapping[str, Guarded] | None = None) -> Methods:
|
|
103
|
+
"""Find the methods the file at `path` calls that an installed class it names declares (see `Methods`).
|
|
104
|
+
|
|
105
|
+
Named through its imports, those under `if TYPE_CHECKING:` included, and those its fixes will add
|
|
106
|
+
there (`guarded`, see `project.Imported`): a return type spelled through one is a receiver's.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Them; none for a file `catalog` doesn't have.
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
modules: dict[str, Module] = catalog.modules
|
|
113
|
+
found: Methods = Methods({}, {}, {}, {})
|
|
114
|
+
target: Module | None
|
|
115
|
+
if path.suffix != SUFFIX or (target := modules.get(module_name(path))) is None or not target.method_calls:
|
|
116
|
+
return found
|
|
117
|
+
reader: Reader = Reader(modules)
|
|
118
|
+
memo: Memo = MEMO.of(modules)
|
|
119
|
+
spelled: str
|
|
120
|
+
origin: Origin
|
|
121
|
+
names: dict[str, Origin] = {
|
|
122
|
+
**{name: found.origin for name, found in (guarded or {}).items()},
|
|
123
|
+
**target.guarded,
|
|
124
|
+
**target.names,
|
|
125
|
+
}
|
|
126
|
+
names.update(_packages(modules, names))
|
|
127
|
+
for spelled, origin in classnames.classes(modules, names, memo.packages):
|
|
128
|
+
_class_methods(found, reader, target, spelled, origin)
|
|
129
|
+
alias: Origin
|
|
130
|
+
for spelled, alias in classnames.classes(modules, names, memo.packages, aliases=True):
|
|
131
|
+
if alias not in memo.expansions:
|
|
132
|
+
memo.expansions[alias] = reader.expansion(alias)
|
|
133
|
+
expanded: Expanded | None
|
|
134
|
+
if (expanded := memo.expansions[alias]) is None:
|
|
135
|
+
continue
|
|
136
|
+
paths: tuple[tuple[str, Origin], ...]
|
|
137
|
+
origin, found.aliases[spelled], paths = expanded
|
|
138
|
+
_class_methods(found, reader, target, spelled, origin)
|
|
139
|
+
path_named: str
|
|
140
|
+
for path_named, origin in paths:
|
|
141
|
+
found.lineage[path_named] = _lineage(reader, origin)
|
|
142
|
+
return found
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _packages(modules: Mapping[str, Module], names: Mapping[str, Origin]) -> dict[str, Origin]:
|
|
146
|
+
"""Find the installed packages a file takes names of without importing them (`from grids import make`).
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Each, by its name, where the file binds it to nothing: a fix may import one's alias, which
|
|
150
|
+
is then a receiver's type, named by its dotted path.
|
|
151
|
+
|
|
152
|
+
"""
|
|
153
|
+
tops: set[str] = {origin[0].partition(".")[0] for origin in names.values()} - set(names)
|
|
154
|
+
return {top: (top, None) for top in tops if top in modules and modules[top].installed}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _lineage(reader: Reader, origin: Origin) -> tuple[str, ...]:
|
|
158
|
+
"""Find an installed class's lineage (see `Methods.lineage`), and the methods it has, once.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
It.
|
|
162
|
+
|
|
163
|
+
"""
|
|
164
|
+
memo: Memo = MEMO.of(reader.modules)
|
|
165
|
+
if origin not in memo.lines:
|
|
166
|
+
memo.lines[origin] = tuple(dict.fromkeys(reader.lineage(origin, 0)))
|
|
167
|
+
memo.names[origin] = classnames.method_names(reader.modules, memo.lines[origin])
|
|
168
|
+
return memo.lines[origin]
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _class_methods(found: Methods, reader: Reader, target: Module, spelled: str, origin: Origin) -> None:
|
|
172
|
+
"""Add an installed class's methods `target` calls, as it writes the class (`spelled`), to `found`."""
|
|
173
|
+
found.lineage[spelled] = _lineage(reader, origin)
|
|
174
|
+
memo: Memo = MEMO.of(reader.modules)
|
|
175
|
+
module: Module = reader.modules[origin[0]]
|
|
176
|
+
klass: Class = cast("Declarations", module.declared).classes[origin[1] or ""]
|
|
177
|
+
name: str
|
|
178
|
+
for name in sorted(memo.names[origin] & target.method_calls):
|
|
179
|
+
if (origin, name) not in memo.methods:
|
|
180
|
+
# Its lineage has the method: the class or a base declares it.
|
|
181
|
+
memo.methods[origin, name] = cast(
|
|
182
|
+
"tuple[ReadSignature, ...]",
|
|
183
|
+
reader.method(module, klass, name, 0),
|
|
184
|
+
)
|
|
185
|
+
found.signatures[f"{spelled}.{name}"] = memo.methods[origin, name]
|
|
186
|
+
found.parameters[spelled] = tuple(param for param, _ in klass.params)
|
|
187
|
+
attr: str
|
|
188
|
+
each: tuple[Signature, ...]
|
|
189
|
+
# What it reads of the method's return, off the call (see `chained`).
|
|
190
|
+
for attr, each in chained.attributes(module, klass, name, target.attributes).items():
|
|
191
|
+
found.signatures[f"{spelled}.{chained.key(name, attr)}"] = read_all(
|
|
192
|
+
reader,
|
|
193
|
+
module,
|
|
194
|
+
each,
|
|
195
|
+
frozenset(param for param, _ in klass.params),
|
|
196
|
+
dict(klass.params),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def read_signatures(modules: Mapping[str, Module], module: Module, name: str) -> tuple[ReadSignature, ...]:
|
|
201
|
+
"""Read an installed function's signatures as the tables hold theirs.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Them.
|
|
205
|
+
|
|
206
|
+
"""
|
|
207
|
+
# Found by its signatures (`project.definition`): an installed module's, or a checked file's overloads.
|
|
208
|
+
return read_all(Reader(modules), module, own_overloads.written(module, name), frozenset(), {})
|
|
Binary file
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Cross-module `--fix` for an empty container passed to another checked file's function.
|
|
3
|
+
|
|
4
|
+
Found through the calling file's imports and the re-exports between them (`add(names, "a")`,
|
|
5
|
+
`util.add(names, "a")`); a parameter the function declares a builtin container (`names: list[str]`)
|
|
6
|
+
types what's passed to it, where the file can write that type (see `project.portable`, and
|
|
7
|
+
`constricter.fix.values.fills`).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from constricter.fix.core.known import Guarded, Takers
|
|
13
|
+
from constricter.fix.index import project, sides
|
|
14
|
+
from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def calls(catalog: Index, path: Path, guarded: dict[str, Guarded]) -> dict[str, Takers]:
|
|
18
|
+
"""Find the parameters the functions the file at `path` calls declare a builtin container.
|
|
19
|
+
|
|
20
|
+
`guarded`: the names its imported types need imported for type checking (see `Guarded`), which
|
|
21
|
+
these types' are added to.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Each call's name as written (`add`, `util.add`), mapped to those parameters (see `Takers`),
|
|
25
|
+
each type as the file can write it; none for a file `catalog` doesn't have.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
target: Module | None = catalog.modules.get(module_name(path)) if path.suffix == SUFFIX else None
|
|
29
|
+
found: dict[str, Takers] = {}
|
|
30
|
+
if target is None:
|
|
31
|
+
return found
|
|
32
|
+
spelled: str
|
|
33
|
+
defined: tuple[Module, str]
|
|
34
|
+
for spelled, defined in sides.called(catalog.modules, target, lambda module, name: name in module.takers):
|
|
35
|
+
declared: Takers = defined[0].takers[defined[1]]
|
|
36
|
+
kept: dict[str, str] = project.portable(
|
|
37
|
+
catalog.modules,
|
|
38
|
+
(target, defined),
|
|
39
|
+
spelled,
|
|
40
|
+
{name: taken[2] for name, taken in declared.items()},
|
|
41
|
+
guarded,
|
|
42
|
+
)
|
|
43
|
+
if kept:
|
|
44
|
+
found[spelled] = {name: (*declared[name][:2], text) for name, text in kept.items()}
|
|
45
|
+
return found
|
|
Binary file
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""The tuples a file's names stand for, other checked files': a named tuple's fields, an alias's tuple.
|
|
3
|
+
|
|
4
|
+
What an unpacking splits a value of one of them by (see `targets.named_tuples`), spelled as the file
|
|
5
|
+
can write it; and the unions its names for their aliases stand for (see `targets.aliased_unions`).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import Callable, Iterator, Mapping
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from constricter.fix.core.known import Guarded, Origin
|
|
12
|
+
from constricter.fix.index.modules import SUFFIX, Index, Module, module_name
|
|
13
|
+
from constricter.fix.index.project import ALIAS, CLASS, definition, portable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def fields(catalog: Index, path: Path, guarded: dict[str, Guarded]) -> dict[str, str]:
|
|
17
|
+
"""Find the tuples the file at `path` can name, by how it names them.
|
|
18
|
+
|
|
19
|
+
Those the types written for it name, which it needn't import itself (a property's type, a
|
|
20
|
+
function's return; see `Guarded`), those it imports, and those a module it imports has.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Each name, and the tuple unpacking a value of it gives; nothing for a file `catalog` doesn't
|
|
24
|
+
have.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
target: Module | None
|
|
28
|
+
if path.suffix != SUFFIX or (target := catalog.modules.get(module_name(path))) is None:
|
|
29
|
+
return {}
|
|
30
|
+
found: dict[str, str] = {}
|
|
31
|
+
key: str
|
|
32
|
+
defined: tuple[Module, str]
|
|
33
|
+
for key, defined in _named(catalog.modules, target, guarded, _tuples):
|
|
34
|
+
found.update(_fields(catalog.modules, target, key, defined, guarded))
|
|
35
|
+
return found
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def unions(catalog: Index, path: Path, guarded: dict[str, Guarded]) -> dict[str, str]:
|
|
39
|
+
"""Find the unions the file at `path` names by other files' type aliases, by how it names them.
|
|
40
|
+
|
|
41
|
+
The aliases `fields` would find, were they tuples'.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Each name, and the union as its own module writes it: read for its members, never written.
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
target: Module | None
|
|
48
|
+
if path.suffix != SUFFIX or (target := catalog.modules.get(module_name(path))) is None:
|
|
49
|
+
return {}
|
|
50
|
+
return {
|
|
51
|
+
key: defined[0].unions[defined[1]]
|
|
52
|
+
for key, defined in _named(catalog.modules, target, guarded, _unions)
|
|
53
|
+
if defined[1] in defined[0].unions
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _tuples(module: Module) -> Mapping[str, str]:
|
|
58
|
+
return module.tuples
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _unions(module: Module) -> Mapping[str, str]:
|
|
62
|
+
return module.unions
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _named(
|
|
66
|
+
modules: Mapping[str, Module],
|
|
67
|
+
target: Module,
|
|
68
|
+
guarded: Mapping[str, Guarded],
|
|
69
|
+
held: Callable[[Module], Mapping[str, str]],
|
|
70
|
+
) -> Iterator[tuple[str, tuple[Module, str]]]:
|
|
71
|
+
"""Find the classes and aliases a file (`target`) names, and those of a module it imports that `held` has.
|
|
72
|
+
|
|
73
|
+
Yields:
|
|
74
|
+
Each one's name as the file spells it (`Pair`, or `shapes.Pair` after
|
|
75
|
+
`import pkg.shapes as shapes`), and where it's defined.
|
|
76
|
+
|
|
77
|
+
"""
|
|
78
|
+
named: list[tuple[str, Origin]] = [
|
|
79
|
+
*((key, each.origin) for key, each in guarded.items()),
|
|
80
|
+
*target.guarded.items(), # under `if TYPE_CHECKING:`
|
|
81
|
+
*((key, origin) for key, origin in target.names.items() if origin[0] != target.name),
|
|
82
|
+
]
|
|
83
|
+
key: str
|
|
84
|
+
origin: Origin
|
|
85
|
+
for key, origin in named:
|
|
86
|
+
defined: tuple[Module, str] | None = definition(modules, origin, CLASS) or definition(
|
|
87
|
+
modules,
|
|
88
|
+
origin,
|
|
89
|
+
ALIAS,
|
|
90
|
+
)
|
|
91
|
+
if defined is not None:
|
|
92
|
+
yield key, defined
|
|
93
|
+
module: Module | None
|
|
94
|
+
if origin[1] is None and (module := modules.get(origin[0])) is not None:
|
|
95
|
+
yield from ((f"{key}.{name}", (module, name)) for name in held(module))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _fields(
|
|
99
|
+
modules: Mapping[str, Module],
|
|
100
|
+
target: Module,
|
|
101
|
+
key: str,
|
|
102
|
+
defined: tuple[Module, str],
|
|
103
|
+
guarded: dict[str, Guarded],
|
|
104
|
+
) -> dict[str, str]:
|
|
105
|
+
"""Spell one tuple type's tuple (see `targets.named_tuples`) for the file (`target`) naming it `key`.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
`key`, and the tuple; nothing for any other class or alias, or types the file can't write.
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
tuple_type: str | None = defined[0].tuples.get(defined[1])
|
|
112
|
+
return {} if tuple_type is None else portable(modules, (target, defined), key, {key: tuple_type}, guarded)
|
|
Binary file
|
|
Binary file
|