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