@alloy-js/python 0.5.0-dev.1 → 0.5.0

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