@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
package/dist/orm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orm.js","sourceRoot":"","sources":["../src/orm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Pydantic extractor for Python — a `DTO` and the shape it declares.
|
|
3
|
+
*
|
|
4
|
+
* **A `DTO` is a transport-boundary claim, and this file is where it is earned.**
|
|
5
|
+
* DEC-043 declined to emit one from the language adapter and was right to: the
|
|
6
|
+
* conformance corpus holds three structurally identical classes, one expected to
|
|
7
|
+
* be a `MODEL` and two `DTO`s, and nothing in Python separates them but their
|
|
8
|
+
* names. A name rule there is the thing golden pattern 12 exists to punish.
|
|
9
|
+
*
|
|
10
|
+
* Pydantic is the evidence that was missing. A class whose base chain reaches
|
|
11
|
+
* `BaseModel` **imported from `pydantic`** has declared itself a validated
|
|
12
|
+
* boundary object; the framework enforces the shape at runtime. That is a
|
|
13
|
+
* declaration, not an inference, and it is the same standard `orm.ts` holds a
|
|
14
|
+
* `MODEL` to — a declarative base and a mapped column.
|
|
15
|
+
*
|
|
16
|
+
* Until now only `adapter-openapi` declared `DTO`, and it needs a published
|
|
17
|
+
* contract. This is the first *language* adapter to earn one.
|
|
18
|
+
*
|
|
19
|
+
* ## What the shape is, measured rather than assumed
|
|
20
|
+
*
|
|
21
|
+
* §8 asked for "the validation rules a route enforces", which invites reading
|
|
22
|
+
* `ge`, `le`, `min_length` and friends. Counted across 515 models on two real
|
|
23
|
+
* repositories **before this file was written**, those total **18** — two of
|
|
24
|
+
* them on dispatch's 449 models. Ranked by that number they are not the payload.
|
|
25
|
+
*
|
|
26
|
+
* What Pydantic writes down at scale is **presence and type**: 1,409 annotated
|
|
27
|
+
* fields on dispatch, 874 of them optional. "This field is required" is a
|
|
28
|
+
* validation rule the route enforces on every request — a POST without it is a
|
|
29
|
+
* 422 — and it is stated in every model rather than in eighteen of them.
|
|
30
|
+
*
|
|
31
|
+
* So constraints are carried **where they are written** and never invented, and
|
|
32
|
+
* the capability is described by what it actually reads. `attrs.fields` is the
|
|
33
|
+
* same key `orm.ts` uses and the same key golden pattern 05 compares.
|
|
34
|
+
*
|
|
35
|
+
* ## What is deliberately not read
|
|
36
|
+
*
|
|
37
|
+
* A `@field_validator` body is arbitrary Python. 76 of them across the two
|
|
38
|
+
* repositories say something true about what the route accepts, and none of it
|
|
39
|
+
* is readable without executing the code. They are disclosed as an
|
|
40
|
+
* unreadable-constraint count on the node rather than silently treated as
|
|
41
|
+
* absent — the same distinction `orm.ts` draws with `fieldsUndecided`, and the
|
|
42
|
+
* one `nullable=FLAG` exists to protect.
|
|
43
|
+
*/
|
|
44
|
+
import type { PyAttribute } from "./orm.ts";
|
|
45
|
+
/** Modules whose serializer roots are DRF's own. */
|
|
46
|
+
export declare const DRF_SERIALIZER_MODULES: ReadonlySet<string>;
|
|
47
|
+
export declare const DRF_SERIALIZER_ROOTS: ReadonlySet<string>;
|
|
48
|
+
/** Is this base name, with this origin module, one of DRF's own serializer roots? */
|
|
49
|
+
export declare function isDrfSerializerRoot(baseName: string, originModule: string | undefined): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The shape a DRF serializer declares.
|
|
52
|
+
*
|
|
53
|
+
* Returns `null` where the class declares no field at all — the same guard
|
|
54
|
+
* `ormShapeOf` uses to keep a project's own serializer base out of the graph
|
|
55
|
+
* as a `DTO` of its own.
|
|
56
|
+
*/
|
|
57
|
+
export declare function drfShapeOf(input: {
|
|
58
|
+
readonly attributes: readonly PyAttribute[];
|
|
59
|
+
}): DtoShape | null;
|
|
60
|
+
/** Modules whose `BaseModel` is Pydantic's. A local class of that name is not. */
|
|
61
|
+
export declare const PYDANTIC_MODULES: ReadonlySet<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Bases that are Pydantic's own roots.
|
|
64
|
+
*
|
|
65
|
+
* `BaseSettings` is included and moved package in Pydantic v2 — it lives in
|
|
66
|
+
* `pydantic_settings` now and in `pydantic` before that, which is why the module
|
|
67
|
+
* set carries both. Restricting a rule to one spelling of a library's own move
|
|
68
|
+
* is the mistake DEC-069 recorded when `collections.abc.Callable` was missed.
|
|
69
|
+
*/
|
|
70
|
+
export declare const PYDANTIC_ROOTS: ReadonlySet<string>;
|
|
71
|
+
/** Constraint keywords Pydantic enforces, as opposed to documents. */
|
|
72
|
+
export declare const CONSTRAINT_KEYWORDS: ReadonlySet<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Keywords that describe rather than constrain.
|
|
75
|
+
*
|
|
76
|
+
* `description` and `title` are 189 of dispatch's 214 `Field(...)` keywords.
|
|
77
|
+
* Counting them as validation would make the capability look an order of
|
|
78
|
+
* magnitude larger than it is, which is the reporting failure this project
|
|
79
|
+
* exists to avoid.
|
|
80
|
+
*/
|
|
81
|
+
export declare const DOCUMENTATION_KEYWORDS: ReadonlySet<string>;
|
|
82
|
+
export interface DtoField {
|
|
83
|
+
readonly name: string;
|
|
84
|
+
/** The annotation as written — never evaluated. */
|
|
85
|
+
readonly type: string | null;
|
|
86
|
+
/**
|
|
87
|
+
* Must a request supply this field at all?
|
|
88
|
+
*
|
|
89
|
+
* **Distinct from `nullable`, and Pydantic is the reason.** `x: Optional[int]`
|
|
90
|
+
* with no default is *required and nullable*: the caller must pass it, and may
|
|
91
|
+
* pass null. `x: int = 5` is *optional and not nullable*. Collapsing the two
|
|
92
|
+
* into one flag loses exactly the rule a request generator needs — it would
|
|
93
|
+
* omit a required field, get a 422, and report it as a finding.
|
|
94
|
+
*
|
|
95
|
+
* This is the validation rule that is written on every one of 1,694 fields
|
|
96
|
+
* across two repositories, as against the 18 that carry a range constraint.
|
|
97
|
+
*/
|
|
98
|
+
readonly required: boolean;
|
|
99
|
+
/** May the value be null once supplied? `Optional[X]`, `X | None`. */
|
|
100
|
+
readonly nullable: boolean;
|
|
101
|
+
/** Constraints the source states, where it states any. Never invented. */
|
|
102
|
+
readonly constraints?: Readonly<Record<string, unknown>>;
|
|
103
|
+
}
|
|
104
|
+
export interface DtoShape {
|
|
105
|
+
readonly fields: readonly DtoField[];
|
|
106
|
+
/**
|
|
107
|
+
* Fields whose constraint is written in a form this reader cannot evaluate —
|
|
108
|
+
* `min_length=LIMIT`. Listed rather than dropped, for the same reason
|
|
109
|
+
* `orm.ts` lists an undecided nullability: the source answered and this
|
|
110
|
+
* reader failed to read it, which is not the same as the source being silent.
|
|
111
|
+
*/
|
|
112
|
+
readonly undecided: readonly string[];
|
|
113
|
+
/** `@field_validator` and friends: real rules, none of them statically readable. */
|
|
114
|
+
readonly unreadableValidators: number;
|
|
115
|
+
}
|
|
116
|
+
/** Is this base name, with this origin module, one of Pydantic's own roots? */
|
|
117
|
+
export declare function isPydanticRoot(baseName: string, originModule: string | undefined): boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Does this field have a default, so a request may omit it?
|
|
120
|
+
*
|
|
121
|
+
* `x: int = 5` does. `x: int` does not. The case in between is Pydantic's own
|
|
122
|
+
* spelling and it is the one that was read backwards first:
|
|
123
|
+
*
|
|
124
|
+
* hostname: str = Field(..., min_length=3)
|
|
125
|
+
*
|
|
126
|
+
* The `...` is an **explicit "no default"** — Pydantic's documented way of
|
|
127
|
+
* writing a required field that also carries constraints. Syntactically it is an
|
|
128
|
+
* assignment, so the naive reading is "has a default", and `hostname: str`
|
|
129
|
+
* carrying a `min_length` came out **nullable** — a required field reported as
|
|
130
|
+
* optional. That is the wrong direction to be wrong in: a request generator
|
|
131
|
+
* would omit it and call the resulting 422 a finding.
|
|
132
|
+
*
|
|
133
|
+
* Caught by the field's own type contradicting its verdict — `str`, not
|
|
134
|
+
* `str | None`, marked nullable — rather than by any check.
|
|
135
|
+
*/
|
|
136
|
+
export declare function hasDefault(annotated: boolean, attribute: PyAttribute | undefined): boolean;
|
|
137
|
+
/** Does an annotation admit absence — `Optional[X]`, `X | None`, `Any`? */
|
|
138
|
+
export declare function annotationIsNullable(annotation: string | null | undefined): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* The shape a Pydantic class declares.
|
|
141
|
+
*
|
|
142
|
+
* `attributes` is every assignment in the class body, as `extract.py` recorded
|
|
143
|
+
* it — the same input `orm.ts` reads, which is why this extractor cost almost
|
|
144
|
+
* nothing to add.
|
|
145
|
+
*/
|
|
146
|
+
export declare function dtoShapeOf(input: {
|
|
147
|
+
readonly attributes: readonly PyAttribute[];
|
|
148
|
+
readonly annotatedFields: readonly {
|
|
149
|
+
name: string;
|
|
150
|
+
annotation: string | null;
|
|
151
|
+
hasDefault: boolean;
|
|
152
|
+
}[];
|
|
153
|
+
readonly validatorCount: number;
|
|
154
|
+
}): DtoShape;
|
|
155
|
+
/** Decorators that declare a validation rule no static reader can evaluate. */
|
|
156
|
+
export declare function isValidatorDecorator(text: string | null): boolean;
|
|
157
|
+
//# sourceMappingURL=pydantic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pydantic.d.ts","sourceRoot":"","sources":["../src/pydantic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAe5C,oDAAoD;AACpD,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAGrD,CAAC;AAEH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAKnD,CAAC;AAEH,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAQ/F;AAkBD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,UAAU,EAAE,SAAS,WAAW,EAAE,CAAA;CAAE,GAAG,QAAQ,GAAG,IAAI,CA6ClG;AAED,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAO/C,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAK7C,CAAC;AAEH,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAclD,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAOrD,CAAC;AAEH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,oFAAoF;IACpF,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAU1F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAc1F;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAGnF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,QAAQ,CAAC,UAAU,EAAE,SAAS,WAAW,EAAE,CAAC;IAC5C,QAAQ,CAAC,eAAe,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IACtG,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC,GAAG,QAAQ,CAgDX;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAGjE"}
|
package/dist/pydantic.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Pydantic extractor for Python — a `DTO` and the shape it declares.
|
|
3
|
+
*
|
|
4
|
+
* **A `DTO` is a transport-boundary claim, and this file is where it is earned.**
|
|
5
|
+
* DEC-043 declined to emit one from the language adapter and was right to: the
|
|
6
|
+
* conformance corpus holds three structurally identical classes, one expected to
|
|
7
|
+
* be a `MODEL` and two `DTO`s, and nothing in Python separates them but their
|
|
8
|
+
* names. A name rule there is the thing golden pattern 12 exists to punish.
|
|
9
|
+
*
|
|
10
|
+
* Pydantic is the evidence that was missing. A class whose base chain reaches
|
|
11
|
+
* `BaseModel` **imported from `pydantic`** has declared itself a validated
|
|
12
|
+
* boundary object; the framework enforces the shape at runtime. That is a
|
|
13
|
+
* declaration, not an inference, and it is the same standard `orm.ts` holds a
|
|
14
|
+
* `MODEL` to — a declarative base and a mapped column.
|
|
15
|
+
*
|
|
16
|
+
* Until now only `adapter-openapi` declared `DTO`, and it needs a published
|
|
17
|
+
* contract. This is the first *language* adapter to earn one.
|
|
18
|
+
*
|
|
19
|
+
* ## What the shape is, measured rather than assumed
|
|
20
|
+
*
|
|
21
|
+
* §8 asked for "the validation rules a route enforces", which invites reading
|
|
22
|
+
* `ge`, `le`, `min_length` and friends. Counted across 515 models on two real
|
|
23
|
+
* repositories **before this file was written**, those total **18** — two of
|
|
24
|
+
* them on dispatch's 449 models. Ranked by that number they are not the payload.
|
|
25
|
+
*
|
|
26
|
+
* What Pydantic writes down at scale is **presence and type**: 1,409 annotated
|
|
27
|
+
* fields on dispatch, 874 of them optional. "This field is required" is a
|
|
28
|
+
* validation rule the route enforces on every request — a POST without it is a
|
|
29
|
+
* 422 — and it is stated in every model rather than in eighteen of them.
|
|
30
|
+
*
|
|
31
|
+
* So constraints are carried **where they are written** and never invented, and
|
|
32
|
+
* the capability is described by what it actually reads. `attrs.fields` is the
|
|
33
|
+
* same key `orm.ts` uses and the same key golden pattern 05 compares.
|
|
34
|
+
*
|
|
35
|
+
* ## What is deliberately not read
|
|
36
|
+
*
|
|
37
|
+
* A `@field_validator` body is arbitrary Python. 76 of them across the two
|
|
38
|
+
* repositories say something true about what the route accepts, and none of it
|
|
39
|
+
* is readable without executing the code. They are disclosed as an
|
|
40
|
+
* unreadable-constraint count on the node rather than silently treated as
|
|
41
|
+
* absent — the same distinction `orm.ts` draws with `fieldsUndecided`, and the
|
|
42
|
+
* one `nullable=FLAG` exists to protect.
|
|
43
|
+
*/
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// DRF's `serializers.Serializer`/`ModelSerializer` — a second, structurally
|
|
46
|
+
// different producer of the same `DtoShape` this file already earns for
|
|
47
|
+
// Pydantic.
|
|
48
|
+
//
|
|
49
|
+
// **Not an annotation shape.** Pydantic fields are `AnnAssign` —
|
|
50
|
+
// `name: str` — which is why `dtoShapeOf` reads `annotatedFields`. DRF
|
|
51
|
+
// fields are field-CONSTRUCTOR assignments — `email =
|
|
52
|
+
// serializers.EmailField()` — the same shape `orm.ts` already reads for
|
|
53
|
+
// Django's own ORM columns, and for the same reason: neither framework
|
|
54
|
+
// spells a field as a type annotation.
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
/** Modules whose serializer roots are DRF's own. */
|
|
57
|
+
export const DRF_SERIALIZER_MODULES = new Set([
|
|
58
|
+
"rest_framework.serializers",
|
|
59
|
+
"rest_framework.fields",
|
|
60
|
+
]);
|
|
61
|
+
export const DRF_SERIALIZER_ROOTS = new Set([
|
|
62
|
+
"Serializer",
|
|
63
|
+
"ModelSerializer",
|
|
64
|
+
"HyperlinkedModelSerializer",
|
|
65
|
+
"ListSerializer",
|
|
66
|
+
]);
|
|
67
|
+
/** Is this base name, with this origin module, one of DRF's own serializer roots? */
|
|
68
|
+
export function isDrfSerializerRoot(baseName, originModule) {
|
|
69
|
+
const leaf = baseName.includes(".") ? baseName.slice(baseName.lastIndexOf(".") + 1) : baseName;
|
|
70
|
+
const bare = leaf.split("[")[0];
|
|
71
|
+
if (!DRF_SERIALIZER_ROOTS.has(bare))
|
|
72
|
+
return false;
|
|
73
|
+
if (originModule === undefined)
|
|
74
|
+
return false;
|
|
75
|
+
return [...DRF_SERIALIZER_MODULES].some((module) => originModule === module || originModule.startsWith(`${module}.`));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Is this attribute a DRF field, shape-based like Django's own ORM fields —
|
|
79
|
+
* every field class DRF ships ends in `Field`, and third-party field classes
|
|
80
|
+
* follow the same convention. A closed list would miss every custom field a
|
|
81
|
+
* project writes, the same gap `isColumnCall` avoids for Django's ORM.
|
|
82
|
+
*/
|
|
83
|
+
function isDrfFieldCall(attribute) {
|
|
84
|
+
if (attribute.callee === null)
|
|
85
|
+
return false;
|
|
86
|
+
const leaf = attribute.callee.includes(".")
|
|
87
|
+
? attribute.callee.slice(attribute.callee.lastIndexOf(".") + 1)
|
|
88
|
+
: attribute.callee;
|
|
89
|
+
return leaf.endsWith("Field") && leaf.length > "Field".length;
|
|
90
|
+
}
|
|
91
|
+
const DRF_NOT_A_FIELD = new Set(["Meta"]);
|
|
92
|
+
/**
|
|
93
|
+
* The shape a DRF serializer declares.
|
|
94
|
+
*
|
|
95
|
+
* Returns `null` where the class declares no field at all — the same guard
|
|
96
|
+
* `ormShapeOf` uses to keep a project's own serializer base out of the graph
|
|
97
|
+
* as a `DTO` of its own.
|
|
98
|
+
*/
|
|
99
|
+
export function drfShapeOf(input) {
|
|
100
|
+
const fields = [];
|
|
101
|
+
const undecided = [];
|
|
102
|
+
for (const attribute of input.attributes) {
|
|
103
|
+
if (DRF_NOT_A_FIELD.has(attribute.name) || attribute.name.startsWith("__"))
|
|
104
|
+
continue;
|
|
105
|
+
if (!isDrfFieldCall(attribute))
|
|
106
|
+
continue;
|
|
107
|
+
const constraints = {};
|
|
108
|
+
let sawUnreadable = false;
|
|
109
|
+
for (const [key, value] of Object.entries(attribute.keywords)) {
|
|
110
|
+
if (CONSTRAINT_KEYWORDS.has(key))
|
|
111
|
+
constraints[key] = value;
|
|
112
|
+
}
|
|
113
|
+
for (const key of attribute.opaqueKeywords) {
|
|
114
|
+
if (CONSTRAINT_KEYWORDS.has(key))
|
|
115
|
+
sawUnreadable = true;
|
|
116
|
+
}
|
|
117
|
+
if (sawUnreadable)
|
|
118
|
+
undecided.push(attribute.name);
|
|
119
|
+
// DRF's own default: a field is required unless the source says
|
|
120
|
+
// otherwise, either with `required=False` directly or by supplying a
|
|
121
|
+
// `default=` — the mirror image of Pydantic's "has a default" trigger,
|
|
122
|
+
// read the way DRF's own field constructor documents it.
|
|
123
|
+
let required = true;
|
|
124
|
+
if (!attribute.opaqueKeywords.includes("required") && typeof attribute.keywords["required"] === "boolean") {
|
|
125
|
+
required = attribute.keywords["required"];
|
|
126
|
+
}
|
|
127
|
+
else if (attribute.keywords["default"] !== undefined || attribute.opaqueKeywords.includes("default")) {
|
|
128
|
+
required = false;
|
|
129
|
+
}
|
|
130
|
+
fields.push({
|
|
131
|
+
name: attribute.name,
|
|
132
|
+
type: attribute.callee,
|
|
133
|
+
required,
|
|
134
|
+
nullable: attribute.keywords["allow_null"] === true,
|
|
135
|
+
...(Object.keys(constraints).length > 0 ? { constraints } : {}),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (fields.length === 0 && undecided.length === 0)
|
|
139
|
+
return null;
|
|
140
|
+
return {
|
|
141
|
+
fields: fields.sort((a, b) => a.name.localeCompare(b.name)),
|
|
142
|
+
undecided: undecided.sort(),
|
|
143
|
+
unreadableValidators: 0,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** Modules whose `BaseModel` is Pydantic's. A local class of that name is not. */
|
|
147
|
+
export const PYDANTIC_MODULES = new Set([
|
|
148
|
+
"pydantic",
|
|
149
|
+
"pydantic.main",
|
|
150
|
+
"pydantic.v1",
|
|
151
|
+
"pydantic.v1.main",
|
|
152
|
+
"pydantic.generics",
|
|
153
|
+
"pydantic_settings",
|
|
154
|
+
]);
|
|
155
|
+
/**
|
|
156
|
+
* Bases that are Pydantic's own roots.
|
|
157
|
+
*
|
|
158
|
+
* `BaseSettings` is included and moved package in Pydantic v2 — it lives in
|
|
159
|
+
* `pydantic_settings` now and in `pydantic` before that, which is why the module
|
|
160
|
+
* set carries both. Restricting a rule to one spelling of a library's own move
|
|
161
|
+
* is the mistake DEC-069 recorded when `collections.abc.Callable` was missed.
|
|
162
|
+
*/
|
|
163
|
+
export const PYDANTIC_ROOTS = new Set([
|
|
164
|
+
"BaseModel",
|
|
165
|
+
"BaseSettings",
|
|
166
|
+
"RootModel",
|
|
167
|
+
"GenericModel",
|
|
168
|
+
]);
|
|
169
|
+
/** Constraint keywords Pydantic enforces, as opposed to documents. */
|
|
170
|
+
export const CONSTRAINT_KEYWORDS = new Set([
|
|
171
|
+
"gt",
|
|
172
|
+
"ge",
|
|
173
|
+
"lt",
|
|
174
|
+
"le",
|
|
175
|
+
"min_length",
|
|
176
|
+
"max_length",
|
|
177
|
+
"min_items",
|
|
178
|
+
"max_items",
|
|
179
|
+
"multiple_of",
|
|
180
|
+
"pattern",
|
|
181
|
+
"regex",
|
|
182
|
+
"max_digits",
|
|
183
|
+
"decimal_places",
|
|
184
|
+
]);
|
|
185
|
+
/**
|
|
186
|
+
* Keywords that describe rather than constrain.
|
|
187
|
+
*
|
|
188
|
+
* `description` and `title` are 189 of dispatch's 214 `Field(...)` keywords.
|
|
189
|
+
* Counting them as validation would make the capability look an order of
|
|
190
|
+
* magnitude larger than it is, which is the reporting failure this project
|
|
191
|
+
* exists to avoid.
|
|
192
|
+
*/
|
|
193
|
+
export const DOCUMENTATION_KEYWORDS = new Set([
|
|
194
|
+
"description",
|
|
195
|
+
"title",
|
|
196
|
+
"examples",
|
|
197
|
+
"example",
|
|
198
|
+
"deprecated",
|
|
199
|
+
"json_schema_extra",
|
|
200
|
+
]);
|
|
201
|
+
/** Is this base name, with this origin module, one of Pydantic's own roots? */
|
|
202
|
+
export function isPydanticRoot(baseName, originModule) {
|
|
203
|
+
const leaf = baseName.includes(".") ? baseName.slice(baseName.lastIndexOf(".") + 1) : baseName;
|
|
204
|
+
const bare = leaf.split("[")[0];
|
|
205
|
+
if (!PYDANTIC_ROOTS.has(bare))
|
|
206
|
+
return false;
|
|
207
|
+
// The module check is the whole precision control. A repository's own
|
|
208
|
+
// `BaseModel` is not Pydantic's, and matching the bare name would admit it.
|
|
209
|
+
if (originModule === undefined)
|
|
210
|
+
return false;
|
|
211
|
+
return [...PYDANTIC_MODULES].some((module) => originModule === module || originModule.startsWith(`${module}.`));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Does this field have a default, so a request may omit it?
|
|
215
|
+
*
|
|
216
|
+
* `x: int = 5` does. `x: int` does not. The case in between is Pydantic's own
|
|
217
|
+
* spelling and it is the one that was read backwards first:
|
|
218
|
+
*
|
|
219
|
+
* hostname: str = Field(..., min_length=3)
|
|
220
|
+
*
|
|
221
|
+
* The `...` is an **explicit "no default"** — Pydantic's documented way of
|
|
222
|
+
* writing a required field that also carries constraints. Syntactically it is an
|
|
223
|
+
* assignment, so the naive reading is "has a default", and `hostname: str`
|
|
224
|
+
* carrying a `min_length` came out **nullable** — a required field reported as
|
|
225
|
+
* optional. That is the wrong direction to be wrong in: a request generator
|
|
226
|
+
* would omit it and call the resulting 422 a finding.
|
|
227
|
+
*
|
|
228
|
+
* Caught by the field's own type contradicting its verdict — `str`, not
|
|
229
|
+
* `str | None`, marked nullable — rather than by any check.
|
|
230
|
+
*/
|
|
231
|
+
export function hasDefault(annotated, attribute) {
|
|
232
|
+
if (!annotated)
|
|
233
|
+
return false;
|
|
234
|
+
if (attribute === undefined || attribute.callee === null)
|
|
235
|
+
return true;
|
|
236
|
+
const callee = attribute.callee.slice(attribute.callee.lastIndexOf(".") + 1);
|
|
237
|
+
if (callee !== "Field")
|
|
238
|
+
return true; // some other call — its result is the default
|
|
239
|
+
if (attribute.keywords["default"] !== undefined)
|
|
240
|
+
return true;
|
|
241
|
+
if (attribute.opaqueKeywords.includes("default"))
|
|
242
|
+
return true;
|
|
243
|
+
if (attribute.keywords["default_factory"] !== undefined)
|
|
244
|
+
return true;
|
|
245
|
+
if (attribute.opaqueKeywords.includes("default_factory"))
|
|
246
|
+
return true;
|
|
247
|
+
const positional = attribute.args[0];
|
|
248
|
+
if (positional === undefined)
|
|
249
|
+
return false; // `Field(min_length=3)` — required
|
|
250
|
+
return positional.trim() !== "..."; // `Field(...)` — explicitly required
|
|
251
|
+
}
|
|
252
|
+
/** Does an annotation admit absence — `Optional[X]`, `X | None`, `Any`? */
|
|
253
|
+
export function annotationIsNullable(annotation) {
|
|
254
|
+
if (annotation === null || annotation === undefined)
|
|
255
|
+
return false;
|
|
256
|
+
return /\bOptional\s*\[/.test(annotation) || /\bNone\b/.test(annotation);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* The shape a Pydantic class declares.
|
|
260
|
+
*
|
|
261
|
+
* `attributes` is every assignment in the class body, as `extract.py` recorded
|
|
262
|
+
* it — the same input `orm.ts` reads, which is why this extractor cost almost
|
|
263
|
+
* nothing to add.
|
|
264
|
+
*/
|
|
265
|
+
export function dtoShapeOf(input) {
|
|
266
|
+
const byName = new Map();
|
|
267
|
+
for (const attribute of input.attributes)
|
|
268
|
+
byName.set(attribute.name, attribute);
|
|
269
|
+
const fields = [];
|
|
270
|
+
const undecided = [];
|
|
271
|
+
for (const declared of input.annotatedFields) {
|
|
272
|
+
// `model_config = ConfigDict(...)` is configuration, not a field, and it is
|
|
273
|
+
// the one class-body assignment Pydantic treats specially.
|
|
274
|
+
if (declared.name === "model_config" || declared.name.startsWith("__"))
|
|
275
|
+
continue;
|
|
276
|
+
const attribute = byName.get(declared.name);
|
|
277
|
+
const constraints = {};
|
|
278
|
+
let sawUnreadable = false;
|
|
279
|
+
if (attribute !== undefined && attribute.callee !== null) {
|
|
280
|
+
const callee = attribute.callee.slice(attribute.callee.lastIndexOf(".") + 1);
|
|
281
|
+
if (callee === "Field") {
|
|
282
|
+
for (const [key, value] of Object.entries(attribute.keywords)) {
|
|
283
|
+
if (CONSTRAINT_KEYWORDS.has(key))
|
|
284
|
+
constraints[key] = value;
|
|
285
|
+
}
|
|
286
|
+
for (const key of attribute.opaqueKeywords) {
|
|
287
|
+
// A constraint written in a form this reader cannot evaluate is the
|
|
288
|
+
// `nullable=FLAG` case: the source stated a rule and letting silence
|
|
289
|
+
// stand in for it would report the opposite of the source as fact.
|
|
290
|
+
if (CONSTRAINT_KEYWORDS.has(key))
|
|
291
|
+
sawUnreadable = true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (sawUnreadable)
|
|
296
|
+
undecided.push(declared.name);
|
|
297
|
+
const defaulted = hasDefault(declared.hasDefault, attribute);
|
|
298
|
+
fields.push({
|
|
299
|
+
name: declared.name,
|
|
300
|
+
type: declared.annotation,
|
|
301
|
+
required: !defaulted,
|
|
302
|
+
nullable: annotationIsNullable(declared.annotation),
|
|
303
|
+
...(Object.keys(constraints).length > 0 ? { constraints } : {}),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
fields: fields.sort((a, b) => a.name.localeCompare(b.name)),
|
|
308
|
+
undecided: undecided.sort(),
|
|
309
|
+
unreadableValidators: input.validatorCount,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/** Decorators that declare a validation rule no static reader can evaluate. */
|
|
313
|
+
export function isValidatorDecorator(text) {
|
|
314
|
+
if (text === null)
|
|
315
|
+
return false;
|
|
316
|
+
return /\b(field_validator|model_validator|root_validator|validator)\b/.test(text);
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=pydantic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pydantic.js","sourceRoot":"","sources":["../src/pydantic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project's own test-collection rules, read rather than assumed.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* `test_*.py`, `Test*`, `test_*` are pytest's **defaults**, and a project can
|
|
7
|
+
* change all three. Hard-coding them is the same mistake as matching a class
|
|
8
|
+
* name to decide it is a test suite: approximating a framework's rule when the
|
|
9
|
+
* framework will state it. The standing rule is *if the framework will tell you,
|
|
10
|
+
* never infer it* — and here it will, in a file already sitting in the
|
|
11
|
+
* repository.
|
|
12
|
+
*
|
|
13
|
+
* Not hypothetical: `threeclouds-ai` declares `[tool.pytest.ini_options]` with
|
|
14
|
+
* `testpaths`, and nothing read it.
|
|
15
|
+
*
|
|
16
|
+
* ## What is read, and what is deliberately not
|
|
17
|
+
*
|
|
18
|
+
* Only the three keys that decide **what is a test**: `python_files`,
|
|
19
|
+
* `python_classes`, `python_functions`. `testpaths` is *not* read: it narrows
|
|
20
|
+
* where pytest looks, and narrowing what Descry analyses on the strength of a
|
|
21
|
+
* test-runner setting would silently shrink the graph for a reason that has
|
|
22
|
+
* nothing to do with the graph.
|
|
23
|
+
*
|
|
24
|
+
* ## Precedence
|
|
25
|
+
*
|
|
26
|
+
* pytest's own order for choosing its config file, and only the first one that
|
|
27
|
+
* carries a section counts — a `pytest.ini` wins outright even when empty of
|
|
28
|
+
* these keys, exactly as it does for pytest.
|
|
29
|
+
*
|
|
30
|
+
* ## The parsing, and its honest limit
|
|
31
|
+
*
|
|
32
|
+
* Zero runtime dependencies, so there is no TOML parser here. This reads one
|
|
33
|
+
* known section and three known keys whose values are a string or an array of
|
|
34
|
+
* strings, which is the whole of what these keys can be. A key it cannot parse
|
|
35
|
+
* leaves the default in place — the failure direction is "behaves as before",
|
|
36
|
+
* never "collects something strange".
|
|
37
|
+
*/
|
|
38
|
+
export interface PytestConfig {
|
|
39
|
+
/** Globs a test file must match. pytest's default is `test_*.py`, `*_test.py`. */
|
|
40
|
+
readonly filePatterns: readonly string[];
|
|
41
|
+
/** Prefixes a collected class must carry. Default `Test`. */
|
|
42
|
+
readonly classPrefixes: readonly string[];
|
|
43
|
+
/** Prefixes a collected function must carry. Default `test`. */
|
|
44
|
+
readonly functionPrefixes: readonly string[];
|
|
45
|
+
/** Where the values came from, for the capability report. */
|
|
46
|
+
readonly source: string;
|
|
47
|
+
}
|
|
48
|
+
export declare const PYTEST_DEFAULTS: PytestConfig;
|
|
49
|
+
/** `test_*.py` -> a regex anchored at both ends. Only `*` and `?` are glob here. */
|
|
50
|
+
export declare function globToRegExp(glob: string): RegExp;
|
|
51
|
+
/**
|
|
52
|
+
* Read the collection rules a repository declares, or pytest's defaults.
|
|
53
|
+
*
|
|
54
|
+
* Order is pytest's: `pytest.ini`, then `pyproject.toml`, then `tox.ini`, then
|
|
55
|
+
* `setup.cfg`. The first file carrying the section wins outright, whether or not
|
|
56
|
+
* it sets any of the three keys — narrowing to "the first file that happens to
|
|
57
|
+
* set the key I want" would silently blend two projects' configurations.
|
|
58
|
+
*/
|
|
59
|
+
export declare function readPytestConfig(absoluteRoot: string): PytestConfig;
|
|
60
|
+
//# sourceMappingURL=pytest-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pytest-config.d.ts","sourceRoot":"","sources":["../src/pytest-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAKH,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,6DAA6D;IAC7D,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,gEAAgE;IAChE,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,eAAe,EAAE,YAK7B,CAAC;AAEF,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjD;AAiDD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,CA0CnE"}
|