@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,1089 @@
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
+ fileName: import.meta.url,
26
+ lineNumber: 19,
27
+ columnNumber: 11
28
+ });
29
+ }
30
+ }, {
31
+ fileName: import.meta.url,
32
+ lineNumber: 15,
33
+ columnNumber: 9
34
+ });
35
+ }
36
+ }, {
37
+ fileName: import.meta.url,
38
+ lineNumber: 14,
39
+ columnNumber: 7
40
+ })).toRenderTo(`
41
+ from pydantic import BaseModel
42
+ from typing import final
43
+
44
+
45
+ @final
46
+ class User(BaseModel):
47
+ id: int
48
+
49
+ `);
50
+ });
51
+ it("emits a pydantic model with BaseModel, fields, and Field import", () => {
52
+ expect(_$createComponent(TestOutput, {
53
+ path: "models.py",
54
+ externals: [pydanticModule],
55
+ get children() {
56
+ return _$createComponent(py.PydanticClassDeclaration, {
57
+ name: "User",
58
+ get children() {
59
+ return [_$createComponent(py.VariableDeclaration, {
60
+ instanceVariable: true,
61
+ omitNone: true,
62
+ name: "id",
63
+ type: "int"
64
+ }, {
65
+ fileName: import.meta.url,
66
+ lineNumber: 45,
67
+ columnNumber: 11
68
+ }), _$createComponent(py.VariableDeclaration, {
69
+ instanceVariable: true,
70
+ name: "name",
71
+ type: "str",
72
+ get initializer() {
73
+ return code`${pydanticModule["."].Field}(default="anon")`;
74
+ }
75
+ }, {
76
+ fileName: import.meta.url,
77
+ lineNumber: 51,
78
+ columnNumber: 11
79
+ })];
80
+ }
81
+ }, {
82
+ fileName: import.meta.url,
83
+ lineNumber: 44,
84
+ columnNumber: 9
85
+ });
86
+ }
87
+ }, {
88
+ fileName: import.meta.url,
89
+ lineNumber: 43,
90
+ columnNumber: 7
91
+ })).toRenderTo(`
92
+ from pydantic import BaseModel
93
+ from pydantic import Field
94
+
95
+
96
+ class User(BaseModel):
97
+ id: int
98
+ name: str = Field(default="anon")
99
+
100
+ `);
101
+ });
102
+ it("inherits from another pydantic model via bases", () => {
103
+ const baseRef = refkey();
104
+ expect(_$createComponent(TestOutput, {
105
+ path: "models.py",
106
+ externals: [pydanticModule],
107
+ get children() {
108
+ return _$createComponent(py.StatementList, {
109
+ get children() {
110
+ return [_$createComponent(py.PydanticClassDeclaration, {
111
+ name: "User",
112
+ refkey: baseRef,
113
+ get children() {
114
+ return _$createComponent(py.VariableDeclaration, {
115
+ instanceVariable: true,
116
+ omitNone: true,
117
+ name: "id",
118
+ type: "int"
119
+ }, {
120
+ fileName: import.meta.url,
121
+ lineNumber: 79,
122
+ columnNumber: 13
123
+ });
124
+ }
125
+ }, {
126
+ fileName: import.meta.url,
127
+ lineNumber: 78,
128
+ columnNumber: 11
129
+ }), _$createComponent(py.PydanticClassDeclaration, {
130
+ name: "Admin",
131
+ bases: [baseRef],
132
+ get children() {
133
+ return _$createComponent(py.VariableDeclaration, {
134
+ instanceVariable: true,
135
+ omitNone: true,
136
+ name: "role",
137
+ type: "str"
138
+ }, {
139
+ fileName: import.meta.url,
140
+ lineNumber: 87,
141
+ columnNumber: 13
142
+ });
143
+ }
144
+ }, {
145
+ fileName: import.meta.url,
146
+ lineNumber: 86,
147
+ columnNumber: 11
148
+ })];
149
+ }
150
+ }, {
151
+ fileName: import.meta.url,
152
+ lineNumber: 77,
153
+ columnNumber: 9
154
+ });
155
+ }
156
+ }, {
157
+ fileName: import.meta.url,
158
+ lineNumber: 76,
159
+ columnNumber: 7
160
+ })).toRenderTo(`
161
+ from pydantic import BaseModel
162
+
163
+
164
+ class User(BaseModel):
165
+ id: int
166
+
167
+ class Admin(User):
168
+ role: str
169
+
170
+ `);
171
+ });
172
+ it("supports required, optional, Field default, and plain default", () => {
173
+ expect(_$createComponent(TestOutput, {
174
+ path: "models.py",
175
+ externals: [pydanticModule],
176
+ get children() {
177
+ return _$createComponent(py.PydanticClassDeclaration, {
178
+ name: "Item",
179
+ get children() {
180
+ return [_$createComponent(py.VariableDeclaration, {
181
+ instanceVariable: true,
182
+ omitNone: true,
183
+ name: "sku",
184
+ type: "str"
185
+ }, {
186
+ fileName: import.meta.url,
187
+ lineNumber: 115,
188
+ columnNumber: 11
189
+ }), _$createComponent(py.VariableDeclaration, {
190
+ instanceVariable: true,
191
+ name: "notes",
192
+ get type() {
193
+ return _$createComponent(py.UnionTypeExpression, {
194
+ children: ["str", "None"]
195
+ }, {
196
+ fileName: import.meta.url,
197
+ lineNumber: 124,
198
+ columnNumber: 19
199
+ });
200
+ }
201
+ }, {
202
+ fileName: import.meta.url,
203
+ lineNumber: 121,
204
+ columnNumber: 11
205
+ }), _$createComponent(py.VariableDeclaration, {
206
+ instanceVariable: true,
207
+ name: "label",
208
+ type: "str",
209
+ get initializer() {
210
+ return code`${pydanticModule["."].Field}(default="untitled")`;
211
+ }
212
+ }, {
213
+ fileName: import.meta.url,
214
+ lineNumber: 126,
215
+ columnNumber: 11
216
+ }), _$createComponent(py.VariableDeclaration, {
217
+ instanceVariable: true,
218
+ name: "qty",
219
+ type: "int",
220
+ initializer: 1
221
+ }, {
222
+ fileName: import.meta.url,
223
+ lineNumber: 132,
224
+ columnNumber: 11
225
+ })];
226
+ }
227
+ }, {
228
+ fileName: import.meta.url,
229
+ lineNumber: 114,
230
+ columnNumber: 9
231
+ });
232
+ }
233
+ }, {
234
+ fileName: import.meta.url,
235
+ lineNumber: 113,
236
+ columnNumber: 7
237
+ })).toRenderTo(`
238
+ from pydantic import BaseModel
239
+ from pydantic import Field
240
+
241
+
242
+ class Item(BaseModel):
243
+ sku: str
244
+ notes: str | None = None
245
+ label: str = Field(default="untitled")
246
+ qty: int = 1
247
+
248
+ `);
249
+ });
250
+ it("emits class docstring", () => {
251
+ const doc = _$createComponent(py.ClassDoc, {
252
+ get description() {
253
+ return [_$createComponent(Prose, {
254
+ children: "Payload for an API request."
255
+ }, {
256
+ fileName: import.meta.url,
257
+ lineNumber: 158,
258
+ columnNumber: 34
259
+ })];
260
+ }
261
+ }, {
262
+ fileName: import.meta.url,
263
+ lineNumber: 158,
264
+ columnNumber: 7
265
+ });
266
+ expect(_$createComponent(TestOutput, {
267
+ path: "models.py",
268
+ externals: [pydanticModule],
269
+ get children() {
270
+ return _$createComponent(py.PydanticClassDeclaration, {
271
+ name: "RequestBody",
272
+ doc: doc,
273
+ get children() {
274
+ return _$createComponent(py.VariableDeclaration, {
275
+ instanceVariable: true,
276
+ omitNone: true,
277
+ name: "value",
278
+ type: "str"
279
+ }, {
280
+ fileName: import.meta.url,
281
+ lineNumber: 163,
282
+ columnNumber: 11
283
+ });
284
+ }
285
+ }, {
286
+ fileName: import.meta.url,
287
+ lineNumber: 162,
288
+ columnNumber: 9
289
+ });
290
+ }
291
+ }, {
292
+ fileName: import.meta.url,
293
+ lineNumber: 161,
294
+ columnNumber: 7
295
+ })).toRenderTo(`
296
+ from pydantic import BaseModel
297
+
298
+
299
+ class RequestBody(BaseModel):
300
+ """
301
+ Payload for an API request.
302
+ """
303
+
304
+ value: str
305
+
306
+ `);
307
+ });
308
+ it("resolves refkey across files with pydantic imports", () => {
309
+ const modelRef = refkey();
310
+ expect(_$createComponent(TestOutputDirectory, {
311
+ externals: [pydanticModule],
312
+ get children() {
313
+ return [_$createComponent(py.SourceFile, {
314
+ path: "models.py",
315
+ get children() {
316
+ return _$createComponent(py.PydanticClassDeclaration, {
317
+ name: "User",
318
+ refkey: modelRef,
319
+ get children() {
320
+ return _$createComponent(py.VariableDeclaration, {
321
+ instanceVariable: true,
322
+ omitNone: true,
323
+ name: "id",
324
+ type: "int"
325
+ }, {
326
+ fileName: import.meta.url,
327
+ lineNumber: 193,
328
+ columnNumber: 13
329
+ });
330
+ }
331
+ }, {
332
+ fileName: import.meta.url,
333
+ lineNumber: 192,
334
+ columnNumber: 11
335
+ });
336
+ }
337
+ }, {
338
+ fileName: import.meta.url,
339
+ lineNumber: 191,
340
+ columnNumber: 9
341
+ }), _$createComponent(py.SourceFile, {
342
+ path: "service.py",
343
+ get children() {
344
+ return _$createComponent(py.FunctionDeclaration, {
345
+ name: "load_user",
346
+ returnType: modelRef,
347
+ parameters: [{
348
+ name: "user_id",
349
+ type: "int"
350
+ }],
351
+ children: "return User(id=user_id)"
352
+ }, {
353
+ fileName: import.meta.url,
354
+ lineNumber: 202,
355
+ columnNumber: 11
356
+ });
357
+ }
358
+ }, {
359
+ fileName: import.meta.url,
360
+ lineNumber: 201,
361
+ columnNumber: 9
362
+ })];
363
+ }
364
+ }, {
365
+ fileName: import.meta.url,
366
+ lineNumber: 190,
367
+ columnNumber: 7
368
+ })).toRenderTo({
369
+ "models.py": `
370
+ from pydantic import BaseModel
371
+
372
+
373
+ class User(BaseModel):
374
+ id: int
375
+
376
+ `,
377
+ "service.py": `
378
+ from typing import TYPE_CHECKING
379
+
380
+ if TYPE_CHECKING:
381
+ from models import User
382
+
383
+
384
+ def load_user(user_id: int) -> User:
385
+ return User(id=user_id)
386
+
387
+ `
388
+ });
389
+ });
390
+ it("emits model_config = ConfigDict(...) from top-level config props", () => {
391
+ expect(_$createComponent(TestOutput, {
392
+ path: "models.py",
393
+ externals: [pydanticModule],
394
+ get children() {
395
+ return _$createComponent(py.PydanticClassDeclaration, {
396
+ name: "User",
397
+ frozen: true,
398
+ extra: "forbid",
399
+ validateAssignment: true,
400
+ get children() {
401
+ return _$createComponent(py.VariableDeclaration, {
402
+ instanceVariable: true,
403
+ omitNone: true,
404
+ name: "id",
405
+ type: "int"
406
+ }, {
407
+ fileName: import.meta.url,
408
+ lineNumber: 243,
409
+ columnNumber: 11
410
+ });
411
+ }
412
+ }, {
413
+ fileName: import.meta.url,
414
+ lineNumber: 237,
415
+ columnNumber: 9
416
+ });
417
+ }
418
+ }, {
419
+ fileName: import.meta.url,
420
+ lineNumber: 236,
421
+ columnNumber: 7
422
+ })).toRenderTo(`
423
+ from pydantic import BaseModel
424
+ from pydantic import ConfigDict
425
+
426
+
427
+ class User(BaseModel):
428
+ model_config = ConfigDict(frozen=True, extra="forbid", validate_assignment=True)
429
+ id: int
430
+
431
+ `);
432
+ });
433
+ it("emits model_config = ConfigDict(...) from modelConfig", () => {
434
+ expect(_$createComponent(TestOutput, {
435
+ path: "models.py",
436
+ externals: [pydanticModule],
437
+ get children() {
438
+ return _$createComponent(py.PydanticClassDeclaration, {
439
+ name: "User",
440
+ modelConfig: {
441
+ frozen: true,
442
+ extra: "forbid",
443
+ validateAssignment: true
444
+ },
445
+ get children() {
446
+ return _$createComponent(py.VariableDeclaration, {
447
+ instanceVariable: true,
448
+ omitNone: true,
449
+ name: "id",
450
+ type: "int"
451
+ }, {
452
+ fileName: import.meta.url,
453
+ lineNumber: 276,
454
+ columnNumber: 11
455
+ });
456
+ }
457
+ }, {
458
+ fileName: import.meta.url,
459
+ lineNumber: 268,
460
+ columnNumber: 9
461
+ });
462
+ }
463
+ }, {
464
+ fileName: import.meta.url,
465
+ lineNumber: 267,
466
+ columnNumber: 7
467
+ })).toRenderTo(`
468
+ from pydantic import BaseModel
469
+ from pydantic import ConfigDict
470
+
471
+
472
+ class User(BaseModel):
473
+ model_config = ConfigDict(frozen=True, extra="forbid", validate_assignment=True)
474
+ id: int
475
+
476
+ `);
477
+ });
478
+ it("gives precedence to top-level config props over modelConfig", () => {
479
+ expect(_$createComponent(TestOutput, {
480
+ path: "models.py",
481
+ externals: [pydanticModule],
482
+ get children() {
483
+ return _$createComponent(py.PydanticClassDeclaration, {
484
+ name: "User",
485
+ modelConfig: {
486
+ frozen: false,
487
+ extra: "allow"
488
+ },
489
+ frozen: true,
490
+ extra: "forbid"
491
+ }, {
492
+ fileName: import.meta.url,
493
+ lineNumber: 301,
494
+ columnNumber: 9
495
+ });
496
+ }
497
+ }, {
498
+ fileName: import.meta.url,
499
+ lineNumber: 300,
500
+ columnNumber: 7
501
+ })).toRenderTo(`
502
+ from pydantic import BaseModel
503
+ from pydantic import ConfigDict
504
+
505
+
506
+ class User(BaseModel):
507
+ model_config = ConfigDict(frozen=True, extra="forbid")
508
+
509
+ `);
510
+ });
511
+ it("supports additional typed ConfigDict props", () => {
512
+ expect(_$createComponent(TestOutput, {
513
+ path: "models.py",
514
+ externals: [pydanticModule],
515
+ get children() {
516
+ return _$createComponent(py.PydanticClassDeclaration, {
517
+ name: "User",
518
+ useEnumValues: true,
519
+ coerceNumbersToStr: true,
520
+ validateReturn: true,
521
+ strMinLength: 1,
522
+ strMaxLength: 128,
523
+ serJsonBytes: "base64",
524
+ valJsonBytes: "hex"
525
+ }, {
526
+ fileName: import.meta.url,
527
+ lineNumber: 324,
528
+ columnNumber: 9
529
+ });
530
+ }
531
+ }, {
532
+ fileName: import.meta.url,
533
+ lineNumber: 323,
534
+ columnNumber: 7
535
+ })).toRenderTo(`
536
+ from pydantic import BaseModel
537
+ from pydantic import ConfigDict
538
+
539
+
540
+ class User(BaseModel):
541
+ 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)
542
+
543
+ `);
544
+ });
545
+ it("imports SecretStr when used as a field type", () => {
546
+ expect(_$createComponent(TestOutput, {
547
+ path: "models.py",
548
+ externals: [pydanticModule],
549
+ get children() {
550
+ return _$createComponent(py.PydanticClassDeclaration, {
551
+ name: "Credentials",
552
+ get children() {
553
+ return _$createComponent(py.VariableDeclaration, {
554
+ instanceVariable: true,
555
+ omitNone: true,
556
+ name: "token",
557
+ get type() {
558
+ return pydanticModule["."].SecretStr;
559
+ }
560
+ }, {
561
+ fileName: import.meta.url,
562
+ lineNumber: 352,
563
+ columnNumber: 11
564
+ });
565
+ }
566
+ }, {
567
+ fileName: import.meta.url,
568
+ lineNumber: 351,
569
+ columnNumber: 9
570
+ });
571
+ }
572
+ }, {
573
+ fileName: import.meta.url,
574
+ lineNumber: 350,
575
+ columnNumber: 7
576
+ })).toRenderTo(`
577
+ from pydantic import BaseModel
578
+ from typing import TYPE_CHECKING
579
+
580
+ if TYPE_CHECKING:
581
+ from pydantic import SecretStr
582
+
583
+
584
+ class Credentials(BaseModel):
585
+ token: SecretStr
586
+
587
+ `);
588
+ });
589
+ it("emits arbitrary model_config via modelConfigExpression", () => {
590
+ expect(_$createComponent(TestOutput, {
591
+ path: "models.py",
592
+ externals: [pydanticModule],
593
+ get children() {
594
+ return _$createComponent(py.PydanticClassDeclaration, {
595
+ name: "M",
596
+ get modelConfigExpression() {
597
+ return code`${pydanticModule["."].ConfigDict}(frozen=True, extra="allow")`;
598
+ }
599
+ }, {
600
+ fileName: import.meta.url,
601
+ lineNumber: 379,
602
+ columnNumber: 9
603
+ });
604
+ }
605
+ }, {
606
+ fileName: import.meta.url,
607
+ lineNumber: 378,
608
+ columnNumber: 7
609
+ })).toRenderTo(`
610
+ from pydantic import BaseModel
611
+ from pydantic import ConfigDict
612
+
613
+
614
+ class M(BaseModel):
615
+ model_config = ConfigDict(frozen=True, extra="allow")
616
+
617
+ `);
618
+ });
619
+ it("supports RootModel as explicit bases entry", () => {
620
+ expect(_$createComponent(TestOutput, {
621
+ path: "models.py",
622
+ externals: [pydanticModule],
623
+ get children() {
624
+ return _$createComponent(py.PydanticClassDeclaration, {
625
+ name: "Tags",
626
+ get bases() {
627
+ return [code`${pydanticModule["."].RootModel}[list[str]]`];
628
+ }
629
+ }, {
630
+ fileName: import.meta.url,
631
+ lineNumber: 400,
632
+ columnNumber: 9
633
+ });
634
+ }
635
+ }, {
636
+ fileName: import.meta.url,
637
+ lineNumber: 399,
638
+ columnNumber: 7
639
+ })).toRenderTo(`
640
+ from pydantic import RootModel
641
+
642
+
643
+ class Tags(RootModel[list[str]]):
644
+ pass
645
+
646
+ `);
647
+ });
648
+ it("places Pydantic validators above classmethod", () => {
649
+ expect(_$createComponent(TestOutput, {
650
+ path: "models.py",
651
+ externals: [pydanticModule],
652
+ get children() {
653
+ return _$createComponent(py.PydanticClassDeclaration, {
654
+ name: "User",
655
+ get children() {
656
+ return [_$createComponent(py.VariableDeclaration, {
657
+ instanceVariable: true,
658
+ omitNone: true,
659
+ name: "name",
660
+ type: "str"
661
+ }, {
662
+ fileName: import.meta.url,
663
+ lineNumber: 421,
664
+ columnNumber: 11
665
+ }), _$createComponent(py.ClassMethodDeclaration, {
666
+ name: "strip_name",
667
+ get decorators() {
668
+ return [code`@${pydanticModule["."].field_validator}("name", mode="before")`];
669
+ },
670
+ parameters: [{
671
+ name: "value",
672
+ type: "str"
673
+ }],
674
+ returnType: "str",
675
+ children: "return value.strip()"
676
+ }, {
677
+ fileName: import.meta.url,
678
+ lineNumber: 427,
679
+ columnNumber: 11
680
+ })];
681
+ }
682
+ }, {
683
+ fileName: import.meta.url,
684
+ lineNumber: 420,
685
+ columnNumber: 9
686
+ });
687
+ }
688
+ }, {
689
+ fileName: import.meta.url,
690
+ lineNumber: 419,
691
+ columnNumber: 7
692
+ })).toRenderTo(`
693
+ from pydantic import BaseModel
694
+ from pydantic import field_validator
695
+
696
+
697
+ class User(BaseModel):
698
+ name: str
699
+ @field_validator("name", mode="before")
700
+ @classmethod
701
+ def strip_name(cls, value: str) -> str:
702
+ return value.strip()
703
+
704
+
705
+ `);
706
+ });
707
+ });
708
+ describe("Pydantic ecosystem emitters", () => {
709
+ it("typing module resolves Any and similar annotations", () => {
710
+ expect(_$createComponent(TestOutput, {
711
+ path: "models.py",
712
+ externals: [typingModule],
713
+ get children() {
714
+ return _$createComponent(py.FunctionDeclaration, {
715
+ name: "identity",
716
+ get parameters() {
717
+ return [{
718
+ name: "x",
719
+ type: typingModule["."].Any
720
+ }];
721
+ },
722
+ get returnType() {
723
+ return typingModule["."].Any;
724
+ },
725
+ children: "return x"
726
+ }, {
727
+ fileName: import.meta.url,
728
+ lineNumber: 462,
729
+ columnNumber: 9
730
+ });
731
+ }
732
+ }, {
733
+ fileName: import.meta.url,
734
+ lineNumber: 461,
735
+ columnNumber: 7
736
+ })).toRenderTo(`
737
+ from typing import TYPE_CHECKING
738
+
739
+ if TYPE_CHECKING:
740
+ from typing import Any
741
+
742
+
743
+ def identity(x: Any) -> Any:
744
+ return x
745
+
746
+ `);
747
+ });
748
+ it("pydantic.types constrains field annotations", () => {
749
+ expect(_$createComponent(TestOutput, {
750
+ path: "models.py",
751
+ externals: [pydanticModule],
752
+ get children() {
753
+ return _$createComponent(py.PydanticClassDeclaration, {
754
+ name: "Score",
755
+ get children() {
756
+ return _$createComponent(py.VariableDeclaration, {
757
+ instanceVariable: true,
758
+ omitNone: true,
759
+ name: "points",
760
+ get type() {
761
+ return pydanticModule.types.PositiveInt;
762
+ }
763
+ }, {
764
+ fileName: import.meta.url,
765
+ lineNumber: 489,
766
+ columnNumber: 11
767
+ });
768
+ }
769
+ }, {
770
+ fileName: import.meta.url,
771
+ lineNumber: 488,
772
+ columnNumber: 9
773
+ });
774
+ }
775
+ }, {
776
+ fileName: import.meta.url,
777
+ lineNumber: 487,
778
+ columnNumber: 7
779
+ })).toRenderTo(`
780
+ from pydantic import BaseModel
781
+ from typing import TYPE_CHECKING
782
+
783
+ if TYPE_CHECKING:
784
+ from pydantic.types import PositiveInt
785
+
786
+
787
+ class Score(BaseModel):
788
+ points: PositiveInt
789
+
790
+ `);
791
+ });
792
+ it("postponed annotations support forward references in fields", () => {
793
+ expect(_$createComponent(TestOutputDirectory, {
794
+ externals: [pydanticModule],
795
+ get children() {
796
+ return _$createComponent(py.SourceFile, {
797
+ path: "models.py",
798
+ get futureImports() {
799
+ return [_$createComponent(py.FutureStatement, {
800
+ feature: "annotations"
801
+ }, {
802
+ fileName: import.meta.url,
803
+ lineNumber: 518,
804
+ columnNumber: 27
805
+ })];
806
+ },
807
+ get children() {
808
+ return _$createComponent(py.PydanticClassDeclaration, {
809
+ name: "Node",
810
+ get children() {
811
+ return [_$createComponent(py.VariableDeclaration, {
812
+ instanceVariable: true,
813
+ omitNone: true,
814
+ name: "label",
815
+ type: "str"
816
+ }, {
817
+ fileName: import.meta.url,
818
+ lineNumber: 521,
819
+ columnNumber: 13
820
+ }), _$createComponent(py.VariableDeclaration, {
821
+ instanceVariable: true,
822
+ name: "child",
823
+ type: code`"Node" | None`
824
+ }, {
825
+ fileName: import.meta.url,
826
+ lineNumber: 527,
827
+ columnNumber: 13
828
+ })];
829
+ }
830
+ }, {
831
+ fileName: import.meta.url,
832
+ lineNumber: 520,
833
+ columnNumber: 11
834
+ });
835
+ }
836
+ }, {
837
+ fileName: import.meta.url,
838
+ lineNumber: 516,
839
+ columnNumber: 9
840
+ });
841
+ }
842
+ }, {
843
+ fileName: import.meta.url,
844
+ lineNumber: 515,
845
+ columnNumber: 7
846
+ })).toRenderTo({
847
+ "models.py": `
848
+ from __future__ import annotations
849
+
850
+ from pydantic import BaseModel
851
+
852
+
853
+ class Node(BaseModel):
854
+ label: str
855
+ child: "Node" | None = None
856
+
857
+ `
858
+ });
859
+ });
860
+ it("model_config can use pydantic.alias_generators", () => {
861
+ expect(_$createComponent(TestOutput, {
862
+ path: "models.py",
863
+ externals: [pydanticModule],
864
+ get children() {
865
+ return _$createComponent(py.PydanticClassDeclaration, {
866
+ name: "M",
867
+ get modelConfigExpression() {
868
+ return code`${pydanticModule["."].ConfigDict}(alias_generator=${pydanticModule.alias_generators.to_camel})`;
869
+ }
870
+ }, {
871
+ fileName: import.meta.url,
872
+ lineNumber: 553,
873
+ columnNumber: 9
874
+ });
875
+ }
876
+ }, {
877
+ fileName: import.meta.url,
878
+ lineNumber: 552,
879
+ columnNumber: 7
880
+ })).toRenderTo(`
881
+ from pydantic import BaseModel
882
+ from pydantic import ConfigDict
883
+ from pydantic.alias_generators import to_camel
884
+
885
+
886
+ class M(BaseModel):
887
+ model_config = ConfigDict(alias_generator=to_camel)
888
+
889
+ `);
890
+ });
891
+ it("pydantic_settings exposes BaseSettings", () => {
892
+ expect(_$createComponent(TestOutput, {
893
+ path: "config.py",
894
+ externals: [pydanticSettingsModule],
895
+ get children() {
896
+ return _$createComponent(py.ClassDeclaration, {
897
+ name: "AppSettings",
898
+ get bases() {
899
+ return [pydanticSettingsModule["."].BaseSettings];
900
+ }
901
+ }, {
902
+ fileName: import.meta.url,
903
+ lineNumber: 575,
904
+ columnNumber: 9
905
+ });
906
+ }
907
+ }, {
908
+ fileName: import.meta.url,
909
+ lineNumber: 574,
910
+ columnNumber: 7
911
+ })).toRenderTo(`
912
+ from pydantic_settings import BaseSettings
913
+
914
+
915
+ class AppSettings(BaseSettings):
916
+ pass
917
+
918
+ `);
919
+ });
920
+ it("emits @computed_field above @property via PropertyDeclaration", () => {
921
+ expect(_$createComponent(TestOutput, {
922
+ path: "models.py",
923
+ externals: [pydanticModule],
924
+ get children() {
925
+ return _$createComponent(py.PydanticClassDeclaration, {
926
+ name: "Square",
927
+ get children() {
928
+ return [_$createComponent(py.VariableDeclaration, {
929
+ instanceVariable: true,
930
+ omitNone: true,
931
+ name: "width",
932
+ type: "float"
933
+ }, {
934
+ fileName: import.meta.url,
935
+ lineNumber: 596,
936
+ columnNumber: 11
937
+ }), _$createComponent(py.PropertyDeclaration, {
938
+ name: "area",
939
+ type: "float",
940
+ get decorators() {
941
+ return [code`@${pydanticModule["."].computed_field}`];
942
+ },
943
+ children: "return self.width ** 2"
944
+ }, {
945
+ fileName: import.meta.url,
946
+ lineNumber: 602,
947
+ columnNumber: 11
948
+ })];
949
+ }
950
+ }, {
951
+ fileName: import.meta.url,
952
+ lineNumber: 595,
953
+ columnNumber: 9
954
+ });
955
+ }
956
+ }, {
957
+ fileName: import.meta.url,
958
+ lineNumber: 594,
959
+ columnNumber: 7
960
+ })).toRenderTo(`
961
+ from pydantic import BaseModel
962
+ from pydantic import computed_field
963
+
964
+
965
+ class Square(BaseModel):
966
+ width: float
967
+ @computed_field
968
+ @property
969
+ def area(self) -> float:
970
+ return self.width ** 2
971
+
972
+
973
+ `);
974
+ });
975
+ it("emits computed_field on an instance method", () => {
976
+ expect(_$createComponent(TestOutput, {
977
+ path: "models.py",
978
+ externals: [pydanticModule],
979
+ get children() {
980
+ return _$createComponent(py.PydanticClassDeclaration, {
981
+ name: "Square",
982
+ get children() {
983
+ return [_$createComponent(py.VariableDeclaration, {
984
+ instanceVariable: true,
985
+ omitNone: true,
986
+ name: "width",
987
+ type: "float"
988
+ }, {
989
+ fileName: import.meta.url,
990
+ lineNumber: 633,
991
+ columnNumber: 11
992
+ }), _$createComponent(py.MethodDeclaration, {
993
+ name: "area",
994
+ get decorators() {
995
+ return [code`@${pydanticModule["."].computed_field}`];
996
+ },
997
+ returnType: "float",
998
+ children: "return self.width ** 2"
999
+ }, {
1000
+ fileName: import.meta.url,
1001
+ lineNumber: 639,
1002
+ columnNumber: 11
1003
+ })];
1004
+ }
1005
+ }, {
1006
+ fileName: import.meta.url,
1007
+ lineNumber: 632,
1008
+ columnNumber: 9
1009
+ });
1010
+ }
1011
+ }, {
1012
+ fileName: import.meta.url,
1013
+ lineNumber: 631,
1014
+ columnNumber: 7
1015
+ })).toRenderTo(`
1016
+ from pydantic import BaseModel
1017
+ from pydantic import computed_field
1018
+
1019
+
1020
+ class Square(BaseModel):
1021
+ width: float
1022
+ @computed_field
1023
+ def area(self) -> float:
1024
+ return self.width ** 2
1025
+
1026
+
1027
+ `);
1028
+ });
1029
+ it("emits model_validator above classmethod", () => {
1030
+ expect(_$createComponent(TestOutput, {
1031
+ path: "models.py",
1032
+ externals: [pydanticModule],
1033
+ get children() {
1034
+ return _$createComponent(py.PydanticClassDeclaration, {
1035
+ name: "Bag",
1036
+ get children() {
1037
+ return [_$createComponent(py.VariableDeclaration, {
1038
+ instanceVariable: true,
1039
+ omitNone: true,
1040
+ name: "items",
1041
+ type: "list"
1042
+ }, {
1043
+ fileName: import.meta.url,
1044
+ lineNumber: 669,
1045
+ columnNumber: 11
1046
+ }), _$createComponent(py.ClassMethodDeclaration, {
1047
+ name: "ensure_items",
1048
+ get decorators() {
1049
+ return [code`@${pydanticModule["."].model_validator}(mode="before")`];
1050
+ },
1051
+ parameters: [{
1052
+ name: "data",
1053
+ type: "dict"
1054
+ }],
1055
+ returnType: "dict",
1056
+ children: "return data"
1057
+ }, {
1058
+ fileName: import.meta.url,
1059
+ lineNumber: 675,
1060
+ columnNumber: 11
1061
+ })];
1062
+ }
1063
+ }, {
1064
+ fileName: import.meta.url,
1065
+ lineNumber: 668,
1066
+ columnNumber: 9
1067
+ });
1068
+ }
1069
+ }, {
1070
+ fileName: import.meta.url,
1071
+ lineNumber: 667,
1072
+ columnNumber: 7
1073
+ })).toRenderTo(`
1074
+ from pydantic import BaseModel
1075
+ from pydantic import model_validator
1076
+
1077
+
1078
+ class Bag(BaseModel):
1079
+ items: list
1080
+ @model_validator(mode="before")
1081
+ @classmethod
1082
+ def ensure_items(cls, data: dict) -> dict:
1083
+ return data
1084
+
1085
+
1086
+ `);
1087
+ });
1088
+ });
1089
+ //# sourceMappingURL=pydanticclassdeclarations.test.js.map