@descryy/adapter-python 0.2.0 → 0.3.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/LICENSE +8 -0
- package/dist/adapter.d.ts +13 -20
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +91 -114
- package/dist/adapter.js.map +1 -1
- package/dist/client.d.ts +22 -41
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +35 -62
- package/dist/client.js.map +1 -1
- package/dist/extract.d.ts +16 -0
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +81 -5
- package/dist/extract.js.map +1 -1
- package/dist/extract.py +45 -4
- package/dist/graphql.d.ts +57 -123
- package/dist/graphql.d.ts.map +1 -1
- package/dist/graphql.js +62 -130
- package/dist/graphql.js.map +1 -1
- package/dist/module.d.ts +23 -42
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +26 -47
- package/dist/module.js.map +1 -1
- package/dist/orm.d.ts +31 -46
- package/dist/orm.d.ts.map +1 -1
- package/dist/orm.js +53 -89
- package/dist/orm.js.map +1 -1
- package/dist/pydantic.d.ts +76 -91
- package/dist/pydantic.d.ts.map +1 -1
- package/dist/pydantic.js +106 -99
- package/dist/pydantic.js.map +1 -1
- package/dist/routes.d.ts +46 -93
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +65 -129
- package/dist/routes.js.map +1 -1
- package/package.json +14 -5
- package/src/extract.py +45 -4
package/dist/module.d.ts
CHANGED
|
@@ -1,46 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Python module paths and import resolution — the whole of R1.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* DEC-059: the TypeScript adapter ignored the repository's path
|
|
5
|
+
* configuration, failing every aliased import and cross-directory edge with
|
|
6
|
+
* it. Python's equivalents (`src/` layouts, `PYTHONPATH`, namespace
|
|
7
|
+
* packages, `pyproject.toml`'s package table) are the same defect in a
|
|
8
|
+
* different spelling, so layout is read from the repository, not assumed,
|
|
9
|
+
* and what can't be read is disclosed.
|
|
5
10
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* ## The two hard cases, both in the corpus
|
|
14
|
-
*
|
|
15
|
-
* `from . import validate_order` — level 1, **no module name**. The dots are the
|
|
16
|
-
* whole address, and they are not recoverable from the specifier text: this is
|
|
17
|
-
* why the Python side reports `ImportFrom.level` rather than a string. It
|
|
18
|
-
* resolves to the containing package's `__init__.py`, which is the re-export hop
|
|
19
|
-
* golden pattern 03 turns on.
|
|
20
|
-
*
|
|
21
|
-
* `from .money import format_money` — level 1 with a module. Resolves to a
|
|
22
|
-
* sibling file, and the imported name is a *symbol* in it rather than a module.
|
|
23
|
-
* Deciding which of the two a name is cannot be done from the import alone —
|
|
24
|
-
* `from .pkg import mod` and `from .mod import fn` are identical in shape — so
|
|
25
|
-
* both candidates are returned and the caller picks by what exists.
|
|
11
|
+
* Two hard cases, both in the corpus: `from . import validate_order` (level
|
|
12
|
+
* 1, no module name — the dots are the whole address, resolving to the
|
|
13
|
+
* containing package's `__init__.py`, golden pattern 03's re-export hop) and
|
|
14
|
+
* `from .money import format_money` (level 1 with a module, where the name
|
|
15
|
+
* could be a submodule or a symbol — `.pkg import mod` and `.mod import fn`
|
|
16
|
+
* are identical in shape, so both candidates are returned and the caller
|
|
17
|
+
* picks by what exists).
|
|
26
18
|
*/
|
|
27
19
|
/** `src/order_service.py` -> `["src", "order_service"]`; `src/__init__.py` -> `["src"]`. */
|
|
28
20
|
export declare function modulePathOf(repoRelativeFile: string): readonly string[];
|
|
29
21
|
/**
|
|
30
|
-
* The display name of a module node.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* the symbol `src` at `src/__init__.py`, so `__init__` would fail to bind and
|
|
34
|
-
* the failure would read as a missing node rather than as a naming choice.
|
|
22
|
+
* The display name of a module node. A package takes its directory's name —
|
|
23
|
+
* the corpus binds the `barrel` role to `src` at `src/__init__.py`, so
|
|
24
|
+
* `__init__` would fail to bind and read as a missing node.
|
|
35
25
|
*/
|
|
36
26
|
export declare function moduleNameOf(repoRelativeFile: string): string;
|
|
37
27
|
/**
|
|
38
|
-
* The project's distribution name, read rather than guessed
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* purpose: a TOML dependency would buy correctness on a field nothing else in
|
|
42
|
-
* this adapter reads, and the fallback is disclosed through
|
|
43
|
-
* `capabilities().nodeIdentityFallback` either way.
|
|
28
|
+
* The project's distribution name, read rather than guessed —
|
|
29
|
+
* `pyproject.toml` then `setup.cfg`, parsed with a narrow regex since a full
|
|
30
|
+
* TOML dependency would buy correctness on a field nothing else here reads.
|
|
44
31
|
*/
|
|
45
32
|
export declare function projectName(absoluteRoot: string): {
|
|
46
33
|
name: string;
|
|
@@ -67,21 +54,15 @@ export interface ResolveOptions {
|
|
|
67
54
|
/** Repo-relative files this run analysed. Nothing outside it can be a target. */
|
|
68
55
|
readonly known: ReadonlySet<string>;
|
|
69
56
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* A `src/` layout means `from myapp.models import X` resolves under `src/`,
|
|
73
|
-
* not under the repository root. Discovered rather than assumed — see
|
|
74
|
-
* `importRoots`.
|
|
57
|
+
* Import root directories, repo-relative, longest first — a `src/` layout
|
|
58
|
+
* means `from myapp.models import X` resolves under `src/`. Discovered, not assumed; see `importRoots`.
|
|
75
59
|
*/
|
|
76
60
|
readonly roots: readonly string[];
|
|
77
61
|
}
|
|
78
62
|
/**
|
|
79
63
|
* Resolve one import to a file, and to a symbol inside it when it names one.
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* library, an installed package, a namespace package this run did not read.
|
|
83
|
-
* That is a scope boundary rather than a failure, and DEC-050 keeps the two
|
|
84
|
-
* apart in the ledger.
|
|
64
|
+
* `file: undefined` for anything outside the analysed set (stdlib, an
|
|
65
|
+
* installed package) — a scope boundary, not a failure; DEC-050 keeps the two apart in the ledger.
|
|
85
66
|
*/
|
|
86
67
|
export declare function resolveImport(record: ImportRecord, fromFile: string, options: ResolveOptions): ImportTarget;
|
|
87
68
|
/**
|
package/dist/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,4FAA4F;AAC5F,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAOxE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAuBpF;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAYD,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,GACtB,YAAY,CA0Dd;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAwCvE"}
|
package/dist/module.js
CHANGED
|
@@ -1,28 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Python module paths and import resolution — the whole of R1.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* DEC-059: the TypeScript adapter ignored the repository's path
|
|
5
|
+
* configuration, failing every aliased import and cross-directory edge with
|
|
6
|
+
* it. Python's equivalents (`src/` layouts, `PYTHONPATH`, namespace
|
|
7
|
+
* packages, `pyproject.toml`'s package table) are the same defect in a
|
|
8
|
+
* different spelling, so layout is read from the repository, not assumed,
|
|
9
|
+
* and what can't be read is disclosed.
|
|
5
10
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* ## The two hard cases, both in the corpus
|
|
14
|
-
*
|
|
15
|
-
* `from . import validate_order` — level 1, **no module name**. The dots are the
|
|
16
|
-
* whole address, and they are not recoverable from the specifier text: this is
|
|
17
|
-
* why the Python side reports `ImportFrom.level` rather than a string. It
|
|
18
|
-
* resolves to the containing package's `__init__.py`, which is the re-export hop
|
|
19
|
-
* golden pattern 03 turns on.
|
|
20
|
-
*
|
|
21
|
-
* `from .money import format_money` — level 1 with a module. Resolves to a
|
|
22
|
-
* sibling file, and the imported name is a *symbol* in it rather than a module.
|
|
23
|
-
* Deciding which of the two a name is cannot be done from the import alone —
|
|
24
|
-
* `from .pkg import mod` and `from .mod import fn` are identical in shape — so
|
|
25
|
-
* both candidates are returned and the caller picks by what exists.
|
|
11
|
+
* Two hard cases, both in the corpus: `from . import validate_order` (level
|
|
12
|
+
* 1, no module name — the dots are the whole address, resolving to the
|
|
13
|
+
* containing package's `__init__.py`, golden pattern 03's re-export hop) and
|
|
14
|
+
* `from .money import format_money` (level 1 with a module, where the name
|
|
15
|
+
* could be a submodule or a symbol — `.pkg import mod` and `.mod import fn`
|
|
16
|
+
* are identical in shape, so both candidates are returned and the caller
|
|
17
|
+
* picks by what exists).
|
|
26
18
|
*/
|
|
27
19
|
import { existsSync, readFileSync } from "node:fs";
|
|
28
20
|
import { join } from "node:path";
|
|
@@ -30,31 +22,25 @@ import { join } from "node:path";
|
|
|
30
22
|
export function modulePathOf(repoRelativeFile) {
|
|
31
23
|
const withoutExtension = repoRelativeFile.replace(/\.pyi?$/, "");
|
|
32
24
|
const parts = withoutExtension.split("/").filter((p) => p !== "" && p !== ".");
|
|
33
|
-
// A package
|
|
34
|
-
// one module,
|
|
35
|
-
// the package across two targets.
|
|
25
|
+
// A package is its directory: `src/__init__.py` and directory `src` are
|
|
26
|
+
// one module, or every import into the package would split across two targets.
|
|
36
27
|
if (parts[parts.length - 1] === "__init__")
|
|
37
28
|
parts.pop();
|
|
38
29
|
return parts;
|
|
39
30
|
}
|
|
40
31
|
/**
|
|
41
|
-
* The display name of a module node.
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* the symbol `src` at `src/__init__.py`, so `__init__` would fail to bind and
|
|
45
|
-
* the failure would read as a missing node rather than as a naming choice.
|
|
32
|
+
* The display name of a module node. A package takes its directory's name —
|
|
33
|
+
* the corpus binds the `barrel` role to `src` at `src/__init__.py`, so
|
|
34
|
+
* `__init__` would fail to bind and read as a missing node.
|
|
46
35
|
*/
|
|
47
36
|
export function moduleNameOf(repoRelativeFile) {
|
|
48
37
|
const path = modulePathOf(repoRelativeFile);
|
|
49
38
|
return path[path.length - 1] ?? "";
|
|
50
39
|
}
|
|
51
40
|
/**
|
|
52
|
-
* The project's distribution name, read rather than guessed
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* purpose: a TOML dependency would buy correctness on a field nothing else in
|
|
56
|
-
* this adapter reads, and the fallback is disclosed through
|
|
57
|
-
* `capabilities().nodeIdentityFallback` either way.
|
|
41
|
+
* The project's distribution name, read rather than guessed —
|
|
42
|
+
* `pyproject.toml` then `setup.cfg`, parsed with a narrow regex since a full
|
|
43
|
+
* TOML dependency would buy correctness on a field nothing else here reads.
|
|
58
44
|
*/
|
|
59
45
|
export function projectName(absoluteRoot) {
|
|
60
46
|
const pyproject = join(absoluteRoot, "pyproject.toml");
|
|
@@ -75,18 +61,14 @@ export function projectName(absoluteRoot) {
|
|
|
75
61
|
}
|
|
76
62
|
return {
|
|
77
63
|
name: ".",
|
|
78
|
-
//
|
|
79
|
-
// without a declared name, node identity rests on the module path alone.
|
|
64
|
+
// Same admission DEC-024 required of the TypeScript adapter.
|
|
80
65
|
evidence: "no pyproject.toml or setup.cfg declares a project name, so node identity falls back to " +
|
|
81
66
|
"the module path alone (DEC-024, DEC-054)",
|
|
82
67
|
};
|
|
83
68
|
}
|
|
84
69
|
/**
|
|
85
|
-
* Candidate files for a dotted module path, in Python's own order
|
|
86
|
-
*
|
|
87
|
-
* A package's `__init__.py` is checked before a same-named module, because that
|
|
88
|
-
* is what the interpreter does and a graph that disagrees with the interpreter
|
|
89
|
-
* is wrong regardless of which is nicer.
|
|
70
|
+
* Candidate files for a dotted module path, in Python's own order — a
|
|
71
|
+
* package's `__init__.py` before a same-named module, matching the interpreter.
|
|
90
72
|
*/
|
|
91
73
|
function candidates(parts) {
|
|
92
74
|
if (parts.length === 0)
|
|
@@ -96,11 +78,8 @@ function candidates(parts) {
|
|
|
96
78
|
}
|
|
97
79
|
/**
|
|
98
80
|
* Resolve one import to a file, and to a symbol inside it when it names one.
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* library, an installed package, a namespace package this run did not read.
|
|
102
|
-
* That is a scope boundary rather than a failure, and DEC-050 keeps the two
|
|
103
|
-
* apart in the ledger.
|
|
81
|
+
* `file: undefined` for anything outside the analysed set (stdlib, an
|
|
82
|
+
* installed package) — a scope boundary, not a failure; DEC-050 keeps the two apart in the ledger.
|
|
104
83
|
*/
|
|
105
84
|
export function resolveImport(record, fromFile, options) {
|
|
106
85
|
const miss = { file: undefined, symbol: undefined, viaPackageInit: false };
|
package/dist/module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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"}
|
package/dist/orm.d.ts
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The ORM framework extractor for Python — SQLAlchemy and Django.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* a column declaration. All three are written in the source, and this file reads
|
|
11
|
-
* them rather than inferring them.
|
|
4
|
+
* A `MODEL` is a framework claim, earned here: golden pattern 07's
|
|
5
|
+
* conformance corpus has `Order` and `CreateOrderRequest` structurally
|
|
6
|
+
* identical, so classifying between them by name would be exactly what
|
|
7
|
+
* pattern 12 punishes. The evidence is a persistence mapping — declarative
|
|
8
|
+
* base, table binding, column declaration — all written in source and read,
|
|
9
|
+
* not inferred.
|
|
12
10
|
*
|
|
13
|
-
* Nothing here is a name rule
|
|
14
|
-
* keeps one
|
|
15
|
-
* wrong
|
|
16
|
-
* a
|
|
17
|
-
*
|
|
18
|
-
* admission. A class named `Model` proves nothing.
|
|
11
|
+
* Nothing here is a name rule (unlike `isTestSuiteClass` in `extract.ts`,
|
|
12
|
+
* which keeps one deliberately since misattributing a test costs little). A
|
|
13
|
+
* wrong `MODEL` corrupts every data-propagation traversal above it, so
|
|
14
|
+
* provenance or a chain to provenance is the only admission — a class named
|
|
15
|
+
* `Model` proves nothing.
|
|
19
16
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* about what a form *means* is taken here.
|
|
17
|
+
* `extract.py` records syntactic form and names no framework; every
|
|
18
|
+
* "what does this form mean" decision is taken here.
|
|
23
19
|
*/
|
|
24
20
|
/** One assignment in a class body, as `extract.py` recorded it. */
|
|
25
21
|
export interface PyAttribute {
|
|
@@ -49,50 +45,39 @@ export interface OrmShape {
|
|
|
49
45
|
readonly table: string | null;
|
|
50
46
|
readonly fields: readonly OrmField[];
|
|
51
47
|
/**
|
|
52
|
-
* Fields whose nullability
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* is a stated answer this reader failed to read, and letting the framework
|
|
56
|
-
* default stand in for it would report the opposite of the source as fact.
|
|
48
|
+
* Fields whose nullability (e.g. `nullable=FLAG`) can't be evaluated.
|
|
49
|
+
* Listed, not guessed or dropped — the framework default standing in
|
|
50
|
+
* would report the opposite of the source as fact.
|
|
57
51
|
*/
|
|
58
52
|
readonly undecided: readonly string[];
|
|
59
53
|
}
|
|
60
54
|
/**
|
|
61
|
-
* Modules
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* is why a model is recognisable at R0. Only the cross-file *chain* — a project's
|
|
65
|
-
* own `class BaseModel(Base)` — needs imports resolved.
|
|
55
|
+
* Modules that make the framework certain if a base class came from one.
|
|
56
|
+
* Written in the import statement, needing no resolution — a model is
|
|
57
|
+
* recognisable at R0. Only the cross-file chain (`class BaseModel(Base)`) needs imports resolved.
|
|
66
58
|
*/
|
|
67
59
|
export declare const ORM_BASE_MODULES: Readonly<Record<OrmFramework, ReadonlySet<string>>>;
|
|
68
60
|
/**
|
|
69
|
-
* Calls that
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* at all. Without this rule every 1.x model in existence is invisible.
|
|
61
|
+
* Calls that produce a declarative base rather than being one. SQLAlchemy
|
|
62
|
+
* 1.x writes `Base = declarative_base()` at module level, so the base is a
|
|
63
|
+
* name bound to a call result, not a class declaration — without this rule
|
|
64
|
+
* every 1.x model is invisible.
|
|
74
65
|
*/
|
|
75
66
|
export declare const DECLARATIVE_BASE_FACTORIES: ReadonlySet<string>;
|
|
76
67
|
/**
|
|
77
|
-
* Is this attribute a mapped column, and by which framework's rule?
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* miss `JSONField`, `ArrayField` and every custom field a project writes. The
|
|
83
|
-
* receiver still has to be the imported `models` module, so this is not a name
|
|
84
|
-
* heuristic — the provenance is checked by the caller before this is consulted.
|
|
68
|
+
* Is this attribute a mapped column, and by which framework's rule? Django's
|
|
69
|
+
* rule is shape-based, not a name list — every field class in
|
|
70
|
+
* `django.db.models` ends in `Field`, and a closed list would go stale and
|
|
71
|
+
* miss `JSONField`/`ArrayField`/custom fields. Provenance is still checked
|
|
72
|
+
* by the caller first, so this isn't a name heuristic.
|
|
85
73
|
*/
|
|
86
74
|
export declare function isColumnCall(attribute: PyAttribute, framework: OrmFramework): boolean;
|
|
87
75
|
/** A base class name that is the framework's own, rather than a project's. */
|
|
88
76
|
export declare function isFrameworkBaseName(name: string, framework: OrmFramework): boolean;
|
|
89
77
|
/**
|
|
90
|
-
* The mapped shape of a class already established to belong to `framework
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* keeps a declarative base out of the graph as a model of its own: `class
|
|
94
|
-
* Base(DeclarativeBase): pass` inherits the provenance and maps nothing, and a
|
|
95
|
-
* `MODEL` with no fields is a claim about a table that does not exist.
|
|
78
|
+
* The mapped shape of a class already established to belong to `framework`,
|
|
79
|
+
* or `null` when it declares no column — keeps a bare declarative base
|
|
80
|
+
* (`class Base(DeclarativeBase): pass`) out of the graph as a model of its own.
|
|
96
81
|
*/
|
|
97
82
|
export declare function ormShapeOf(input: {
|
|
98
83
|
readonly framework: OrmFramework;
|
package/dist/orm.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orm.d.ts","sourceRoot":"","sources":["../src/orm.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"orm.d.ts","sourceRoot":"","sources":["../src/orm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,mEAAmE;AACnE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,iFAAiF;IACjF,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEnD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,EAAE,UAAU,GAAG,YAAY,GAAG,mBAAmB,CAAC;CACxE;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAQhF,CAAC;AAWF;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,MAAM,CAIzD,CAAC;AAmCH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,GAAG,OAAO,CAMrF;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,GAAG,OAAO,CAElF;AAuDD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,WAAW,EAAE,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,CAAC;CACvC,GAAG,QAAQ,GAAG,IAAI,CAoClB"}
|
package/dist/orm.js
CHANGED
|
@@ -1,32 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The ORM framework extractor for Python — SQLAlchemy and Django.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* a column declaration. All three are written in the source, and this file reads
|
|
11
|
-
* them rather than inferring them.
|
|
4
|
+
* A `MODEL` is a framework claim, earned here: golden pattern 07's
|
|
5
|
+
* conformance corpus has `Order` and `CreateOrderRequest` structurally
|
|
6
|
+
* identical, so classifying between them by name would be exactly what
|
|
7
|
+
* pattern 12 punishes. The evidence is a persistence mapping — declarative
|
|
8
|
+
* base, table binding, column declaration — all written in source and read,
|
|
9
|
+
* not inferred.
|
|
12
10
|
*
|
|
13
|
-
* Nothing here is a name rule
|
|
14
|
-
* keeps one
|
|
15
|
-
* wrong
|
|
16
|
-
* a
|
|
17
|
-
*
|
|
18
|
-
* admission. A class named `Model` proves nothing.
|
|
11
|
+
* Nothing here is a name rule (unlike `isTestSuiteClass` in `extract.ts`,
|
|
12
|
+
* which keeps one deliberately since misattributing a test costs little). A
|
|
13
|
+
* wrong `MODEL` corrupts every data-propagation traversal above it, so
|
|
14
|
+
* provenance or a chain to provenance is the only admission — a class named
|
|
15
|
+
* `Model` proves nothing.
|
|
19
16
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* about what a form *means* is taken here.
|
|
17
|
+
* `extract.py` records syntactic form and names no framework; every
|
|
18
|
+
* "what does this form mean" decision is taken here.
|
|
23
19
|
*/
|
|
24
20
|
/**
|
|
25
|
-
* Modules
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* is why a model is recognisable at R0. Only the cross-file *chain* — a project's
|
|
29
|
-
* own `class BaseModel(Base)` — needs imports resolved.
|
|
21
|
+
* Modules that make the framework certain if a base class came from one.
|
|
22
|
+
* Written in the import statement, needing no resolution — a model is
|
|
23
|
+
* recognisable at R0. Only the cross-file chain (`class BaseModel(Base)`) needs imports resolved.
|
|
30
24
|
*/
|
|
31
25
|
export const ORM_BASE_MODULES = {
|
|
32
26
|
sqlalchemy: new Set([
|
|
@@ -39,20 +33,17 @@ export const ORM_BASE_MODULES = {
|
|
|
39
33
|
};
|
|
40
34
|
/** The framework's own base class names. A project's own base reaches these by chain. */
|
|
41
35
|
const ORM_BASE_NAMES = {
|
|
42
|
-
// `Base` is deliberately absent
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// something else is free to be called `Base`. The 1.x shape is reached through
|
|
46
|
-
// `DECLARATIVE_BASE_FACTORIES` instead, which is provenance.
|
|
36
|
+
// `Base` is deliberately absent — almost every SQLAlchemy project calls its
|
|
37
|
+
// declarative base that, so admitting it would be a name rule in disguise.
|
|
38
|
+
// The 1.x shape is reached through `DECLARATIVE_BASE_FACTORIES` instead.
|
|
47
39
|
sqlalchemy: new Set(["DeclarativeBase"]),
|
|
48
40
|
django: new Set(["Model"]),
|
|
49
41
|
};
|
|
50
42
|
/**
|
|
51
|
-
* Calls that
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* at all. Without this rule every 1.x model in existence is invisible.
|
|
43
|
+
* Calls that produce a declarative base rather than being one. SQLAlchemy
|
|
44
|
+
* 1.x writes `Base = declarative_base()` at module level, so the base is a
|
|
45
|
+
* name bound to a call result, not a class declaration — without this rule
|
|
46
|
+
* every 1.x model is invisible.
|
|
56
47
|
*/
|
|
57
48
|
export const DECLARATIVE_BASE_FACTORIES = new Set([
|
|
58
49
|
"declarative_base",
|
|
@@ -62,16 +53,10 @@ export const DECLARATIVE_BASE_FACTORIES = new Set([
|
|
|
62
53
|
/** Column constructors, per framework. */
|
|
63
54
|
const COLUMN_CALLS = {
|
|
64
55
|
sqlalchemy: new Set(["Column", "mapped_column", "deferred", "synonym_for"]),
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* Across django's own tree `ForeignKey` is the second most common field
|
|
70
|
-
* constructor in existence, 914 uses against `CharField`'s 1,226, and the
|
|
71
|
-
* shape rule alone missed every one of them because it is the one field class
|
|
72
|
-
* Django did not suffix. `GenericForeignKey` and `GenericRelation` came out of
|
|
73
|
-
* the same count at 59 and 46.
|
|
74
|
-
*/
|
|
56
|
+
// Django's relational fields, counted not guessed: `ForeignKey` is the
|
|
57
|
+
// second most common field constructor in django's own tree (914 uses vs
|
|
58
|
+
// `CharField`'s 1,226) and the one class Django didn't suffix `Field`, so
|
|
59
|
+
// the shape rule alone missed it (`GenericForeignKey`/`GenericRelation`: 59, 46).
|
|
75
60
|
django: new Set([
|
|
76
61
|
"ForeignKey",
|
|
77
62
|
"OneToOneField",
|
|
@@ -81,11 +66,9 @@ const COLUMN_CALLS = {
|
|
|
81
66
|
]),
|
|
82
67
|
};
|
|
83
68
|
/**
|
|
84
|
-
* Attribute names that are configuration
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* is a field, and reporting them as fields would put two members in every model
|
|
88
|
-
* that no query can select.
|
|
69
|
+
* Attribute names that are configuration, not columns — `__tablename__` is
|
|
70
|
+
* the table binding, `__table_args__` its options; reporting either as a
|
|
71
|
+
* field would put an unselectable member in every model.
|
|
89
72
|
*/
|
|
90
73
|
const NOT_A_FIELD = new Set([
|
|
91
74
|
"__tablename__",
|
|
@@ -97,14 +80,11 @@ const NOT_A_FIELD = new Set([
|
|
|
97
80
|
]);
|
|
98
81
|
const leafOf = (dotted) => dotted === null ? "" : (dotted.split(".").pop() ?? "");
|
|
99
82
|
/**
|
|
100
|
-
* Is this attribute a mapped column, and by which framework's rule?
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* miss `JSONField`, `ArrayField` and every custom field a project writes. The
|
|
106
|
-
* receiver still has to be the imported `models` module, so this is not a name
|
|
107
|
-
* heuristic — the provenance is checked by the caller before this is consulted.
|
|
83
|
+
* Is this attribute a mapped column, and by which framework's rule? Django's
|
|
84
|
+
* rule is shape-based, not a name list — every field class in
|
|
85
|
+
* `django.db.models` ends in `Field`, and a closed list would go stale and
|
|
86
|
+
* miss `JSONField`/`ArrayField`/custom fields. Provenance is still checked
|
|
87
|
+
* by the caller first, so this isn't a name heuristic.
|
|
108
88
|
*/
|
|
109
89
|
export function isColumnCall(attribute, framework) {
|
|
110
90
|
if (attribute.callee === null)
|
|
@@ -121,13 +101,9 @@ export function isFrameworkBaseName(name, framework) {
|
|
|
121
101
|
return ORM_BASE_NAMES[framework].has(leafOf(name));
|
|
122
102
|
}
|
|
123
103
|
/**
|
|
124
|
-
* Nullability for one mapped column.
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* keyword is the source's answer and wins outright. A primary key is not
|
|
128
|
-
* nullable — that is SQL, not a convention. SQLAlchemy 2.0 then derives
|
|
129
|
-
* nullability from the `Mapped[...]` annotation, and only when none of those
|
|
130
|
-
* spoke does the framework default apply.
|
|
104
|
+
* Nullability for one mapped column. Order is the framework's own: explicit
|
|
105
|
+
* keyword wins, a primary key is never nullable (SQL, not convention), then
|
|
106
|
+
* SQLAlchemy 2.0's `Mapped[...]` annotation, and only then the framework default.
|
|
131
107
|
*/
|
|
132
108
|
function nullabilityOf(attribute, framework) {
|
|
133
109
|
const keyword = framework === "django" ? "null" : "nullable";
|
|
@@ -141,21 +117,18 @@ function nullabilityOf(attribute, framework) {
|
|
|
141
117
|
return { name: attribute.name, nullable: false, nullableFrom: "declared" };
|
|
142
118
|
}
|
|
143
119
|
if (framework === "django") {
|
|
144
|
-
// Django's documented default
|
|
145
|
-
// unless the model says otherwise.
|
|
120
|
+
// Django's only documented default: NOT NULL unless the model says otherwise.
|
|
146
121
|
return { name: attribute.name, nullable: false, nullableFrom: "framework-default" };
|
|
147
122
|
}
|
|
148
|
-
// SQLAlchemy 2.0: `Mapped[str]` is NOT NULL, `Mapped[str | None]` is
|
|
149
|
-
//
|
|
150
|
-
// 1.x-era default rather than below it.
|
|
123
|
+
// SQLAlchemy 2.0: `Mapped[str]` is NOT NULL, `Mapped[str | None]` is
|
|
124
|
+
// nullable — the annotation IS the declaration here, ranking above the 1.x default.
|
|
151
125
|
const annotation = attribute.annotation;
|
|
152
126
|
if (annotation !== null && /\bMapped\s*\[/.test(annotation)) {
|
|
153
127
|
const inner = annotation.slice(annotation.indexOf("[") + 1, annotation.lastIndexOf("]"));
|
|
154
128
|
const nullable = /\bOptional\s*\[/.test(inner) || /\bNone\b/.test(inner);
|
|
155
129
|
return { name: attribute.name, nullable, nullableFrom: "annotation" };
|
|
156
130
|
}
|
|
157
|
-
// SQLAlchemy 1.x:
|
|
158
|
-
// of Django's default, which is why neither is written once for both.
|
|
131
|
+
// SQLAlchemy 1.x: nullable unless it says otherwise — the opposite of Django's default.
|
|
159
132
|
return { name: attribute.name, nullable: true, nullableFrom: "framework-default" };
|
|
160
133
|
}
|
|
161
134
|
/** The table a model binds to, where the source names one. */
|
|
@@ -169,12 +142,9 @@ function tableOf(attributes, meta, framework) {
|
|
|
169
142
|
return null;
|
|
170
143
|
}
|
|
171
144
|
/**
|
|
172
|
-
* The mapped shape of a class already established to belong to `framework
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* keeps a declarative base out of the graph as a model of its own: `class
|
|
176
|
-
* Base(DeclarativeBase): pass` inherits the provenance and maps nothing, and a
|
|
177
|
-
* `MODEL` with no fields is a claim about a table that does not exist.
|
|
145
|
+
* The mapped shape of a class already established to belong to `framework`,
|
|
146
|
+
* or `null` when it declares no column — keeps a bare declarative base
|
|
147
|
+
* (`class Base(DeclarativeBase): pass`) out of the graph as a model of its own.
|
|
178
148
|
*/
|
|
179
149
|
export function ormShapeOf(input) {
|
|
180
150
|
const fields = [];
|
|
@@ -190,20 +160,14 @@ export function ormShapeOf(input) {
|
|
|
190
160
|
else
|
|
191
161
|
fields.push(read);
|
|
192
162
|
}
|
|
193
|
-
//
|
|
194
|
-
// frameworks', not this reader's
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
// Django has no such class in the analysed set: `models.Model` is Django's own
|
|
202
|
-
// and is never a subclass of itself. Inheriting it IS the declaration, and
|
|
203
|
-
// `class Foo(models.Model): pass` is a complete model — Django gives it an
|
|
204
|
-
// implicit primary key. Requiring a column here cost 317 real models on
|
|
205
|
-
// django's own tree, which is how the asymmetry was found rather than reasoned
|
|
206
|
-
// about.
|
|
163
|
+
// The column requirement is SQLAlchemy's alone — the asymmetry is the
|
|
164
|
+
// frameworks', not this reader's. SQLAlchemy's declarative base is a class
|
|
165
|
+
// the project writes (`class Base(DeclarativeBase): pass`), inheriting
|
|
166
|
+
// provenance while mapping nothing; without this guard it becomes a
|
|
167
|
+
// `MODEL` describing no table. Django has no such class in the analysed
|
|
168
|
+
// set — `class Foo(models.Model): pass` is a complete model with an
|
|
169
|
+
// implicit primary key, and requiring a column here cost 317 real models
|
|
170
|
+
// on django's own tree (found by measurement, not reasoned about).
|
|
207
171
|
const table = tableOf(input.attributes, input.meta, input.framework);
|
|
208
172
|
if (input.framework === "sqlalchemy" &&
|
|
209
173
|
fields.length === 0 &&
|