@descryy/adapter-python 0.3.3 → 0.4.0
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.
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +37 -3
- package/dist/adapter.js.map +1 -1
- package/dist/annotation-density.d.ts +147 -0
- package/dist/annotation-density.d.ts.map +1 -0
- package/dist/annotation-density.js +173 -0
- package/dist/annotation-density.js.map +1 -0
- package/dist/external.d.ts +134 -0
- package/dist/external.d.ts.map +1 -0
- package/dist/external.js +227 -0
- package/dist/external.js.map +1 -0
- package/dist/extract.d.ts +6 -0
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +73 -2
- package/dist/extract.js.map +1 -1
- package/dist/extract.py +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/module.d.ts +9 -0
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js.map +1 -1
- package/package.json +4 -4
- package/src/extract.py +44 -0
package/dist/external.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dependency boundary, Python side.
|
|
3
|
+
*
|
|
4
|
+
* A call like `yaml.safe_load(raw)` leaves the repository. Today it becomes a
|
|
5
|
+
* ledger row saying the name resolves to something this run did not analyse,
|
|
6
|
+
* and the call graph stops there. This module answers the one question that
|
|
7
|
+
* turns that boundary into a fact the graph can hold: **which module, and which
|
|
8
|
+
* symbol in it.**
|
|
9
|
+
*
|
|
10
|
+
* **Identity comes from what the source writes, and Python is the language that
|
|
11
|
+
* makes the reason impossible to miss.** The distribution `pyyaml` installs the
|
|
12
|
+
* module `yaml`; `beautifulsoup4` installs `bs4`; `Pillow` installs `PIL`. A
|
|
13
|
+
* name taken from an installed tree, a `requirements.txt` line or
|
|
14
|
+
* `pyproject.toml`'s dependency table would therefore name something no line of
|
|
15
|
+
* source ever writes, and would move the first time somebody ran `pip install`.
|
|
16
|
+
* Nothing here reads any of those. **No distribution name and no version is
|
|
17
|
+
* recorded at all**, so neither can ride inside a node id — which is
|
|
18
|
+
* `hash(repo, kind, qualified symbol path)` and never includes a path.
|
|
19
|
+
*
|
|
20
|
+
* Python has two attributable shapes where Go has one:
|
|
21
|
+
*
|
|
22
|
+
* - `import yaml` … `yaml.safe_load(x)` — a module alias as receiver, the
|
|
23
|
+
* direct analogue of Go's package-qualified call.
|
|
24
|
+
* - `from yaml import safe_load` … `safe_load(x)` — a bare name. `adapter-go`
|
|
25
|
+
* refuses unqualified names because nothing in Go binds one to a package;
|
|
26
|
+
* `from … import` is exactly such a binding, and on measurement it is the
|
|
27
|
+
* LARGER half (2,790 of 4,836 attributions on `dispatch`).
|
|
28
|
+
*
|
|
29
|
+
* **Everything this module does not attribute is a deliberate refusal**, and
|
|
30
|
+
* each one has a test naming what it costs. The measured bar is 0 wrong
|
|
31
|
+
* attributions across 115 graded in-repo call sites on two repositories, and
|
|
32
|
+
* rule 2 prices a wrong edge far above a missing one.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Provenance of the standard-library name list, carried on every attributed
|
|
36
|
+
* row. Read from `sys.stdlib_module_names` at authoring time and frozen into
|
|
37
|
+
* this source — never read from the machine at run time, which would make the
|
|
38
|
+
* answer depend on the interpreter a developer happens to have.
|
|
39
|
+
*/
|
|
40
|
+
export const STDLIB_BASIS = "python-3.12 sys.stdlib_module_names";
|
|
41
|
+
/** Top-level standard-library module names. See `STDLIB_BASIS`. */
|
|
42
|
+
export const STDLIB_TOP_LEVEL = new Set(("__future__ _abc _aix_support _ast _asyncio _bisect _blake2 _bz2 _codecs _codecs_cn " +
|
|
43
|
+
"_codecs_hk _codecs_iso2022 _codecs_jp _codecs_kr _codecs_tw _collections _collections_abc " +
|
|
44
|
+
"_compat_pickle _compression _contextvars _crypt _csv _ctypes _curses _curses_panel _datetime " +
|
|
45
|
+
"_dbm _decimal _elementtree _frozen_importlib _frozen_importlib_external _functools _gdbm " +
|
|
46
|
+
"_hashlib _heapq _imp _io _json _locale _lsprof _lzma _markupbase _md5 _msi _multibytecodec " +
|
|
47
|
+
"_multiprocessing _opcode _operator _osx_support _overlapped _pickle _posixshmem " +
|
|
48
|
+
"_posixsubprocess _py_abc _pydatetime _pydecimal _pyio _pylong _queue _random _scproxy _sha1 " +
|
|
49
|
+
"_sha2 _sha3 _signal _sitebuiltins _socket _sqlite3 _sre _ssl _stat _statistics _string " +
|
|
50
|
+
"_strptime _struct _symtable _thread _threading_local _tkinter _tokenize _tracemalloc _typing " +
|
|
51
|
+
"_uuid _warnings _weakref _weakrefset _winapi _zoneinfo abc aifc antigravity argparse array " +
|
|
52
|
+
"ast asyncio atexit audioop base64 bdb binascii bisect builtins bz2 cProfile calendar cgi " +
|
|
53
|
+
"cgitb chunk cmath cmd code codecs codeop collections colorsys compileall concurrent " +
|
|
54
|
+
"configparser contextlib contextvars copy copyreg crypt csv ctypes curses dataclasses " +
|
|
55
|
+
"datetime dbm decimal difflib dis doctest email encodings ensurepip enum errno faulthandler " +
|
|
56
|
+
"fcntl filecmp fileinput fnmatch fractions ftplib functools gc genericpath getopt getpass " +
|
|
57
|
+
"gettext glob graphlib grp gzip hashlib heapq hmac html http idlelib imaplib imghdr importlib " +
|
|
58
|
+
"inspect io ipaddress itertools json keyword lib2to3 linecache locale logging lzma mailbox " +
|
|
59
|
+
"mailcap marshal math mimetypes mmap modulefinder msilib msvcrt multiprocessing netrc nis " +
|
|
60
|
+
"nntplib nt ntpath nturl2path numbers opcode operator optparse os ossaudiodev pathlib pdb " +
|
|
61
|
+
"pickle pickletools pipes pkgutil platform plistlib poplib posix posixpath pprint profile " +
|
|
62
|
+
"pstats pty pwd py_compile pyclbr pydoc pydoc_data pyexpat queue quopri random re readline " +
|
|
63
|
+
"reprlib resource rlcompleter runpy sched secrets select selectors shelve shlex shutil signal " +
|
|
64
|
+
"site smtplib sndhdr socket socketserver spwd sqlite3 sre_compile sre_constants sre_parse ssl " +
|
|
65
|
+
"stat statistics string stringprep struct subprocess sunau symtable sys sysconfig syslog " +
|
|
66
|
+
"tabnanny tarfile telnetlib tempfile termios textwrap this threading time timeit tkinter " +
|
|
67
|
+
"token tokenize tomllib trace traceback tracemalloc tty turtle turtledemo types typing " +
|
|
68
|
+
"unicodedata unittest urllib uu uuid venv warnings wave weakref webbrowser winreg winsound " +
|
|
69
|
+
"wsgiref xdrlib xml xmlrpc zipapp zipfile zipimport zlib zoneinfo").split(" "));
|
|
70
|
+
/** Is `module`, or one of its parents, a package the repository itself provides?
|
|
71
|
+
* Compared on WHOLE dotted segments: a root-prefix test would make
|
|
72
|
+
* `dispatchbus.models` part of `dispatch`, which is §5.2's vendor-namespace
|
|
73
|
+
* hazard. Only the first segment can match, because only a top-level name is
|
|
74
|
+
* ever an import root's own child. */
|
|
75
|
+
function isOurs(module, own) {
|
|
76
|
+
return own.has(module.split(".")[0] ?? "");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* One file's `import` statements, reduced to the bindings that leave the
|
|
80
|
+
* repository.
|
|
81
|
+
*
|
|
82
|
+
* Four refusals are made here rather than at the call site, because they are
|
|
83
|
+
* facts about the statement and not about any one use of it.
|
|
84
|
+
*/
|
|
85
|
+
export function externalImportTable(records, options) {
|
|
86
|
+
const table = new Map();
|
|
87
|
+
/** local -> the exact statement that bound it, for the conflict check. */
|
|
88
|
+
const boundBy = new Map();
|
|
89
|
+
const conflicted = new Set();
|
|
90
|
+
for (const record of records) {
|
|
91
|
+
// A relative import cannot leave the repository — the dots address our own
|
|
92
|
+
// package. A name bound by one this run failed to resolve is unanalysed
|
|
93
|
+
// code of OURS, which is an analysis gap and a different fact entirely.
|
|
94
|
+
if (record.level > 0)
|
|
95
|
+
continue;
|
|
96
|
+
const module = record.module ?? "";
|
|
97
|
+
if (module === "")
|
|
98
|
+
continue;
|
|
99
|
+
// `from yaml import *` binds names nobody wrote down, so it binds nothing
|
|
100
|
+
// this reader may claim.
|
|
101
|
+
if (record.name === "*")
|
|
102
|
+
continue;
|
|
103
|
+
if (isOurs(module, options.ownNamespaces))
|
|
104
|
+
continue;
|
|
105
|
+
if (options.resolvedLocals.has(record.local))
|
|
106
|
+
continue;
|
|
107
|
+
let binding;
|
|
108
|
+
if (record.name === null) {
|
|
109
|
+
// `import a.b.c` binds `a`, not `a.b.c`; `import a.b.c as x` binds the
|
|
110
|
+
// whole path to `x`. `asname` is read from the statement when the parse
|
|
111
|
+
// recorded it, and falls back to comparing the local against the top
|
|
112
|
+
// segment for a recording made before that field existed.
|
|
113
|
+
const top = module.split(".")[0] ?? module;
|
|
114
|
+
const aliased = record.asname !== undefined && record.asname !== null
|
|
115
|
+
? true
|
|
116
|
+
: record.local !== top;
|
|
117
|
+
binding = { kind: "module", module: aliased ? module : top };
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
binding = { kind: "symbol", module, name: record.name };
|
|
121
|
+
}
|
|
122
|
+
const signature = JSON.stringify(binding);
|
|
123
|
+
const previous = boundBy.get(record.local);
|
|
124
|
+
if (previous !== undefined && previous !== signature) {
|
|
125
|
+
// `try: import ujson as json / except ImportError: import json`. Which
|
|
126
|
+
// module a call reaches is decided at run time by what is installed —
|
|
127
|
+
// the one question a source-only reader must not answer. Go never needed
|
|
128
|
+
// this refusal; Python's conditional-import idiom makes it routine.
|
|
129
|
+
conflicted.add(record.local);
|
|
130
|
+
table.delete(record.local);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (conflicted.has(record.local))
|
|
134
|
+
continue;
|
|
135
|
+
boundBy.set(record.local, signature);
|
|
136
|
+
table.set(record.local, binding);
|
|
137
|
+
}
|
|
138
|
+
return table;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The top-level module names the repository provides for itself.
|
|
142
|
+
*
|
|
143
|
+
* Found by the same outward walk `importRoots` uses — a directory stops being
|
|
144
|
+
* part of the module path at the first ancestor with no `__init__.py` — so the
|
|
145
|
+
* two cannot disagree about where a package starts and an import root ends.
|
|
146
|
+
* `src/dispatch/models.py` makes `dispatch` ours; `tests/test_models.py` makes
|
|
147
|
+
* `test_models` ours, because `tests/` has no `__init__.py` and is the import
|
|
148
|
+
* root rather than a package.
|
|
149
|
+
*
|
|
150
|
+
* Over-inclusive by construction: a repository with many rootless directories
|
|
151
|
+
* contributes many names, and every one of them is a name this reader will
|
|
152
|
+
* decline to call a dependency. That is the safe direction.
|
|
153
|
+
*/
|
|
154
|
+
export function ownNamespaces(files) {
|
|
155
|
+
const present = new Set(files);
|
|
156
|
+
const own = new Set();
|
|
157
|
+
for (const file of files) {
|
|
158
|
+
const parts = file.split("/");
|
|
159
|
+
let depth = parts.length - 1;
|
|
160
|
+
while (depth > 0 && present.has(`${parts.slice(0, depth).join("/")}/__init__.py`)) {
|
|
161
|
+
depth -= 1;
|
|
162
|
+
}
|
|
163
|
+
const head = parts[depth];
|
|
164
|
+
if (head === undefined)
|
|
165
|
+
continue;
|
|
166
|
+
const name = head.replace(/\.pyi?$/, "");
|
|
167
|
+
if (name !== "")
|
|
168
|
+
own.add(name);
|
|
169
|
+
}
|
|
170
|
+
return own;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The module and symbol behind a call that left the repository, or `null` when
|
|
174
|
+
* no binding the developer wrote settles it.
|
|
175
|
+
*/
|
|
176
|
+
export function attributeExternal(site) {
|
|
177
|
+
const { receiver, name } = site;
|
|
178
|
+
if (name === "")
|
|
179
|
+
return null;
|
|
180
|
+
if (receiver === undefined || receiver === "") {
|
|
181
|
+
// An unqualified call. `from yaml import safe_load` … `safe_load(x)`.
|
|
182
|
+
if (site.assigned.has(name))
|
|
183
|
+
return null;
|
|
184
|
+
const binding = site.imports.get(name);
|
|
185
|
+
if (binding === undefined)
|
|
186
|
+
return null;
|
|
187
|
+
// `import yaml` … `yaml(x)` calls a module. Nothing sensible is being
|
|
188
|
+
// named; refuse rather than mint a symbol named after a module.
|
|
189
|
+
if (binding.kind !== "symbol")
|
|
190
|
+
return null;
|
|
191
|
+
return attribution(binding.module, [binding.name]);
|
|
192
|
+
}
|
|
193
|
+
// `self.x()` needs the enclosing class's own members, which is a different
|
|
194
|
+
// question with a different answer, and it is never a dependency.
|
|
195
|
+
if (receiver === "self")
|
|
196
|
+
return null;
|
|
197
|
+
// A dotted head means a value was traversed and its TYPE decides the callee —
|
|
198
|
+
// a question no import table answers. The parser reports a receiver only for
|
|
199
|
+
// a bare name, so this cannot arise today; the guard stays because a change
|
|
200
|
+
// there must not silently become an attribution here.
|
|
201
|
+
if (receiver.includes("."))
|
|
202
|
+
return null;
|
|
203
|
+
// A local, a parameter or a `def` of the same spelling shadows the import
|
|
204
|
+
// inside its scope. The import table cannot see the shadow, so the only
|
|
205
|
+
// honest move is to decline the site entirely.
|
|
206
|
+
if (site.assigned.has(receiver))
|
|
207
|
+
return null;
|
|
208
|
+
const binding = site.imports.get(receiver);
|
|
209
|
+
if (binding === undefined)
|
|
210
|
+
return null;
|
|
211
|
+
// `from django.conf import settings` … `settings.configure()`. The import
|
|
212
|
+
// binds an OBJECT, not a namespace, so which class declares `configure` is a
|
|
213
|
+
// question about its type. This is Go's non-alias-receiver refusal and it is
|
|
214
|
+
// the largest single cost here: 1,580 ledger rows on `dispatch`, 4,967 on
|
|
215
|
+
// `plane`.
|
|
216
|
+
if (binding.kind !== "module")
|
|
217
|
+
return null;
|
|
218
|
+
return attribution(binding.module, [name]);
|
|
219
|
+
}
|
|
220
|
+
function attribution(module, symbolPath) {
|
|
221
|
+
return {
|
|
222
|
+
moduleOrNamespace: module,
|
|
223
|
+
symbolPath,
|
|
224
|
+
thirdParty: !STDLIB_TOP_LEVEL.has(module.split(".")[0] ?? module),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=external.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external.js","sourceRoot":"","sources":["../src/external.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AA+CH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,qCAAqC,CAAC;AAElE,mEAAmE;AACnE,MAAM,CAAC,MAAM,gBAAgB,GAAwB,IAAI,GAAG,CAC1D,CACE,qFAAqF;IACrF,4FAA4F;IAC5F,+FAA+F;IAC/F,2FAA2F;IAC3F,6FAA6F;IAC7F,kFAAkF;IAClF,8FAA8F;IAC9F,yFAAyF;IACzF,+FAA+F;IAC/F,6FAA6F;IAC7F,2FAA2F;IAC3F,sFAAsF;IACtF,uFAAuF;IACvF,6FAA6F;IAC7F,2FAA2F;IAC3F,+FAA+F;IAC/F,4FAA4F;IAC5F,2FAA2F;IAC3F,2FAA2F;IAC3F,2FAA2F;IAC3F,4FAA4F;IAC5F,+FAA+F;IAC/F,+FAA+F;IAC/F,0FAA0F;IAC1F,0FAA0F;IAC1F,wFAAwF;IACxF,4FAA4F;IAC5F,kEAAkE,CACnE,CAAC,KAAK,CAAC,GAAG,CAAC,CACb,CAAC;AAkBF;;;;uCAIuC;AACvC,SAAS,MAAM,CAAC,MAAc,EAAE,GAAwB;IACtD,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAgC,EAChC,OAA2B;IAE3B,MAAM,KAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IACjD,0EAA0E;IAC1E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAErC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,2EAA2E;QAC3E,wEAAwE;QACxE,wEAAwE;QACxE,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC;YAAE,SAAS;QAE/B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;QACnC,IAAI,MAAM,KAAK,EAAE;YAAE,SAAS;QAE5B,0EAA0E;QAC1E,yBAAyB;QACzB,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG;YAAE,SAAS;QAElC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC;YAAE,SAAS;QACpD,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,SAAS;QAEvD,IAAI,OAAwB,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACzB,uEAAuE;YACvE,wEAAwE;YACxE,qEAAqE;YACrE,0DAA0D;YAC1D,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;YAC3C,MAAM,OAAO,GACX,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI;gBACnD,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,GAAG,CAAC;YAC3B,OAAO,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QAC1D,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YACrD,uEAAuE;YACvE,sEAAsE;YACtE,yEAAyE;YACzE,oEAAoE;YACpE,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,SAAS;QAC3C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QACrC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,aAAa,CAAC,KAAwB;IACpD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7B,OAAO,KAAK,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YAClF,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACzC,IAAI,IAAI,KAAK,EAAE;YAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAkB;IAClD,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAChC,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAE7B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QAC9C,sEAAsE;QACtE,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACvC,sEAAsE;QACtE,gEAAgE;QAChE,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC3C,OAAO,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,2EAA2E;IAC3E,kEAAkE;IAClE,IAAI,QAAQ,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAErC,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,sDAAsD;IACtD,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,0EAA0E;IAC1E,wEAAwE;IACxE,+CAA+C;IAC/C,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvC,0EAA0E;IAC1E,6EAA6E;IAC7E,6EAA6E;IAC7E,0EAA0E;IAC1E,WAAW;IACX,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3C,OAAO,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,WAAW,CAAC,MAAc,EAAE,UAA6B;IAChE,OAAO;QACL,iBAAiB,EAAE,MAAM;QACzB,UAAU;QACV,UAAU,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;KAClE,CAAC;AACJ,CAAC"}
|
package/dist/extract.d.ts
CHANGED
|
@@ -183,6 +183,12 @@ interface PyFile {
|
|
|
183
183
|
readonly migrationOps?: readonly PyMigrationOp[];
|
|
184
184
|
/** `attrs.migrationRevision`/`migrationDownRevision`. `null` when the file is not an Alembic revision script. */
|
|
185
185
|
readonly migrationRevision?: PyMigrationRevision | null;
|
|
186
|
+
/** Every name this file binds itself — the shadow guard at the dependency
|
|
187
|
+
* boundary (`external.ts`). Optional so an older recording still parses;
|
|
188
|
+
* absent means no name is treated as shadowed, which attributes MORE, so
|
|
189
|
+
* the field is required in practice and the fallback is the honest one only
|
|
190
|
+
* because an old recording predates the hazard being read at all. */
|
|
191
|
+
readonly assignedNames?: readonly string[];
|
|
186
192
|
}
|
|
187
193
|
/** One name this module reads from the environment, and whether it must be set. */
|
|
188
194
|
interface PyEnvRead {
|
package/dist/extract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAWL,KAAK,MAAM,EACX,KAAK,MAAM,EAGX,KAAK,eAAe,EACpB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"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"}
|
package/dist/extract.js
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import { databaseTableNodesFromModel, edgeId, endpointQsp, isCallableNodeType, isTypeLikeNodeType, nodeId, normaliseEndpointPath, symbolQsp, testCaseQsp, } from "@descryy/ir";
|
|
34
34
|
import { importRoots, modulePathOf, moduleNameOf, resolveImport, } from "./module.js";
|
|
35
|
+
import { attributeExternal, externalImportTable, ownNamespaces, STDLIB_BASIS, } from "./external.js";
|
|
35
36
|
import { DECLARATIVE_BASE_FACTORIES, ORM_BASE_MODULES, isFrameworkBaseName, ormShapeOf, } from "./orm.js";
|
|
36
37
|
import { globToRegExp, PYTEST_DEFAULTS } from "./pytest-config.js";
|
|
37
38
|
import { drfShapeOf, dtoShapeOf, isDrfSerializerRoot, isPydanticRoot, isValidatorDecorator, PYDANTIC_MODEL_MEMBERS, } from "./pydantic.js";
|
|
@@ -390,6 +391,19 @@ export function extract(input) {
|
|
|
390
391
|
}
|
|
391
392
|
return target;
|
|
392
393
|
};
|
|
394
|
+
/**
|
|
395
|
+
* The dependency boundary, per file: local name -> the module outside this
|
|
396
|
+
* repository that an import statement bound it to. See `external.ts` — the
|
|
397
|
+
* table is built from the file's own import statements and nothing installed,
|
|
398
|
+
* because the distribution `pyyaml` installs the module `yaml` and only
|
|
399
|
+
* `yaml` is ever written in source.
|
|
400
|
+
*/
|
|
401
|
+
const externalImportsOf = new Map();
|
|
402
|
+
/** Names each file binds itself, which may shadow an import — the guard. */
|
|
403
|
+
const assignedOf = new Map();
|
|
404
|
+
/** Top-level module names this repository provides, so one of ours that this
|
|
405
|
+
* run did not read is never mistaken for a dependency. */
|
|
406
|
+
const own = ownNamespaces(files);
|
|
393
407
|
for (const file of files) {
|
|
394
408
|
const parsed = input.parsed.files[file];
|
|
395
409
|
const bindings = new Map();
|
|
@@ -408,7 +422,48 @@ export function extract(input) {
|
|
|
408
422
|
record,
|
|
409
423
|
});
|
|
410
424
|
}
|
|
425
|
+
externalImportsOf.set(file, externalImportTable(parsed.imports, {
|
|
426
|
+
ownNamespaces: own,
|
|
427
|
+
// A local this run already resolved to a file in the repository is not
|
|
428
|
+
// a boundary, whatever its module path looks like.
|
|
429
|
+
resolvedLocals: new Set(bindings.keys()),
|
|
430
|
+
}));
|
|
431
|
+
assignedOf.set(file, new Set(parsed.assignedNames ?? []));
|
|
411
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* The module and symbol behind a call that left the repository, as ledger
|
|
435
|
+
* `attrs`, or `undefined` when no binding the developer wrote settles it.
|
|
436
|
+
*
|
|
437
|
+
* **This does not mint a node or an edge.** `@descryy/ir` gained the
|
|
438
|
+
* discriminator that lets a `FUNCTION` stand for a symbol in a dependency
|
|
439
|
+
* only at 0.7.0; this package is pinned to the release before it, and a node
|
|
440
|
+
* emitted against that release is rejected with every edge to it — losing the
|
|
441
|
+
* ledger row as well, which is strictly worse than the boundary being
|
|
442
|
+
* unnamed. The attribution is computed at the site that files the row so that
|
|
443
|
+
* minting is the only step left.
|
|
444
|
+
*/
|
|
445
|
+
const externalAttrs = (file, receiver, name) => {
|
|
446
|
+
const attribution = attributeExternal({
|
|
447
|
+
receiver,
|
|
448
|
+
name,
|
|
449
|
+
imports: externalImportsOf.get(file) ?? new Map(),
|
|
450
|
+
assigned: assignedOf.get(file) ?? new Set(),
|
|
451
|
+
});
|
|
452
|
+
if (attribution === null)
|
|
453
|
+
return undefined;
|
|
454
|
+
return {
|
|
455
|
+
external: {
|
|
456
|
+
// Identity, and it is the module path the source imported — never a
|
|
457
|
+
// site-packages directory, never a distribution name, never a version.
|
|
458
|
+
moduleOrNamespace: attribution.moduleOrNamespace,
|
|
459
|
+
symbol: attribution.symbolPath.join("."),
|
|
460
|
+
thirdParty: attribution.thirdParty,
|
|
461
|
+
// Honest degradation: `thirdParty` is decided against a static list,
|
|
462
|
+
// not against anything installed, and the row says which list.
|
|
463
|
+
thirdPartyBasis: STDLIB_BASIS,
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
};
|
|
412
467
|
// --- which classes are test suites, decided from evidence not from names ---
|
|
413
468
|
/** file -> top-level class name -> the bases it was written with. */
|
|
414
469
|
const basesOf = new Map();
|
|
@@ -1417,7 +1472,12 @@ export function extract(input) {
|
|
|
1417
1472
|
if (reference.kind === "call") {
|
|
1418
1473
|
const target = followName(file, reference.name);
|
|
1419
1474
|
if (target === undefined) {
|
|
1420
|
-
|
|
1475
|
+
// The boundary. `from yaml import safe_load` ... `safe_load(raw)` —
|
|
1476
|
+
// a bare name, but an import statement bound it, so the module it
|
|
1477
|
+
// left into is readable. This is the half `adapter-go` has no
|
|
1478
|
+
// analogue for (Go has no from-import), and on measurement it is the
|
|
1479
|
+
// larger half: 2,790 of 4,836 attributions on dispatch.
|
|
1480
|
+
disclose(source, edgeType, reference.name, file, reference.line, "outside", externalAttrs(file, undefined, reference.name));
|
|
1421
1481
|
continue;
|
|
1422
1482
|
}
|
|
1423
1483
|
if ((target.type === "FUNCTION" || target.type === "TEST_CASE") && target.id !== source.id) {
|
|
@@ -1490,7 +1550,18 @@ export function extract(input) {
|
|
|
1490
1550
|
}
|
|
1491
1551
|
const receiverType = instantiatedType(file, reference.scope, reference.receiver);
|
|
1492
1552
|
if (receiverType === undefined) {
|
|
1493
|
-
|
|
1553
|
+
// Last, after every in-repo reading has been tried, so attribution
|
|
1554
|
+
// can never steal a site from a better answer: a receiver that is a
|
|
1555
|
+
// local built from one of our own classes resolved above and never
|
|
1556
|
+
// reaches here.
|
|
1557
|
+
//
|
|
1558
|
+
// `import yaml` ... `yaml.safe_load(raw)`. When an import statement
|
|
1559
|
+
// binds the receiver, "no resolvable binding" is a FALSE sentence
|
|
1560
|
+
// about this row — there is one, and it leaves the repository. So the
|
|
1561
|
+
// reason becomes `outside`, which is what the row actually is. An
|
|
1562
|
+
// unattributed row is untouched and stays `unknown`.
|
|
1563
|
+
const external = externalAttrs(file, reference.receiver, reference.name);
|
|
1564
|
+
disclose(source, edgeType, `${reference.receiver}.${reference.name}`, file, reference.line, external === undefined ? "unknown" : "outside", external);
|
|
1494
1565
|
continue;
|
|
1495
1566
|
}
|
|
1496
1567
|
const method = byFullName
|