@descryy/adapter-python 0.1.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 +75 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +481 -0
- package/dist/adapter.js.map +1 -0
- package/dist/client.d.ts +117 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +347 -0
- package/dist/client.js.map +1 -0
- package/dist/extract.d.ts +231 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +2267 -0
- package/dist/extract.js.map +1 -0
- package/dist/extract.py +1319 -0
- package/dist/graphql.d.ts +197 -0
- package/dist/graphql.d.ts.map +1 -0
- package/dist/graphql.js +302 -0
- package/dist/graphql.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +99 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +211 -0
- package/dist/module.js.map +1 -0
- package/dist/orm.d.ts +102 -0
- package/dist/orm.d.ts.map +1 -0
- package/dist/orm.js +221 -0
- package/dist/orm.js.map +1 -0
- package/dist/pydantic.d.ts +157 -0
- package/dist/pydantic.d.ts.map +1 -0
- package/dist/pydantic.js +318 -0
- package/dist/pydantic.js.map +1 -0
- package/dist/pytest-config.d.ts +60 -0
- package/dist/pytest-config.d.ts.map +1 -0
- package/dist/pytest-config.js +147 -0
- package/dist/pytest-config.js.map +1 -0
- package/dist/routes.d.ts +249 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +511 -0
- package/dist/routes.js.map +1 -0
- package/package.json +34 -0
- package/src/extract.py +1319 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Python `LanguageAdapter` — all seven methods.
|
|
3
|
+
*
|
|
4
|
+
* The parser is Python's own `ast`, reached through one subprocess per run
|
|
5
|
+
* (DEC-062). Everything language-specific stops at this package's boundary;
|
|
6
|
+
* `@descryy/ir` is the only engine dependency, which is what makes the IR
|
|
7
|
+
* boundary a dependency-direction fact rather than a convention.
|
|
8
|
+
*/
|
|
9
|
+
import { type LanguageAdapter, type ResolutionLevel, type SkipReason } from "@descryy/ir";
|
|
10
|
+
import { LANGUAGE, type PyResult } from "./extract.ts";
|
|
11
|
+
export declare const ADAPTER_ID = "adapter-python";
|
|
12
|
+
export declare const ADAPTER_VERSION = "0.1.0";
|
|
13
|
+
export declare const DEFAULT_PYTHON_GLOBS: readonly ["**/*.py", "**/*.pyi"];
|
|
14
|
+
export interface PythonAdapterOptions {
|
|
15
|
+
readonly globs?: readonly string[];
|
|
16
|
+
readonly maxResolution?: ResolutionLevel;
|
|
17
|
+
/** Override the interpreter. Mostly for tests and for a pinned virtualenv. */
|
|
18
|
+
readonly python?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Why the extractor is not read with `execFile`.
|
|
22
|
+
*
|
|
23
|
+
* `execFile` buffers the whole of stdout and fails past `maxBuffer` with a
|
|
24
|
+
* generic error. On home-assistant-core the extractor emits **284.4 MB**, which
|
|
25
|
+
* overflowed a 256 MB bound; the error was indistinguishable from "the
|
|
26
|
+
* interpreter failed", so `prepare()` degraded to R0 and the run reported
|
|
27
|
+
* **success with an empty graph over 18,189 files**. Two separate faults, and
|
|
28
|
+
* the second is the serious one — a capacity limit became a silent wrong answer.
|
|
29
|
+
*
|
|
30
|
+
* This reads the stream line by line instead. There is no buffer ceiling, no
|
|
31
|
+
* single giant string, and a stream that ends without its trailer is detected as
|
|
32
|
+
* truncation rather than mistaken for a short repository.
|
|
33
|
+
*/
|
|
34
|
+
export type ExtractorFailure = {
|
|
35
|
+
readonly kind: "no-interpreter";
|
|
36
|
+
} | {
|
|
37
|
+
readonly kind: "spawn-failed";
|
|
38
|
+
readonly detail: string;
|
|
39
|
+
} | {
|
|
40
|
+
readonly kind: "nonzero-exit";
|
|
41
|
+
readonly code: number;
|
|
42
|
+
readonly stderr: string;
|
|
43
|
+
} | {
|
|
44
|
+
readonly kind: "truncated";
|
|
45
|
+
readonly linesRead: number;
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: "malformed";
|
|
48
|
+
readonly detail: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly kind: "timed-out";
|
|
51
|
+
readonly afterMs: number;
|
|
52
|
+
readonly linesRead: number;
|
|
53
|
+
};
|
|
54
|
+
export interface ExtractorOutcome {
|
|
55
|
+
readonly parsed?: PyResult;
|
|
56
|
+
readonly failure?: ExtractorFailure;
|
|
57
|
+
}
|
|
58
|
+
/** Human-readable, and deliberately explicit that the graph is incomplete. */
|
|
59
|
+
export declare function describeFailure(failure: ExtractorFailure): string;
|
|
60
|
+
/**
|
|
61
|
+
* `extract.py`'s per-file error strings have no machine-readable kind, only a
|
|
62
|
+
* prefix convention (`"unreadable: ..."` for an `OSError`, `"syntax error at
|
|
63
|
+
* line ..."` for a `SyntaxError`) — everything else (`ValueError`,
|
|
64
|
+
* `RecursionError`) falls to `"other"` rather than being guessed at.
|
|
65
|
+
*/
|
|
66
|
+
export declare function skipReasonOf(message: string): SkipReason;
|
|
67
|
+
export interface PythonAdapter extends LanguageAdapter {
|
|
68
|
+
readonly id: string;
|
|
69
|
+
readonly version: string;
|
|
70
|
+
}
|
|
71
|
+
export declare function createPythonAdapter(options?: PythonAdapterOptions): PythonAdapter;
|
|
72
|
+
/** Content hash of a repo-relative file, for `FileRef`. */
|
|
73
|
+
export declare function hashOf(root: string, relative: string): string;
|
|
74
|
+
export { LANGUAGE };
|
|
75
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,EAML,KAAK,eAAe,EAGpB,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAA0B,QAAQ,EAAmB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAIhG,eAAO,MAAM,UAAU,mBAAmB,CAAC;AAC3C,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,oBAAoB,kCAAmC,CAAC;AAkBrE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;IACzC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAiBD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GACnC;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjF;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAuBjE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAIxD;AAkID,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CA2TrF;AAED,2DAA2D;AAC3D,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM7D;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Python `LanguageAdapter` — all seven methods.
|
|
3
|
+
*
|
|
4
|
+
* The parser is Python's own `ast`, reached through one subprocess per run
|
|
5
|
+
* (DEC-062). Everything language-specific stops at this package's boundary;
|
|
6
|
+
* `@descryy/ir` is the only engine dependency, which is what makes the IR
|
|
7
|
+
* boundary a dependency-direction fact rather than a convention.
|
|
8
|
+
*/
|
|
9
|
+
import { execFile, spawn } from "node:child_process";
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import {} from "@descryy/ir";
|
|
14
|
+
import { discover } from "@descryy/adapter-common";
|
|
15
|
+
import { degradedGraph, extract, LANGUAGE } from "./extract.js";
|
|
16
|
+
import { projectName } from "./module.js";
|
|
17
|
+
import { PYTEST_DEFAULTS, readPytestConfig } from "./pytest-config.js";
|
|
18
|
+
export const ADAPTER_ID = "adapter-python";
|
|
19
|
+
export const ADAPTER_VERSION = "0.1.0";
|
|
20
|
+
export const DEFAULT_PYTHON_GLOBS = ["**/*.py", "**/*.pyi"];
|
|
21
|
+
const PRODUCED_BY = `${ADAPTER_ID}@${ADAPTER_VERSION}`;
|
|
22
|
+
/**
|
|
23
|
+
* The extractor, shipped as a file and located relative to this module.
|
|
24
|
+
*
|
|
25
|
+
* Resolved from `import.meta.dirname` rather than from `process.cwd()`, because
|
|
26
|
+
* an adapter is loaded by an engine whose working directory is the repository
|
|
27
|
+
* under analysis, not this package.
|
|
28
|
+
*/
|
|
29
|
+
import { CLIENT_MODULES } from "./client.js";
|
|
30
|
+
const EXTRACTOR = join(import.meta.dirname, "extract.py");
|
|
31
|
+
/** Interpreters to try, in order. `python` last: on many systems it is Python 2. */
|
|
32
|
+
const INTERPRETERS = ["python3", "python"];
|
|
33
|
+
/** Human-readable, and deliberately explicit that the graph is incomplete. */
|
|
34
|
+
export function describeFailure(failure) {
|
|
35
|
+
switch (failure.kind) {
|
|
36
|
+
case "no-interpreter":
|
|
37
|
+
return "no Python 3 interpreter was found, so nothing could be parsed";
|
|
38
|
+
case "spawn-failed":
|
|
39
|
+
return `the extractor could not be started: ${failure.detail}`;
|
|
40
|
+
case "nonzero-exit":
|
|
41
|
+
return `the extractor exited ${failure.code}: ${failure.stderr.slice(0, 400)}`;
|
|
42
|
+
case "truncated":
|
|
43
|
+
return (`the extractor's output ended without its completion marker after ${failure.linesRead} ` +
|
|
44
|
+
"record(s) — the stream was truncated, so the graph is INCOMPLETE by an unknown amount");
|
|
45
|
+
case "malformed":
|
|
46
|
+
return `the extractor's output could not be read: ${failure.detail}`;
|
|
47
|
+
case "timed-out":
|
|
48
|
+
return (`the extractor was killed after ${Math.round(failure.afterMs / 1000)}s, having produced ` +
|
|
49
|
+
`${failure.linesRead} record(s). NOTHING was analysed: what it had streamed is discarded ` +
|
|
50
|
+
"rather than reported, because a partial parse presented as a whole graph is worse than " +
|
|
51
|
+
"no graph. Raise prepareTimeoutMs, or analyse a subdirectory.");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* `extract.py`'s per-file error strings have no machine-readable kind, only a
|
|
56
|
+
* prefix convention (`"unreadable: ..."` for an `OSError`, `"syntax error at
|
|
57
|
+
* line ..."` for a `SyntaxError`) — everything else (`ValueError`,
|
|
58
|
+
* `RecursionError`) falls to `"other"` rather than being guessed at.
|
|
59
|
+
*/
|
|
60
|
+
export function skipReasonOf(message) {
|
|
61
|
+
if (message.startsWith("unreadable:"))
|
|
62
|
+
return "unreadable";
|
|
63
|
+
if (message.startsWith("syntax error"))
|
|
64
|
+
return "grammar-error";
|
|
65
|
+
return "other";
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `AnalysisContext.prepareTimeoutMs` was declared in the contract and honoured by
|
|
69
|
+
* neither adapter — a bound the caller could set and nothing would enforce, so a
|
|
70
|
+
* repository that hung the extractor hung the whole run with no upper limit. The
|
|
71
|
+
* same class of defect as a field computed on one side of a process boundary and
|
|
72
|
+
* never read on the other, and found by the same method: looking for declared
|
|
73
|
+
* things nothing consumes.
|
|
74
|
+
*
|
|
75
|
+
* A timeout here is a **degraded state, not a crash.** The child is killed, what
|
|
76
|
+
* it managed to stream is discarded rather than reported as a whole graph, and
|
|
77
|
+
* the run falls to the disclosed R0 path — because a partial parse silently
|
|
78
|
+
* presented as complete is exactly the failure the posthog transport bug was.
|
|
79
|
+
*/
|
|
80
|
+
function runStreaming(command, args, input, signal, timeoutMs) {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
const files = {};
|
|
83
|
+
const errors = {};
|
|
84
|
+
let pythonVersion = "";
|
|
85
|
+
let sawTrailer = false;
|
|
86
|
+
let lines = 0;
|
|
87
|
+
let pending = "";
|
|
88
|
+
let stderr = "";
|
|
89
|
+
let malformed;
|
|
90
|
+
const child = spawn(command, [...args], { signal });
|
|
91
|
+
let timedOut = false;
|
|
92
|
+
const timer = setTimeout(() => {
|
|
93
|
+
timedOut = true;
|
|
94
|
+
child.kill("SIGKILL");
|
|
95
|
+
}, timeoutMs);
|
|
96
|
+
timer.unref?.();
|
|
97
|
+
const finish = (outcome) => {
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
resolve(outcome);
|
|
100
|
+
};
|
|
101
|
+
const take = (line) => {
|
|
102
|
+
if (line === "")
|
|
103
|
+
return;
|
|
104
|
+
lines += 1;
|
|
105
|
+
let record;
|
|
106
|
+
try {
|
|
107
|
+
record = JSON.parse(line);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
malformed ??= `line ${lines}: ${error.message}`;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (record.kind === "header")
|
|
114
|
+
pythonVersion = record.pythonVersion ?? "";
|
|
115
|
+
else if (record.kind === "file" && record.file !== undefined)
|
|
116
|
+
files[record.file] = record.parsed;
|
|
117
|
+
else if (record.kind === "error" && record.file !== undefined)
|
|
118
|
+
errors[record.file] = record.error ?? "";
|
|
119
|
+
else if (record.kind === "trailer")
|
|
120
|
+
sawTrailer = true;
|
|
121
|
+
};
|
|
122
|
+
child.stdout.setEncoding("utf8");
|
|
123
|
+
child.stdout.on("data", (chunk) => {
|
|
124
|
+
pending += chunk;
|
|
125
|
+
let newline = pending.indexOf("\n");
|
|
126
|
+
while (newline !== -1) {
|
|
127
|
+
take(pending.slice(0, newline));
|
|
128
|
+
pending = pending.slice(newline + 1);
|
|
129
|
+
newline = pending.indexOf("\n");
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
child.stderr.setEncoding("utf8");
|
|
133
|
+
child.stderr.on("data", (chunk) => {
|
|
134
|
+
stderr += chunk;
|
|
135
|
+
});
|
|
136
|
+
child.on("error", (error) => finish({ failure: { kind: "spawn-failed", detail: error.message } }));
|
|
137
|
+
child.on("close", (code) => {
|
|
138
|
+
take(pending);
|
|
139
|
+
// The timeout is reported ahead of the non-zero exit it causes, because
|
|
140
|
+
// "we killed it" and "it failed" are different statements to the reader.
|
|
141
|
+
if (timedOut)
|
|
142
|
+
finish({ failure: { kind: "timed-out", afterMs: timeoutMs, linesRead: lines } });
|
|
143
|
+
else if (code !== 0)
|
|
144
|
+
finish({ failure: { kind: "nonzero-exit", code: code ?? -1, stderr } });
|
|
145
|
+
else if (malformed !== undefined)
|
|
146
|
+
finish({ failure: { kind: "malformed", detail: malformed } });
|
|
147
|
+
else if (!sawTrailer)
|
|
148
|
+
finish({ failure: { kind: "truncated", linesRead: lines } });
|
|
149
|
+
else
|
|
150
|
+
finish({ parsed: { pythonVersion, files, errors } });
|
|
151
|
+
});
|
|
152
|
+
child.stdin.end(input);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function run(command, args, input, signal) {
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
const child = execFile(command, [...args],
|
|
158
|
+
// A repository large enough to overflow this has other problems, but the
|
|
159
|
+
// failure would be a truncated JSON parse with no explanation, so it is
|
|
160
|
+
// bounded explicitly rather than left at the 1 MB default.
|
|
161
|
+
{ maxBuffer: 256 * 1024 * 1024, signal }, (error, stdout, stderr) => {
|
|
162
|
+
// `ExecException.code` is `string | number`: an exit status when the
|
|
163
|
+
// process ran, an errno like ENOENT when it never started. Both mean
|
|
164
|
+
// "not zero" here, and collapsing them is deliberate — the caller
|
|
165
|
+
// distinguishes them by whether stdout parses, not by the code.
|
|
166
|
+
resolve({ code: error === null ? 0 : 1, stdout, stderr });
|
|
167
|
+
});
|
|
168
|
+
child.stdin?.end(input);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async function findInterpreter(preferred, signal) {
|
|
172
|
+
for (const command of preferred === undefined ? INTERPRETERS : [preferred]) {
|
|
173
|
+
const result = await run(command, ["-c", "import sys;print('.'.join(map(str,sys.version_info[:3])))"], "", signal);
|
|
174
|
+
const version = result.stdout.trim();
|
|
175
|
+
// Python 2 cannot run the extractor and its `ast` is a different module.
|
|
176
|
+
if (result.code === 0 && /^3\.\d+/.test(version))
|
|
177
|
+
return { command, version };
|
|
178
|
+
}
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
export function createPythonAdapter(options = {}) {
|
|
182
|
+
const globs = options.globs ?? DEFAULT_PYTHON_GLOBS;
|
|
183
|
+
const ceiling = options.maxResolution ?? 3;
|
|
184
|
+
let session;
|
|
185
|
+
let interpreter;
|
|
186
|
+
const filesUnder = (root) => discover(root.absolutePath, globs).filter((file) => !file.includes("/site-packages/"));
|
|
187
|
+
return {
|
|
188
|
+
id: ADAPTER_ID,
|
|
189
|
+
version: ADAPTER_VERSION,
|
|
190
|
+
async detect(root) {
|
|
191
|
+
const files = filesUnder(root);
|
|
192
|
+
const evidence = [];
|
|
193
|
+
const found = await findInterpreter(options.python, new AbortController().signal);
|
|
194
|
+
if (found === undefined) {
|
|
195
|
+
// A hard stop, not a degradation. DEC-062 takes the interpreter as a
|
|
196
|
+
// dependency of the adapter, and an absent one has to say so — a run
|
|
197
|
+
// that produced nothing must never look like a repository with nothing
|
|
198
|
+
// in it.
|
|
199
|
+
return {
|
|
200
|
+
detected: false,
|
|
201
|
+
roots: [],
|
|
202
|
+
evidence: [
|
|
203
|
+
`no Python 3 interpreter on PATH (tried ${INTERPRETERS.join(", ")}). This adapter ` +
|
|
204
|
+
"parses with Python's own ast (DEC-062) and cannot run without one.",
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
interpreter = found;
|
|
209
|
+
evidence.push(`${found.command} ${found.version}`);
|
|
210
|
+
if (files.length === 0) {
|
|
211
|
+
return { detected: false, roots: [], evidence: [...evidence, "no .py files found"] };
|
|
212
|
+
}
|
|
213
|
+
evidence.push(`${files.length} Python file(s)`);
|
|
214
|
+
// A repository that changed its collection rules must see that we read
|
|
215
|
+
// them. A silently-applied non-default rule is indistinguishable from a
|
|
216
|
+
// silently-ignored one when the numbers come out wrong.
|
|
217
|
+
const pytest = readPytestConfig(root.absolutePath);
|
|
218
|
+
if (pytest !== PYTEST_DEFAULTS)
|
|
219
|
+
evidence.push(pytest.source);
|
|
220
|
+
for (const marker of ["pyproject.toml", "setup.cfg", "setup.py", "requirements.txt"]) {
|
|
221
|
+
if (existsSync(join(root.absolutePath, marker)))
|
|
222
|
+
evidence.push(`${marker} present`);
|
|
223
|
+
}
|
|
224
|
+
const project = projectName(root.absolutePath);
|
|
225
|
+
evidence.push(project.evidence);
|
|
226
|
+
const roots = [
|
|
227
|
+
...new Set(files.map((file) => file.split("/")[0]).filter((p) => p !== undefined)),
|
|
228
|
+
].sort();
|
|
229
|
+
return { detected: true, roots, evidence };
|
|
230
|
+
},
|
|
231
|
+
capabilities() {
|
|
232
|
+
return {
|
|
233
|
+
maxResolution: ceiling,
|
|
234
|
+
/**
|
|
235
|
+
* What this adapter can produce, and by omission what it cannot.
|
|
236
|
+
*
|
|
237
|
+
* `MODEL` is present because a framework extractor is now bound and can
|
|
238
|
+
* earn it from evidence — a declarative base, a table binding, a column
|
|
239
|
+
* declaration, all written in the source. It is not present because the
|
|
240
|
+
* corpus holds a class called `Order`: DEC-043's constraint still stands,
|
|
241
|
+
* and `src/orm.ts` contains no name rule of any kind.
|
|
242
|
+
*
|
|
243
|
+
* `API_ROUTE` and `API_ENDPOINT` are present as of DEC-099: FastAPI,
|
|
244
|
+
* Flask and Django all *declare* their routes, and reading a declaration
|
|
245
|
+
* is not inference. The route extractor admits a route only where its
|
|
246
|
+
* receiver resolves to a router constructed from a web framework's own
|
|
247
|
+
* module — never from an attribute spelled like an HTTP verb, which on
|
|
248
|
+
* saleor would have invented 1,150 routes out of `unittest.mock.patch`.
|
|
249
|
+
*
|
|
250
|
+
* `DTO` joined them under DEC-100. It needs a transport boundary, and
|
|
251
|
+
* DEC-043 was right that Python does not supply one — but Pydantic
|
|
252
|
+
* does: a base chain reaching `BaseModel` imported from `pydantic` is a
|
|
253
|
+
* class declaring itself a validated boundary object, enforced at
|
|
254
|
+
* runtime by the framework. Same standard as `MODEL`, different
|
|
255
|
+
* evidence. This is the first *language* adapter to earn `DTO`;
|
|
256
|
+
* `adapter-openapi` needed a published contract for it.
|
|
257
|
+
*
|
|
258
|
+
* `COMPONENT` remains absent. Nothing SQLAlchemy, Django, Pydantic or a
|
|
259
|
+
* route decorator writes down says anything about a UI.
|
|
260
|
+
*/
|
|
261
|
+
nodeTypes: [
|
|
262
|
+
"MODULE",
|
|
263
|
+
"CLASS",
|
|
264
|
+
"MODEL",
|
|
265
|
+
"DTO",
|
|
266
|
+
"DATABASE_TABLE",
|
|
267
|
+
"DATABASE_COLUMN",
|
|
268
|
+
"FUNCTION",
|
|
269
|
+
"TEST_CASE",
|
|
270
|
+
"API_ROUTE",
|
|
271
|
+
"API_ENDPOINT",
|
|
272
|
+
],
|
|
273
|
+
edgeTypes: [
|
|
274
|
+
"IMPORTS",
|
|
275
|
+
"CALLS",
|
|
276
|
+
"TESTS",
|
|
277
|
+
"INHERITS",
|
|
278
|
+
"USES_TYPE",
|
|
279
|
+
"READS",
|
|
280
|
+
"WRITES",
|
|
281
|
+
"RETURNS",
|
|
282
|
+
"SERVES_API",
|
|
283
|
+
"USES_API",
|
|
284
|
+
],
|
|
285
|
+
hasCallHierarchy: false,
|
|
286
|
+
hasTypeHierarchy: false,
|
|
287
|
+
/**
|
|
288
|
+
* Directories this ecosystem installs dependencies or build output into —
|
|
289
|
+
* DEC-110's end state. The engine used to hold `node_modules`/`vendor`/`target`
|
|
290
|
+
* in a hardcoded list; it holds none of them now and consumes this instead.
|
|
291
|
+
*
|
|
292
|
+
* `filesUnder()` already filters `/site-packages/` out of every discovered file.
|
|
293
|
+
* `.venv` and `.tox` are dotted and structural; the undotted `venv` spelling is
|
|
294
|
+
* not, so it is declared.
|
|
295
|
+
*
|
|
296
|
+
* Names only — the walk matches one path component, so a path or glob matches
|
|
297
|
+
* nothing. Nothing dotted — those are skipped structurally already.
|
|
298
|
+
*/
|
|
299
|
+
noiseDirectories: ["site-packages", "__pycache__", "venv"],
|
|
300
|
+
/**
|
|
301
|
+
* Named per framework rather than per language, because that is the unit
|
|
302
|
+
* the work actually comes in. SQLAlchemy and Django disagree about the
|
|
303
|
+
* default nullability of a column — SQLAlchemy 1.x says nullable, Django
|
|
304
|
+
* says not — so neither rule could be written once for "Python".
|
|
305
|
+
*/
|
|
306
|
+
frameworkExtractors: [
|
|
307
|
+
"sqlalchemy",
|
|
308
|
+
"django",
|
|
309
|
+
"djangorestframework",
|
|
310
|
+
"fastapi",
|
|
311
|
+
"flask",
|
|
312
|
+
"odoo",
|
|
313
|
+
// GraphQL root operations, `method: "GRAPHQL"`. Rides `route-extractor`
|
|
314
|
+
// exactly as gRPC rides Go's — a new query over the existing edges,
|
|
315
|
+
// not a new capability tag.
|
|
316
|
+
"strawberry",
|
|
317
|
+
"pydantic",
|
|
318
|
+
"requests",
|
|
319
|
+
"httpx",
|
|
320
|
+
"aiohttp",
|
|
321
|
+
],
|
|
322
|
+
// The only adapter supplying all three: fastapi/flask/django/odoo
|
|
323
|
+
// routes, DRF's router.register action table, sqlalchemy/django
|
|
324
|
+
// models, requests/httpx/aiohttp calls.
|
|
325
|
+
frameworkCapabilities: ["route-extractor", "orm-extractor", "client-call-extractor"],
|
|
326
|
+
nodeIdentityFallback: {
|
|
327
|
+
appliesTo: ["MODULE", "CLASS", "MODEL", "FUNCTION", "TEST_CASE"],
|
|
328
|
+
reason: "a Python module is identified by its file path — the module path IS the path, as a " +
|
|
329
|
+
"Java class's qualified name contains its package. Moving a file therefore changes " +
|
|
330
|
+
"the node ids of everything it declares, and §11.11's rename pass has to carry them " +
|
|
331
|
+
"across. Required to be declared by DEC-004 rather than assumed.",
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
},
|
|
335
|
+
async prepare(ctx) {
|
|
336
|
+
const files = filesUnder(ctx.root);
|
|
337
|
+
const project = projectName(ctx.root.absolutePath);
|
|
338
|
+
if (interpreter === undefined) {
|
|
339
|
+
interpreter = await findInterpreter(options.python, ctx.signal);
|
|
340
|
+
}
|
|
341
|
+
if (interpreter === undefined || files.length === 0) {
|
|
342
|
+
session = {
|
|
343
|
+
root: ctx.root,
|
|
344
|
+
files,
|
|
345
|
+
parsed: undefined,
|
|
346
|
+
reached: 0,
|
|
347
|
+
packageName: project.name,
|
|
348
|
+
// No files is not a failure; no interpreter is.
|
|
349
|
+
...(interpreter === undefined && files.length > 0
|
|
350
|
+
? { failure: { kind: "no-interpreter" } }
|
|
351
|
+
: {}),
|
|
352
|
+
};
|
|
353
|
+
return 0;
|
|
354
|
+
}
|
|
355
|
+
const outcome = await runStreaming(interpreter.command, [EXTRACTOR], JSON.stringify({
|
|
356
|
+
root: ctx.root.absolutePath,
|
|
357
|
+
files,
|
|
358
|
+
// The Python side records calls at depth 1 by default, which is right
|
|
359
|
+
// for declarative configuration and wrong for the caller half: an
|
|
360
|
+
// HTTP call lives in a method. Naming *which* packages make a file
|
|
361
|
+
// worth full-depth recording is framework knowledge and stays here;
|
|
362
|
+
// the extractor only knows how to record a call.
|
|
363
|
+
deepCallModules: [...CLIENT_MODULES],
|
|
364
|
+
}), ctx.signal,
|
|
365
|
+
// **Measured, not chosen.** Across the 21-repository sweep the slowest
|
|
366
|
+
// observed full-repository extraction was posthog at 25 ms per file
|
|
367
|
+
// (462 s over 18,479 files); strapi ran at 7.6 ms and cal.com at 4.9 ms.
|
|
368
|
+
// The budget is four times the worst observed rate with a one-minute
|
|
369
|
+
// floor, so an ordinary repository can never trip it and a genuinely
|
|
370
|
+
// stuck one is bounded.
|
|
371
|
+
Math.max(ctx.prepareTimeoutMs, 60_000, files.length * 100));
|
|
372
|
+
// R0 needs no imports resolved, R1 upward does — and every level this
|
|
373
|
+
// adapter offers rests on the same single parse, so there is no cheaper
|
|
374
|
+
// rung to fall back to between 0 and the ceiling.
|
|
375
|
+
const reached = outcome.parsed === undefined ? 0 : ceiling;
|
|
376
|
+
session = {
|
|
377
|
+
root: ctx.root,
|
|
378
|
+
files,
|
|
379
|
+
parsed: outcome.parsed,
|
|
380
|
+
reached,
|
|
381
|
+
packageName: project.name,
|
|
382
|
+
...(outcome.failure === undefined ? {} : { failure: outcome.failure }),
|
|
383
|
+
};
|
|
384
|
+
return reached;
|
|
385
|
+
},
|
|
386
|
+
async parseFiles(files) {
|
|
387
|
+
// Independent of `prepare` by contract: R0 must be achievable with no
|
|
388
|
+
// dependencies present. The tree is the parsed record when one exists and
|
|
389
|
+
// an empty placeholder otherwise, because the caller owns the file list.
|
|
390
|
+
const parsed = session?.parsed;
|
|
391
|
+
return files.map((file) => ({
|
|
392
|
+
file,
|
|
393
|
+
tree: parsed?.files[file.path] ?? {},
|
|
394
|
+
}));
|
|
395
|
+
},
|
|
396
|
+
extractNodes() {
|
|
397
|
+
return current().nodes;
|
|
398
|
+
},
|
|
399
|
+
extractEdges() {
|
|
400
|
+
return current().edges;
|
|
401
|
+
},
|
|
402
|
+
async emit(ctx) {
|
|
403
|
+
const reached = await this.prepare(ctx);
|
|
404
|
+
const active = session;
|
|
405
|
+
const extraction = current();
|
|
406
|
+
const errors = active.parsed?.errors ?? {};
|
|
407
|
+
const skippedFiles = Object.entries(errors).map(([file, message]) => ({
|
|
408
|
+
file,
|
|
409
|
+
reason: skipReasonOf(message),
|
|
410
|
+
detail: message,
|
|
411
|
+
}));
|
|
412
|
+
const skippedSet = new Set(Object.keys(errors));
|
|
413
|
+
// **An empty graph over a non-empty repository is never returned silently.**
|
|
414
|
+
//
|
|
415
|
+
// This is the rule that was missing. home-assistant-core and posthog each
|
|
416
|
+
// returned 0 nodes over ~18,000 files and reported a successful R0 run,
|
|
417
|
+
// because a transport failure and "this repository has nothing in it" were
|
|
418
|
+
// indistinguishable downstream. Degrading to R0 is legitimate; saying
|
|
419
|
+
// nothing about it is not — honest degradation requires the fallback to be
|
|
420
|
+
// labelled, and an unlabelled one is a wrong answer wearing a right one's
|
|
421
|
+
// clothes.
|
|
422
|
+
const degraded = extraction.nodes.length === 0 && active.files.length > 0
|
|
423
|
+
? degradedGraph({
|
|
424
|
+
repo: ctx.root.repo,
|
|
425
|
+
workspace: ctx.root.workspace,
|
|
426
|
+
producedBy: PRODUCED_BY,
|
|
427
|
+
reached,
|
|
428
|
+
packageName: active.packageName,
|
|
429
|
+
files: active.files,
|
|
430
|
+
cause: active.failure === undefined
|
|
431
|
+
? "the extractor returned no records and gave no reason"
|
|
432
|
+
: describeFailure(active.failure),
|
|
433
|
+
})
|
|
434
|
+
: undefined;
|
|
435
|
+
return {
|
|
436
|
+
repo: ctx.root.repo,
|
|
437
|
+
...(ctx.root.workspace === undefined ? {} : { workspace: ctx.root.workspace }),
|
|
438
|
+
commitSha: ctx.root.commitSha,
|
|
439
|
+
producedBy: PRODUCED_BY,
|
|
440
|
+
sourceFiles: active.files.filter((f) => !skippedSet.has(f)),
|
|
441
|
+
skippedFiles,
|
|
442
|
+
reachedResolution: reached,
|
|
443
|
+
nodes: degraded?.nodes ?? extraction.nodes,
|
|
444
|
+
edges: degraded?.edges ?? extraction.edges,
|
|
445
|
+
unresolved: degraded?.unresolved ?? extraction.unresolved,
|
|
446
|
+
};
|
|
447
|
+
},
|
|
448
|
+
async dispose() {
|
|
449
|
+
session = undefined;
|
|
450
|
+
interpreter = undefined;
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
function current() {
|
|
454
|
+
const active = session;
|
|
455
|
+
if (active?.parsed === undefined)
|
|
456
|
+
return { nodes: [], edges: [], unresolved: [] };
|
|
457
|
+
return extract({
|
|
458
|
+
repo: active.root.repo,
|
|
459
|
+
workspace: active.root.workspace,
|
|
460
|
+
producedBy: PRODUCED_BY,
|
|
461
|
+
reached: active.reached,
|
|
462
|
+
packageName: active.packageName,
|
|
463
|
+
parsed: active.parsed,
|
|
464
|
+
// The repository's own collection rules, read from its config rather than
|
|
465
|
+
// assumed. `test_*.py` / `Test*` / `test_*` are pytest's defaults, not its
|
|
466
|
+
// definition, and a project may change all three.
|
|
467
|
+
pytest: readPytestConfig(active.root.absolutePath),
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/** Content hash of a repo-relative file, for `FileRef`. */
|
|
472
|
+
export function hashOf(root, relative) {
|
|
473
|
+
try {
|
|
474
|
+
return createHash("sha256").update(readFileSync(join(root, relative))).digest("hex").slice(0, 32);
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
return "";
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
export { LANGUAGE };
|
|
481
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|