@alloy-js/python 0.5.0-dev.0 → 0.5.0-dev.2
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/dev/src/builtins/python.js +46 -0
- package/dist/dev/src/builtins/python.js.map +1 -1
- package/dist/dev/src/components/ClassDeclaration.js +19 -10
- package/dist/dev/src/components/ClassDeclaration.js.map +1 -1
- package/dist/dev/src/components/ClassMethodDeclaration.js +20 -5
- package/dist/dev/src/components/ClassMethodDeclaration.js.map +1 -1
- package/dist/dev/src/components/DataclassDeclaration.js +14 -12
- package/dist/dev/src/components/DataclassDeclaration.js.map +1 -1
- package/dist/dev/src/components/DecoratorList.js +55 -0
- package/dist/dev/src/components/DecoratorList.js.map +1 -0
- package/dist/dev/src/components/EnumDeclaration.js +21 -12
- package/dist/dev/src/components/EnumDeclaration.js.map +1 -1
- package/dist/dev/src/components/FunctionBase.js +21 -10
- package/dist/dev/src/components/FunctionBase.js.map +1 -1
- package/dist/dev/src/components/FutureStatement.js +1 -1
- package/dist/dev/src/components/MethodBase.js +16 -4
- package/dist/dev/src/components/MethodBase.js.map +1 -1
- package/dist/dev/src/components/PropertyDeclaration.js +68 -17
- package/dist/dev/src/components/PropertyDeclaration.js.map +1 -1
- package/dist/dev/src/components/PydanticClassDeclaration.js +136 -0
- package/dist/dev/src/components/PydanticClassDeclaration.js.map +1 -0
- package/dist/dev/src/components/SourceFile.js +44 -32
- package/dist/dev/src/components/SourceFile.js.map +1 -1
- package/dist/dev/src/components/StaticMethodDeclaration.js +19 -5
- package/dist/dev/src/components/StaticMethodDeclaration.js.map +1 -1
- package/dist/dev/src/components/index.js +1 -0
- package/dist/dev/src/components/index.js.map +1 -1
- package/dist/dev/test/callsignatures.test.js +471 -297
- package/dist/dev/test/callsignatures.test.js.map +1 -1
- package/dist/dev/test/class-method-declaration.test.js +21 -10
- package/dist/dev/test/class-method-declaration.test.js.map +1 -1
- package/dist/dev/test/classdeclarations.test.js +480 -381
- package/dist/dev/test/classdeclarations.test.js.map +1 -1
- package/dist/dev/test/classinstantiations.test.js +201 -168
- package/dist/dev/test/classinstantiations.test.js.map +1 -1
- package/dist/dev/test/constructordeclaration.test.js +22 -11
- package/dist/dev/test/constructordeclaration.test.js.map +1 -1
- package/dist/dev/test/dataclassdeclarations.test.js +345 -358
- package/dist/dev/test/dataclassdeclarations.test.js.map +1 -1
- package/dist/dev/test/decoratorlist.test.js +131 -0
- package/dist/dev/test/decoratorlist.test.js.map +1 -0
- package/dist/dev/test/dundermethoddeclaration.test.js +22 -11
- package/dist/dev/test/dundermethoddeclaration.test.js.map +1 -1
- package/dist/dev/test/enums.test.js +231 -166
- package/dist/dev/test/enums.test.js.map +1 -1
- package/dist/dev/test/externals.test.js +57 -45
- package/dist/dev/test/externals.test.js.map +1 -1
- package/dist/dev/test/factories.test.js +124 -50
- package/dist/dev/test/factories.test.js.map +1 -1
- package/dist/dev/test/functioncallexpressions.test.js +199 -164
- package/dist/dev/test/functioncallexpressions.test.js.map +1 -1
- package/dist/dev/test/functiondeclaration.test.js +452 -265
- package/dist/dev/test/functiondeclaration.test.js.map +1 -1
- package/dist/dev/test/imports.test.js +273 -221
- package/dist/dev/test/imports.test.js.map +1 -1
- package/dist/dev/test/memberexpressions.test.js +1237 -972
- package/dist/dev/test/memberexpressions.test.js.map +1 -1
- package/dist/dev/test/methoddeclaration.test.js +200 -45
- package/dist/dev/test/methoddeclaration.test.js.map +1 -1
- package/dist/dev/test/namepolicies.test.js +130 -94
- package/dist/dev/test/namepolicies.test.js.map +1 -1
- package/dist/dev/test/propertydeclaration.test.js +177 -46
- package/dist/dev/test/propertydeclaration.test.js.map +1 -1
- package/dist/dev/test/pydanticclassdeclarations.test.js +1089 -0
- package/dist/dev/test/pydanticclassdeclarations.test.js.map +1 -0
- package/dist/dev/test/pydocs.test.js +888 -715
- package/dist/dev/test/pydocs.test.js.map +1 -1
- package/dist/dev/test/references.test.js +42 -35
- package/dist/dev/test/references.test.js.map +1 -1
- package/dist/dev/test/sourcefiles.test.js +1109 -841
- package/dist/dev/test/sourcefiles.test.js.map +1 -1
- package/dist/dev/test/staticmethoddeclaration.test.js +21 -10
- package/dist/dev/test/staticmethoddeclaration.test.js.map +1 -1
- package/dist/dev/test/type-checking-imports.test.js +408 -359
- package/dist/dev/test/type-checking-imports.test.js.map +1 -1
- package/dist/dev/test/typereference.test.js +55 -40
- package/dist/dev/test/typereference.test.js.map +1 -1
- package/dist/dev/test/uniontypeexpression.test.js +222 -146
- package/dist/dev/test/uniontypeexpression.test.js.map +1 -1
- package/dist/dev/test/utils.js +39 -77
- package/dist/dev/test/utils.js.map +1 -1
- package/dist/dev/test/values.test.js +237 -101
- package/dist/dev/test/values.test.js.map +1 -1
- package/dist/dev/test/variables.test.js +321 -203
- package/dist/dev/test/variables.test.js.map +1 -1
- package/dist/dev/test/vitest.setup.js +2 -0
- package/dist/dev/test/vitest.setup.js.map +1 -0
- package/dist/src/builtins/python.d.ts +30 -0
- package/dist/src/builtins/python.d.ts.map +1 -1
- package/dist/src/builtins/python.js +46 -0
- package/dist/src/builtins/python.js.map +1 -1
- package/dist/src/components/ClassDeclaration.d.ts +21 -0
- package/dist/src/components/ClassDeclaration.d.ts.map +1 -1
- package/dist/src/components/ClassDeclaration.js +6 -1
- package/dist/src/components/ClassDeclaration.js.map +1 -1
- package/dist/src/components/ClassMethodDeclaration.d.ts +5 -1
- package/dist/src/components/ClassMethodDeclaration.d.ts.map +1 -1
- package/dist/src/components/ClassMethodDeclaration.js +14 -3
- package/dist/src/components/ClassMethodDeclaration.js.map +1 -1
- package/dist/src/components/DataclassDeclaration.d.ts.map +1 -1
- package/dist/src/components/DataclassDeclaration.js +10 -4
- package/dist/src/components/DataclassDeclaration.js.map +1 -1
- package/dist/src/components/DecoratorList.d.ts +43 -0
- package/dist/src/components/DecoratorList.d.ts.map +1 -0
- package/dist/src/components/DecoratorList.js +47 -0
- package/dist/src/components/DecoratorList.js.map +1 -0
- package/dist/src/components/EnumDeclaration.d.ts +9 -0
- package/dist/src/components/EnumDeclaration.d.ts.map +1 -1
- package/dist/src/components/EnumDeclaration.js +6 -1
- package/dist/src/components/EnumDeclaration.js.map +1 -1
- package/dist/src/components/FunctionBase.d.ts +31 -1
- package/dist/src/components/FunctionBase.d.ts.map +1 -1
- package/dist/src/components/FunctionBase.js +9 -2
- package/dist/src/components/FunctionBase.js.map +1 -1
- package/dist/src/components/FutureStatement.d.ts +1 -1
- package/dist/src/components/FutureStatement.js +1 -1
- package/dist/src/components/MethodBase.d.ts.map +1 -1
- package/dist/src/components/MethodBase.js +10 -2
- package/dist/src/components/MethodBase.js.map +1 -1
- package/dist/src/components/PropertyDeclaration.d.ts +29 -0
- package/dist/src/components/PropertyDeclaration.d.ts.map +1 -1
- package/dist/src/components/PropertyDeclaration.js +48 -1
- package/dist/src/components/PropertyDeclaration.js.map +1 -1
- package/dist/src/components/PydanticClassDeclaration.d.ts +120 -0
- package/dist/src/components/PydanticClassDeclaration.d.ts.map +1 -0
- package/dist/src/components/PydanticClassDeclaration.js +116 -0
- package/dist/src/components/PydanticClassDeclaration.js.map +1 -0
- package/dist/src/components/SourceFile.d.ts +2 -2
- package/dist/src/components/SourceFile.d.ts.map +1 -1
- package/dist/src/components/SourceFile.js +12 -0
- package/dist/src/components/SourceFile.js.map +1 -1
- package/dist/src/components/StaticMethodDeclaration.d.ts +3 -0
- package/dist/src/components/StaticMethodDeclaration.d.ts.map +1 -1
- package/dist/src/components/StaticMethodDeclaration.js +13 -3
- package/dist/src/components/StaticMethodDeclaration.js.map +1 -1
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/components/index.js +1 -0
- package/dist/src/components/index.js.map +1 -1
- package/dist/test/callsignatures.test.js +346 -272
- package/dist/test/callsignatures.test.js.map +1 -1
- package/dist/test/class-method-declaration.test.js +7 -4
- package/dist/test/class-method-declaration.test.js.map +1 -1
- package/dist/test/classdeclarations.test.js +316 -277
- package/dist/test/classdeclarations.test.js.map +1 -1
- package/dist/test/classinstantiations.test.js +112 -103
- package/dist/test/classinstantiations.test.js.map +1 -1
- package/dist/test/constructordeclaration.test.js +7 -4
- package/dist/test/constructordeclaration.test.js.map +1 -1
- package/dist/test/dataclassdeclarations.test.js +153 -178
- package/dist/test/dataclassdeclarations.test.js.map +1 -1
- package/dist/test/decoratorlist.test.d.ts +2 -0
- package/dist/test/decoratorlist.test.d.ts.map +1 -0
- package/dist/test/decoratorlist.test.js +83 -0
- package/dist/test/decoratorlist.test.js.map +1 -0
- package/dist/test/dundermethoddeclaration.test.js +7 -4
- package/dist/test/dundermethoddeclaration.test.js.map +1 -1
- package/dist/test/enums.test.js +172 -143
- package/dist/test/enums.test.js.map +1 -1
- package/dist/test/externals.test.js +24 -24
- package/dist/test/externals.test.js.map +1 -1
- package/dist/test/factories.test.js +75 -33
- package/dist/test/factories.test.js.map +1 -1
- package/dist/test/functioncallexpressions.test.js +117 -106
- package/dist/test/functioncallexpressions.test.js.map +1 -1
- package/dist/test/functiondeclaration.test.js +256 -173
- package/dist/test/functiondeclaration.test.js.map +1 -1
- package/dist/test/imports.test.js +171 -143
- package/dist/test/imports.test.js.map +1 -1
- package/dist/test/memberexpressions.test.js +582 -453
- package/dist/test/memberexpressions.test.js.map +1 -1
- package/dist/test/methoddeclaration.test.js +106 -19
- package/dist/test/methoddeclaration.test.js.map +1 -1
- package/dist/test/namepolicies.test.js +90 -78
- package/dist/test/namepolicies.test.js.map +1 -1
- package/dist/test/propertydeclaration.test.js +90 -15
- package/dist/test/propertydeclaration.test.js.map +1 -1
- package/dist/test/pydanticclassdeclarations.test.d.ts +2 -0
- package/dist/test/pydanticclassdeclarations.test.d.ts.map +1 -0
- package/dist/test/pydanticclassdeclarations.test.js +773 -0
- package/dist/test/pydanticclassdeclarations.test.js.map +1 -0
- package/dist/test/pydocs.test.js +573 -532
- package/dist/test/pydocs.test.js.map +1 -1
- package/dist/test/references.test.js +31 -28
- package/dist/test/references.test.js.map +1 -1
- package/dist/test/sourcefiles.test.js +700 -580
- package/dist/test/sourcefiles.test.js.map +1 -1
- package/dist/test/staticmethoddeclaration.test.js +7 -4
- package/dist/test/staticmethoddeclaration.test.js.map +1 -1
- package/dist/test/type-checking-imports.test.js +297 -284
- package/dist/test/type-checking-imports.test.js.map +1 -1
- package/dist/test/typereference.test.js +29 -22
- package/dist/test/typereference.test.js.map +1 -1
- package/dist/test/uniontypeexpression.test.js +124 -88
- package/dist/test/uniontypeexpression.test.js.map +1 -1
- package/dist/test/utils.d.ts +10 -17
- package/dist/test/utils.d.ts.map +1 -1
- package/dist/test/utils.js +32 -74
- package/dist/test/utils.js.map +1 -1
- package/dist/test/values.test.js +135 -67
- package/dist/test/values.test.js.map +1 -1
- package/dist/test/variables.test.js +201 -151
- package/dist/test/variables.test.js.map +1 -1
- package/dist/test/vitest.setup.d.ts +2 -0
- package/dist/test/vitest.setup.d.ts.map +1 -0
- package/dist/test/vitest.setup.js +2 -0
- package/dist/test/vitest.setup.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/api/components/ClassDeclaration.md +10 -7
- package/docs/api/components/ClassEnumDeclaration.md +9 -6
- package/docs/api/components/ClassMethodDeclaration.md +7 -5
- package/docs/api/components/DataclassDeclaration.md +9 -5
- package/docs/api/components/DunderMethodDeclaration.md +7 -5
- package/docs/api/components/FunctionDeclaration.md +9 -5
- package/docs/api/components/FutureStatement.md +1 -1
- package/docs/api/components/MethodDeclaration.md +11 -6
- package/docs/api/components/PropertyDeclaration.md +11 -8
- package/docs/api/components/PydanticClassDeclaration.md +146 -0
- package/docs/api/components/SourceFile.md +20 -8
- package/docs/api/components/StaticMethodDeclaration.md +7 -5
- package/docs/api/components/index.md +1 -0
- package/docs/api/index.md +3 -3
- package/docs/api/types/CommonFunctionProps.md +4 -3
- package/docs/api/types/PydanticModelConfigDictProps.md +32 -0
- package/docs/api/types/index.md +1 -0
- package/docs/api/variables/index.md +3 -0
- package/docs/api/variables/pydanticModule.md +27 -0
- package/docs/api/variables/pydanticSettingsModule.md +7 -0
- package/docs/api/variables/typingModule.md +9 -0
- package/package.json +4 -4
- package/src/builtins/python.ts +539 -1
- package/src/components/ClassDeclaration.tsx +23 -0
- package/src/components/ClassMethodDeclaration.tsx +9 -1
- package/src/components/DataclassDeclaration.tsx +18 -11
- package/src/components/DecoratorList.tsx +50 -0
- package/src/components/EnumDeclaration.tsx +11 -0
- package/src/components/FunctionBase.tsx +34 -3
- package/src/components/FutureStatement.tsx +1 -1
- package/src/components/MethodBase.tsx +6 -2
- package/src/components/PropertyDeclaration.tsx +48 -1
- package/src/components/PydanticClassDeclaration.tsx +222 -0
- package/src/components/SourceFile.tsx +6 -1
- package/src/components/StaticMethodDeclaration.tsx +7 -1
- package/src/components/index.ts +1 -0
- package/temp/api.json +1158 -86
- package/test/callsignatures.test.tsx +309 -283
- package/test/class-method-declaration.test.tsx +3 -4
- package/test/classdeclarations.test.tsx +277 -235
- package/test/classinstantiations.test.tsx +115 -109
- package/test/constructordeclaration.test.tsx +9 -6
- package/test/dataclassdeclarations.test.tsx +256 -349
- package/test/decoratorlist.test.tsx +114 -0
- package/test/dundermethoddeclaration.test.tsx +3 -4
- package/test/enums.test.tsx +84 -71
- package/test/externals.test.tsx +25 -25
- package/test/factories.test.tsx +64 -22
- package/test/functioncallexpressions.test.tsx +123 -109
- package/test/functiondeclaration.test.tsx +218 -140
- package/test/imports.test.tsx +119 -91
- package/test/memberexpressions.test.tsx +265 -207
- package/test/methoddeclaration.test.tsx +115 -24
- package/test/namepolicies.test.tsx +69 -69
- package/test/propertydeclaration.test.tsx +71 -7
- package/test/pydanticclassdeclarations.test.tsx +704 -0
- package/test/pydocs.test.tsx +531 -579
- package/test/references.test.tsx +24 -23
- package/test/sourcefiles.test.tsx +527 -492
- package/test/staticmethoddeclaration.test.tsx +3 -4
- package/test/type-checking-imports.test.tsx +206 -218
- package/test/typereference.test.tsx +15 -12
- package/test/uniontypeexpression.test.tsx +74 -61
- package/test/utils.tsx +26 -110
- package/test/values.test.tsx +82 -32
- package/test/variables.test.tsx +162 -142
- package/test/vitest.setup.ts +1 -0
- package/vitest.config.ts +3 -0
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
import { Prose, code, refkey } from "@alloy-js/core";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
pydanticModule,
|
|
5
|
+
pydanticSettingsModule,
|
|
6
|
+
typingModule,
|
|
7
|
+
} from "../src/builtins/python.js";
|
|
8
|
+
import * as py from "../src/index.js";
|
|
9
|
+
import { TestOutput, TestOutputDirectory } from "./utils.js";
|
|
10
|
+
|
|
11
|
+
describe("PydanticClassDeclaration", () => {
|
|
12
|
+
it("forwards class-level decorators above `class`", () => {
|
|
13
|
+
expect(
|
|
14
|
+
<TestOutput path="models.py" externals={[pydanticModule, typingModule]}>
|
|
15
|
+
<py.PydanticClassDeclaration
|
|
16
|
+
name="User"
|
|
17
|
+
decorators={[code`@${typingModule["."].final}`]}
|
|
18
|
+
>
|
|
19
|
+
<py.VariableDeclaration
|
|
20
|
+
instanceVariable
|
|
21
|
+
omitNone
|
|
22
|
+
name="id"
|
|
23
|
+
type="int"
|
|
24
|
+
/>
|
|
25
|
+
</py.PydanticClassDeclaration>
|
|
26
|
+
</TestOutput>,
|
|
27
|
+
).toRenderTo(
|
|
28
|
+
`
|
|
29
|
+
from pydantic import BaseModel
|
|
30
|
+
from typing import final
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@final
|
|
34
|
+
class User(BaseModel):
|
|
35
|
+
id: int
|
|
36
|
+
|
|
37
|
+
`,
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("emits a pydantic model with BaseModel, fields, and Field import", () => {
|
|
42
|
+
expect(
|
|
43
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
44
|
+
<py.PydanticClassDeclaration name="User">
|
|
45
|
+
<py.VariableDeclaration
|
|
46
|
+
instanceVariable
|
|
47
|
+
omitNone
|
|
48
|
+
name="id"
|
|
49
|
+
type="int"
|
|
50
|
+
/>
|
|
51
|
+
<py.VariableDeclaration
|
|
52
|
+
instanceVariable
|
|
53
|
+
name="name"
|
|
54
|
+
type="str"
|
|
55
|
+
initializer={code`${pydanticModule["."].Field}(default="anon")`}
|
|
56
|
+
/>
|
|
57
|
+
</py.PydanticClassDeclaration>
|
|
58
|
+
</TestOutput>,
|
|
59
|
+
).toRenderTo(
|
|
60
|
+
`
|
|
61
|
+
from pydantic import BaseModel
|
|
62
|
+
from pydantic import Field
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class User(BaseModel):
|
|
66
|
+
id: int
|
|
67
|
+
name: str = Field(default="anon")
|
|
68
|
+
|
|
69
|
+
`,
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("inherits from another pydantic model via bases", () => {
|
|
74
|
+
const baseRef = refkey();
|
|
75
|
+
expect(
|
|
76
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
77
|
+
<py.StatementList>
|
|
78
|
+
<py.PydanticClassDeclaration name="User" refkey={baseRef}>
|
|
79
|
+
<py.VariableDeclaration
|
|
80
|
+
instanceVariable
|
|
81
|
+
omitNone
|
|
82
|
+
name="id"
|
|
83
|
+
type="int"
|
|
84
|
+
/>
|
|
85
|
+
</py.PydanticClassDeclaration>
|
|
86
|
+
<py.PydanticClassDeclaration name="Admin" bases={[baseRef]}>
|
|
87
|
+
<py.VariableDeclaration
|
|
88
|
+
instanceVariable
|
|
89
|
+
omitNone
|
|
90
|
+
name="role"
|
|
91
|
+
type="str"
|
|
92
|
+
/>
|
|
93
|
+
</py.PydanticClassDeclaration>
|
|
94
|
+
</py.StatementList>
|
|
95
|
+
</TestOutput>,
|
|
96
|
+
).toRenderTo(
|
|
97
|
+
`
|
|
98
|
+
from pydantic import BaseModel
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class User(BaseModel):
|
|
102
|
+
id: int
|
|
103
|
+
|
|
104
|
+
class Admin(User):
|
|
105
|
+
role: str
|
|
106
|
+
|
|
107
|
+
`,
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("supports required, optional, Field default, and plain default", () => {
|
|
112
|
+
expect(
|
|
113
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
114
|
+
<py.PydanticClassDeclaration name="Item">
|
|
115
|
+
<py.VariableDeclaration
|
|
116
|
+
instanceVariable
|
|
117
|
+
omitNone
|
|
118
|
+
name="sku"
|
|
119
|
+
type="str"
|
|
120
|
+
/>
|
|
121
|
+
<py.VariableDeclaration
|
|
122
|
+
instanceVariable
|
|
123
|
+
name="notes"
|
|
124
|
+
type={<py.UnionTypeExpression children={["str", "None"]} />}
|
|
125
|
+
/>
|
|
126
|
+
<py.VariableDeclaration
|
|
127
|
+
instanceVariable
|
|
128
|
+
name="label"
|
|
129
|
+
type="str"
|
|
130
|
+
initializer={code`${pydanticModule["."].Field}(default="untitled")`}
|
|
131
|
+
/>
|
|
132
|
+
<py.VariableDeclaration
|
|
133
|
+
instanceVariable
|
|
134
|
+
name="qty"
|
|
135
|
+
type="int"
|
|
136
|
+
initializer={1}
|
|
137
|
+
/>
|
|
138
|
+
</py.PydanticClassDeclaration>
|
|
139
|
+
</TestOutput>,
|
|
140
|
+
).toRenderTo(
|
|
141
|
+
`
|
|
142
|
+
from pydantic import BaseModel
|
|
143
|
+
from pydantic import Field
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class Item(BaseModel):
|
|
147
|
+
sku: str
|
|
148
|
+
notes: str | None = None
|
|
149
|
+
label: str = Field(default="untitled")
|
|
150
|
+
qty: int = 1
|
|
151
|
+
|
|
152
|
+
`,
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("emits class docstring", () => {
|
|
157
|
+
const doc = (
|
|
158
|
+
<py.ClassDoc description={[<Prose>Payload for an API request.</Prose>]} />
|
|
159
|
+
);
|
|
160
|
+
expect(
|
|
161
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
162
|
+
<py.PydanticClassDeclaration name="RequestBody" doc={doc}>
|
|
163
|
+
<py.VariableDeclaration
|
|
164
|
+
instanceVariable
|
|
165
|
+
omitNone
|
|
166
|
+
name="value"
|
|
167
|
+
type="str"
|
|
168
|
+
/>
|
|
169
|
+
</py.PydanticClassDeclaration>
|
|
170
|
+
</TestOutput>,
|
|
171
|
+
).toRenderTo(
|
|
172
|
+
`
|
|
173
|
+
from pydantic import BaseModel
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class RequestBody(BaseModel):
|
|
177
|
+
"""
|
|
178
|
+
Payload for an API request.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
value: str
|
|
182
|
+
|
|
183
|
+
`,
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("resolves refkey across files with pydantic imports", () => {
|
|
188
|
+
const modelRef = refkey();
|
|
189
|
+
expect(
|
|
190
|
+
<TestOutputDirectory externals={[pydanticModule]}>
|
|
191
|
+
<py.SourceFile path="models.py">
|
|
192
|
+
<py.PydanticClassDeclaration name="User" refkey={modelRef}>
|
|
193
|
+
<py.VariableDeclaration
|
|
194
|
+
instanceVariable
|
|
195
|
+
omitNone
|
|
196
|
+
name="id"
|
|
197
|
+
type="int"
|
|
198
|
+
/>
|
|
199
|
+
</py.PydanticClassDeclaration>
|
|
200
|
+
</py.SourceFile>
|
|
201
|
+
<py.SourceFile path="service.py">
|
|
202
|
+
<py.FunctionDeclaration
|
|
203
|
+
name="load_user"
|
|
204
|
+
returnType={modelRef}
|
|
205
|
+
parameters={[{ name: "user_id", type: "int" }]}
|
|
206
|
+
>
|
|
207
|
+
{"return User(id=user_id)"}
|
|
208
|
+
</py.FunctionDeclaration>
|
|
209
|
+
</py.SourceFile>
|
|
210
|
+
</TestOutputDirectory>,
|
|
211
|
+
).toRenderTo({
|
|
212
|
+
"models.py": `
|
|
213
|
+
from pydantic import BaseModel
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class User(BaseModel):
|
|
217
|
+
id: int
|
|
218
|
+
|
|
219
|
+
`,
|
|
220
|
+
"service.py": `
|
|
221
|
+
from typing import TYPE_CHECKING
|
|
222
|
+
|
|
223
|
+
if TYPE_CHECKING:
|
|
224
|
+
from models import User
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def load_user(user_id: int) -> User:
|
|
228
|
+
return User(id=user_id)
|
|
229
|
+
|
|
230
|
+
`,
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("emits model_config = ConfigDict(...) from top-level config props", () => {
|
|
235
|
+
expect(
|
|
236
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
237
|
+
<py.PydanticClassDeclaration
|
|
238
|
+
name="User"
|
|
239
|
+
frozen
|
|
240
|
+
extra="forbid"
|
|
241
|
+
validateAssignment
|
|
242
|
+
>
|
|
243
|
+
<py.VariableDeclaration
|
|
244
|
+
instanceVariable
|
|
245
|
+
omitNone
|
|
246
|
+
name="id"
|
|
247
|
+
type="int"
|
|
248
|
+
/>
|
|
249
|
+
</py.PydanticClassDeclaration>
|
|
250
|
+
</TestOutput>,
|
|
251
|
+
).toRenderTo(
|
|
252
|
+
`
|
|
253
|
+
from pydantic import BaseModel
|
|
254
|
+
from pydantic import ConfigDict
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class User(BaseModel):
|
|
258
|
+
model_config = ConfigDict(frozen=True, extra="forbid", validate_assignment=True)
|
|
259
|
+
id: int
|
|
260
|
+
|
|
261
|
+
`,
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("emits model_config = ConfigDict(...) from modelConfig", () => {
|
|
266
|
+
expect(
|
|
267
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
268
|
+
<py.PydanticClassDeclaration
|
|
269
|
+
name="User"
|
|
270
|
+
modelConfig={{
|
|
271
|
+
frozen: true,
|
|
272
|
+
extra: "forbid",
|
|
273
|
+
validateAssignment: true,
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
<py.VariableDeclaration
|
|
277
|
+
instanceVariable
|
|
278
|
+
omitNone
|
|
279
|
+
name="id"
|
|
280
|
+
type="int"
|
|
281
|
+
/>
|
|
282
|
+
</py.PydanticClassDeclaration>
|
|
283
|
+
</TestOutput>,
|
|
284
|
+
).toRenderTo(
|
|
285
|
+
`
|
|
286
|
+
from pydantic import BaseModel
|
|
287
|
+
from pydantic import ConfigDict
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class User(BaseModel):
|
|
291
|
+
model_config = ConfigDict(frozen=True, extra="forbid", validate_assignment=True)
|
|
292
|
+
id: int
|
|
293
|
+
|
|
294
|
+
`,
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("gives precedence to top-level config props over modelConfig", () => {
|
|
299
|
+
expect(
|
|
300
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
301
|
+
<py.PydanticClassDeclaration
|
|
302
|
+
name="User"
|
|
303
|
+
modelConfig={{ frozen: false, extra: "allow" }}
|
|
304
|
+
frozen
|
|
305
|
+
extra="forbid"
|
|
306
|
+
/>
|
|
307
|
+
</TestOutput>,
|
|
308
|
+
).toRenderTo(
|
|
309
|
+
`
|
|
310
|
+
from pydantic import BaseModel
|
|
311
|
+
from pydantic import ConfigDict
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class User(BaseModel):
|
|
315
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
316
|
+
|
|
317
|
+
`,
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("supports additional typed ConfigDict props", () => {
|
|
322
|
+
expect(
|
|
323
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
324
|
+
<py.PydanticClassDeclaration
|
|
325
|
+
name="User"
|
|
326
|
+
useEnumValues
|
|
327
|
+
coerceNumbersToStr
|
|
328
|
+
validateReturn
|
|
329
|
+
strMinLength={1}
|
|
330
|
+
strMaxLength={128}
|
|
331
|
+
serJsonBytes="base64"
|
|
332
|
+
valJsonBytes="hex"
|
|
333
|
+
/>
|
|
334
|
+
</TestOutput>,
|
|
335
|
+
).toRenderTo(
|
|
336
|
+
`
|
|
337
|
+
from pydantic import BaseModel
|
|
338
|
+
from pydantic import ConfigDict
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class User(BaseModel):
|
|
342
|
+
model_config = ConfigDict(coerce_numbers_to_str=True, ser_json_bytes="base64", str_min_length=1, str_max_length=128, use_enum_values=True, val_json_bytes="hex", validate_return=True)
|
|
343
|
+
|
|
344
|
+
`,
|
|
345
|
+
);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("imports SecretStr when used as a field type", () => {
|
|
349
|
+
expect(
|
|
350
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
351
|
+
<py.PydanticClassDeclaration name="Credentials">
|
|
352
|
+
<py.VariableDeclaration
|
|
353
|
+
instanceVariable
|
|
354
|
+
omitNone
|
|
355
|
+
name="token"
|
|
356
|
+
type={pydanticModule["."].SecretStr}
|
|
357
|
+
/>
|
|
358
|
+
</py.PydanticClassDeclaration>
|
|
359
|
+
</TestOutput>,
|
|
360
|
+
).toRenderTo(
|
|
361
|
+
`
|
|
362
|
+
from pydantic import BaseModel
|
|
363
|
+
from typing import TYPE_CHECKING
|
|
364
|
+
|
|
365
|
+
if TYPE_CHECKING:
|
|
366
|
+
from pydantic import SecretStr
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class Credentials(BaseModel):
|
|
370
|
+
token: SecretStr
|
|
371
|
+
|
|
372
|
+
`,
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("emits arbitrary model_config via modelConfigExpression", () => {
|
|
377
|
+
expect(
|
|
378
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
379
|
+
<py.PydanticClassDeclaration
|
|
380
|
+
name="M"
|
|
381
|
+
modelConfigExpression={code`${pydanticModule["."].ConfigDict}(frozen=True, extra="allow")`}
|
|
382
|
+
/>
|
|
383
|
+
</TestOutput>,
|
|
384
|
+
).toRenderTo(
|
|
385
|
+
`
|
|
386
|
+
from pydantic import BaseModel
|
|
387
|
+
from pydantic import ConfigDict
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
class M(BaseModel):
|
|
391
|
+
model_config = ConfigDict(frozen=True, extra="allow")
|
|
392
|
+
|
|
393
|
+
`,
|
|
394
|
+
);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("supports RootModel as explicit bases entry", () => {
|
|
398
|
+
expect(
|
|
399
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
400
|
+
<py.PydanticClassDeclaration
|
|
401
|
+
name="Tags"
|
|
402
|
+
bases={[code`${pydanticModule["."].RootModel}[list[str]]`]}
|
|
403
|
+
/>
|
|
404
|
+
</TestOutput>,
|
|
405
|
+
).toRenderTo(
|
|
406
|
+
`
|
|
407
|
+
from pydantic import RootModel
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class Tags(RootModel[list[str]]):
|
|
411
|
+
pass
|
|
412
|
+
|
|
413
|
+
`,
|
|
414
|
+
);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it("places Pydantic validators above classmethod", () => {
|
|
418
|
+
expect(
|
|
419
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
420
|
+
<py.PydanticClassDeclaration name="User">
|
|
421
|
+
<py.VariableDeclaration
|
|
422
|
+
instanceVariable
|
|
423
|
+
omitNone
|
|
424
|
+
name="name"
|
|
425
|
+
type="str"
|
|
426
|
+
/>
|
|
427
|
+
<py.ClassMethodDeclaration
|
|
428
|
+
name="strip_name"
|
|
429
|
+
decorators={[
|
|
430
|
+
code`@${pydanticModule["."].field_validator}("name", mode="before")`,
|
|
431
|
+
]}
|
|
432
|
+
parameters={[{ name: "value", type: "str" }]}
|
|
433
|
+
returnType="str"
|
|
434
|
+
>
|
|
435
|
+
{"return value.strip()"}
|
|
436
|
+
</py.ClassMethodDeclaration>
|
|
437
|
+
</py.PydanticClassDeclaration>
|
|
438
|
+
</TestOutput>,
|
|
439
|
+
).toRenderTo(
|
|
440
|
+
`
|
|
441
|
+
from pydantic import BaseModel
|
|
442
|
+
from pydantic import field_validator
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
class User(BaseModel):
|
|
446
|
+
name: str
|
|
447
|
+
@field_validator("name", mode="before")
|
|
448
|
+
@classmethod
|
|
449
|
+
def strip_name(cls, value: str) -> str:
|
|
450
|
+
return value.strip()
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
`,
|
|
454
|
+
);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe("Pydantic ecosystem emitters", () => {
|
|
459
|
+
it("typing module resolves Any and similar annotations", () => {
|
|
460
|
+
expect(
|
|
461
|
+
<TestOutput path="models.py" externals={[typingModule]}>
|
|
462
|
+
<py.FunctionDeclaration
|
|
463
|
+
name="identity"
|
|
464
|
+
parameters={[{ name: "x", type: typingModule["."].Any }]}
|
|
465
|
+
returnType={typingModule["."].Any}
|
|
466
|
+
>
|
|
467
|
+
{"return x"}
|
|
468
|
+
</py.FunctionDeclaration>
|
|
469
|
+
</TestOutput>,
|
|
470
|
+
).toRenderTo(
|
|
471
|
+
`
|
|
472
|
+
from typing import TYPE_CHECKING
|
|
473
|
+
|
|
474
|
+
if TYPE_CHECKING:
|
|
475
|
+
from typing import Any
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def identity(x: Any) -> Any:
|
|
479
|
+
return x
|
|
480
|
+
|
|
481
|
+
`,
|
|
482
|
+
);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it("pydantic.types constrains field annotations", () => {
|
|
486
|
+
expect(
|
|
487
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
488
|
+
<py.PydanticClassDeclaration name="Score">
|
|
489
|
+
<py.VariableDeclaration
|
|
490
|
+
instanceVariable
|
|
491
|
+
omitNone
|
|
492
|
+
name="points"
|
|
493
|
+
type={pydanticModule.types.PositiveInt}
|
|
494
|
+
/>
|
|
495
|
+
</py.PydanticClassDeclaration>
|
|
496
|
+
</TestOutput>,
|
|
497
|
+
).toRenderTo(
|
|
498
|
+
`
|
|
499
|
+
from pydantic import BaseModel
|
|
500
|
+
from typing import TYPE_CHECKING
|
|
501
|
+
|
|
502
|
+
if TYPE_CHECKING:
|
|
503
|
+
from pydantic.types import PositiveInt
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
class Score(BaseModel):
|
|
507
|
+
points: PositiveInt
|
|
508
|
+
|
|
509
|
+
`,
|
|
510
|
+
);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it("postponed annotations support forward references in fields", () => {
|
|
514
|
+
expect(
|
|
515
|
+
<TestOutputDirectory externals={[pydanticModule]}>
|
|
516
|
+
<py.SourceFile
|
|
517
|
+
path="models.py"
|
|
518
|
+
futureImports={[<py.FutureStatement feature="annotations" />]}
|
|
519
|
+
>
|
|
520
|
+
<py.PydanticClassDeclaration name="Node">
|
|
521
|
+
<py.VariableDeclaration
|
|
522
|
+
instanceVariable
|
|
523
|
+
omitNone
|
|
524
|
+
name="label"
|
|
525
|
+
type="str"
|
|
526
|
+
/>
|
|
527
|
+
<py.VariableDeclaration
|
|
528
|
+
instanceVariable
|
|
529
|
+
name="child"
|
|
530
|
+
type={code`"Node" | None`}
|
|
531
|
+
/>
|
|
532
|
+
</py.PydanticClassDeclaration>
|
|
533
|
+
</py.SourceFile>
|
|
534
|
+
</TestOutputDirectory>,
|
|
535
|
+
).toRenderTo({
|
|
536
|
+
"models.py": `
|
|
537
|
+
from __future__ import annotations
|
|
538
|
+
|
|
539
|
+
from pydantic import BaseModel
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
class Node(BaseModel):
|
|
543
|
+
label: str
|
|
544
|
+
child: "Node" | None = None
|
|
545
|
+
|
|
546
|
+
`,
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it("model_config can use pydantic.alias_generators", () => {
|
|
551
|
+
expect(
|
|
552
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
553
|
+
<py.PydanticClassDeclaration
|
|
554
|
+
name="M"
|
|
555
|
+
modelConfigExpression={code`${pydanticModule["."].ConfigDict}(alias_generator=${pydanticModule.alias_generators.to_camel})`}
|
|
556
|
+
/>
|
|
557
|
+
</TestOutput>,
|
|
558
|
+
).toRenderTo(
|
|
559
|
+
`
|
|
560
|
+
from pydantic import BaseModel
|
|
561
|
+
from pydantic import ConfigDict
|
|
562
|
+
from pydantic.alias_generators import to_camel
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class M(BaseModel):
|
|
566
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
567
|
+
|
|
568
|
+
`,
|
|
569
|
+
);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it("pydantic_settings exposes BaseSettings", () => {
|
|
573
|
+
expect(
|
|
574
|
+
<TestOutput path="config.py" externals={[pydanticSettingsModule]}>
|
|
575
|
+
<py.ClassDeclaration
|
|
576
|
+
name="AppSettings"
|
|
577
|
+
bases={[pydanticSettingsModule["."].BaseSettings]}
|
|
578
|
+
/>
|
|
579
|
+
</TestOutput>,
|
|
580
|
+
).toRenderTo(
|
|
581
|
+
`
|
|
582
|
+
from pydantic_settings import BaseSettings
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
class AppSettings(BaseSettings):
|
|
586
|
+
pass
|
|
587
|
+
|
|
588
|
+
`,
|
|
589
|
+
);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it("emits @computed_field above @property via PropertyDeclaration", () => {
|
|
593
|
+
expect(
|
|
594
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
595
|
+
<py.PydanticClassDeclaration name="Square">
|
|
596
|
+
<py.VariableDeclaration
|
|
597
|
+
instanceVariable
|
|
598
|
+
omitNone
|
|
599
|
+
name="width"
|
|
600
|
+
type="float"
|
|
601
|
+
/>
|
|
602
|
+
<py.PropertyDeclaration
|
|
603
|
+
name="area"
|
|
604
|
+
type="float"
|
|
605
|
+
decorators={[code`@${pydanticModule["."].computed_field}`]}
|
|
606
|
+
>
|
|
607
|
+
{"return self.width ** 2"}
|
|
608
|
+
</py.PropertyDeclaration>
|
|
609
|
+
</py.PydanticClassDeclaration>
|
|
610
|
+
</TestOutput>,
|
|
611
|
+
).toRenderTo(
|
|
612
|
+
`
|
|
613
|
+
from pydantic import BaseModel
|
|
614
|
+
from pydantic import computed_field
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
class Square(BaseModel):
|
|
618
|
+
width: float
|
|
619
|
+
@computed_field
|
|
620
|
+
@property
|
|
621
|
+
def area(self) -> float:
|
|
622
|
+
return self.width ** 2
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
`,
|
|
626
|
+
);
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it("emits computed_field on an instance method", () => {
|
|
630
|
+
expect(
|
|
631
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
632
|
+
<py.PydanticClassDeclaration name="Square">
|
|
633
|
+
<py.VariableDeclaration
|
|
634
|
+
instanceVariable
|
|
635
|
+
omitNone
|
|
636
|
+
name="width"
|
|
637
|
+
type="float"
|
|
638
|
+
/>
|
|
639
|
+
<py.MethodDeclaration
|
|
640
|
+
name="area"
|
|
641
|
+
decorators={[code`@${pydanticModule["."].computed_field}`]}
|
|
642
|
+
returnType="float"
|
|
643
|
+
>
|
|
644
|
+
{"return self.width ** 2"}
|
|
645
|
+
</py.MethodDeclaration>
|
|
646
|
+
</py.PydanticClassDeclaration>
|
|
647
|
+
</TestOutput>,
|
|
648
|
+
).toRenderTo(
|
|
649
|
+
`
|
|
650
|
+
from pydantic import BaseModel
|
|
651
|
+
from pydantic import computed_field
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
class Square(BaseModel):
|
|
655
|
+
width: float
|
|
656
|
+
@computed_field
|
|
657
|
+
def area(self) -> float:
|
|
658
|
+
return self.width ** 2
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
`,
|
|
662
|
+
);
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it("emits model_validator above classmethod", () => {
|
|
666
|
+
expect(
|
|
667
|
+
<TestOutput path="models.py" externals={[pydanticModule]}>
|
|
668
|
+
<py.PydanticClassDeclaration name="Bag">
|
|
669
|
+
<py.VariableDeclaration
|
|
670
|
+
instanceVariable
|
|
671
|
+
omitNone
|
|
672
|
+
name="items"
|
|
673
|
+
type="list"
|
|
674
|
+
/>
|
|
675
|
+
<py.ClassMethodDeclaration
|
|
676
|
+
name="ensure_items"
|
|
677
|
+
decorators={[
|
|
678
|
+
code`@${pydanticModule["."].model_validator}(mode="before")`,
|
|
679
|
+
]}
|
|
680
|
+
parameters={[{ name: "data", type: "dict" }]}
|
|
681
|
+
returnType="dict"
|
|
682
|
+
>
|
|
683
|
+
{"return data"}
|
|
684
|
+
</py.ClassMethodDeclaration>
|
|
685
|
+
</py.PydanticClassDeclaration>
|
|
686
|
+
</TestOutput>,
|
|
687
|
+
).toRenderTo(
|
|
688
|
+
`
|
|
689
|
+
from pydantic import BaseModel
|
|
690
|
+
from pydantic import model_validator
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
class Bag(BaseModel):
|
|
694
|
+
items: list
|
|
695
|
+
@model_validator(mode="before")
|
|
696
|
+
@classmethod
|
|
697
|
+
def ensure_items(cls, data: dict) -> dict:
|
|
698
|
+
return data
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
`,
|
|
702
|
+
);
|
|
703
|
+
});
|
|
704
|
+
});
|