@typespec/emitter-framework 0.15.0-dev.3 → 0.15.0-dev.4

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 (253) hide show
  1. package/CHANGELOG.md +1 -16
  2. package/dist/src/python/builtins.d.ts +14 -0
  3. package/dist/src/python/builtins.d.ts.map +1 -0
  4. package/dist/src/python/builtins.js +29 -0
  5. package/dist/src/python/builtins.js.map +1 -0
  6. package/dist/src/python/components/array-expression/array-expression.d.ts +6 -0
  7. package/dist/src/python/components/array-expression/array-expression.d.ts.map +1 -0
  8. package/dist/src/python/components/array-expression/array-expression.js +11 -0
  9. package/dist/src/python/components/array-expression/array-expression.js.map +1 -0
  10. package/dist/src/python/components/array-expression/array-expression.test.d.ts +2 -0
  11. package/dist/src/python/components/array-expression/array-expression.test.d.ts.map +1 -0
  12. package/dist/src/python/components/array-expression/array-expression.test.js +19 -0
  13. package/dist/src/python/components/array-expression/array-expression.test.js.map +1 -0
  14. package/dist/src/python/components/array-expression/index.d.ts +2 -0
  15. package/dist/src/python/components/array-expression/index.d.ts.map +1 -0
  16. package/dist/src/python/components/array-expression/index.js +2 -0
  17. package/dist/src/python/components/array-expression/index.js.map +1 -0
  18. package/dist/src/python/components/atom/atom.d.ts +33 -0
  19. package/dist/src/python/components/atom/atom.d.ts.map +1 -0
  20. package/dist/src/python/components/atom/atom.js +88 -0
  21. package/dist/src/python/components/atom/atom.js.map +1 -0
  22. package/dist/src/python/components/atom/atom.test.d.ts +2 -0
  23. package/dist/src/python/components/atom/atom.test.d.ts.map +1 -0
  24. package/dist/src/python/components/atom/atom.test.js +224 -0
  25. package/dist/src/python/components/atom/atom.test.js.map +1 -0
  26. package/dist/src/python/components/atom/index.d.ts +2 -0
  27. package/dist/src/python/components/atom/index.d.ts.map +1 -0
  28. package/dist/src/python/components/atom/index.js +2 -0
  29. package/dist/src/python/components/atom/index.js.map +1 -0
  30. package/dist/src/python/components/class-declaration/class-bases.d.ts +45 -0
  31. package/dist/src/python/components/class-declaration/class-bases.d.ts.map +1 -0
  32. package/dist/src/python/components/class-declaration/class-bases.js +84 -0
  33. package/dist/src/python/components/class-declaration/class-bases.js.map +1 -0
  34. package/dist/src/python/components/class-declaration/class-body.d.ts +16 -0
  35. package/dist/src/python/components/class-declaration/class-body.d.ts.map +1 -0
  36. package/dist/src/python/components/class-declaration/class-body.js +52 -0
  37. package/dist/src/python/components/class-declaration/class-body.js.map +1 -0
  38. package/dist/src/python/components/class-declaration/class-declaration.d.ts +23 -0
  39. package/dist/src/python/components/class-declaration/class-declaration.d.ts.map +1 -0
  40. package/dist/src/python/components/class-declaration/class-declaration.js +118 -0
  41. package/dist/src/python/components/class-declaration/class-declaration.js.map +1 -0
  42. package/dist/src/python/components/class-declaration/class-declaration.test.d.ts +2 -0
  43. package/dist/src/python/components/class-declaration/class-declaration.test.d.ts.map +1 -0
  44. package/dist/src/python/components/class-declaration/class-declaration.test.js +1527 -0
  45. package/dist/src/python/components/class-declaration/class-declaration.test.js.map +1 -0
  46. package/dist/src/python/components/class-declaration/class-member.d.ts +16 -0
  47. package/dist/src/python/components/class-declaration/class-member.d.ts.map +1 -0
  48. package/dist/src/python/components/class-declaration/class-member.js +71 -0
  49. package/dist/src/python/components/class-declaration/class-member.js.map +1 -0
  50. package/dist/src/python/components/class-declaration/class-member.test.d.ts +2 -0
  51. package/dist/src/python/components/class-declaration/class-member.test.d.ts.map +1 -0
  52. package/dist/src/python/components/class-declaration/class-member.test.js +202 -0
  53. package/dist/src/python/components/class-declaration/class-member.test.js.map +1 -0
  54. package/dist/src/python/components/class-declaration/class-method.d.ts +22 -0
  55. package/dist/src/python/components/class-declaration/class-method.d.ts.map +1 -0
  56. package/dist/src/python/components/class-declaration/class-method.js +76 -0
  57. package/dist/src/python/components/class-declaration/class-method.js.map +1 -0
  58. package/dist/src/python/components/class-declaration/class-method.test.d.ts +2 -0
  59. package/dist/src/python/components/class-declaration/class-method.test.d.ts.map +1 -0
  60. package/dist/src/python/components/class-declaration/class-method.test.js +298 -0
  61. package/dist/src/python/components/class-declaration/class-method.test.js.map +1 -0
  62. package/dist/src/python/components/class-declaration/index.d.ts +7 -0
  63. package/dist/src/python/components/class-declaration/index.d.ts.map +1 -0
  64. package/dist/src/python/components/class-declaration/index.js +7 -0
  65. package/dist/src/python/components/class-declaration/index.js.map +1 -0
  66. package/dist/src/python/components/class-declaration/primitive-initializer.d.ts +22 -0
  67. package/dist/src/python/components/class-declaration/primitive-initializer.d.ts.map +1 -0
  68. package/dist/src/python/components/class-declaration/primitive-initializer.js +67 -0
  69. package/dist/src/python/components/class-declaration/primitive-initializer.js.map +1 -0
  70. package/dist/src/python/components/doc-element/doc-element.d.ts +46 -0
  71. package/dist/src/python/components/doc-element/doc-element.d.ts.map +1 -0
  72. package/dist/src/python/components/doc-element/doc-element.js +59 -0
  73. package/dist/src/python/components/doc-element/doc-element.js.map +1 -0
  74. package/dist/src/python/components/doc-element/index.d.ts +2 -0
  75. package/dist/src/python/components/doc-element/index.d.ts.map +1 -0
  76. package/dist/src/python/components/doc-element/index.js +2 -0
  77. package/dist/src/python/components/doc-element/index.js.map +1 -0
  78. package/dist/src/python/components/enum-declaration/enum-declaration.d.ts +8 -0
  79. package/dist/src/python/components/enum-declaration/enum-declaration.d.ts.map +1 -0
  80. package/dist/src/python/components/enum-declaration/enum-declaration.js +80 -0
  81. package/dist/src/python/components/enum-declaration/enum-declaration.js.map +1 -0
  82. package/dist/src/python/components/enum-declaration/enum-declaration.test.d.ts +2 -0
  83. package/dist/src/python/components/enum-declaration/enum-declaration.test.d.ts.map +1 -0
  84. package/dist/src/python/components/enum-declaration/enum-declaration.test.js +344 -0
  85. package/dist/src/python/components/enum-declaration/enum-declaration.test.js.map +1 -0
  86. package/dist/src/python/components/enum-declaration/enum-member.d.ts +9 -0
  87. package/dist/src/python/components/enum-declaration/enum-member.d.ts.map +1 -0
  88. package/dist/src/python/components/enum-declaration/enum-member.js +22 -0
  89. package/dist/src/python/components/enum-declaration/enum-member.js.map +1 -0
  90. package/dist/src/python/components/enum-declaration/index.d.ts +3 -0
  91. package/dist/src/python/components/enum-declaration/index.d.ts.map +1 -0
  92. package/dist/src/python/components/enum-declaration/index.js +3 -0
  93. package/dist/src/python/components/enum-declaration/index.js.map +1 -0
  94. package/dist/src/python/components/function-declaration/function-declaration.d.ts +24 -0
  95. package/dist/src/python/components/function-declaration/function-declaration.d.ts.map +1 -0
  96. package/dist/src/python/components/function-declaration/function-declaration.js +68 -0
  97. package/dist/src/python/components/function-declaration/function-declaration.js.map +1 -0
  98. package/dist/src/python/components/function-declaration/function-declaration.test.d.ts +2 -0
  99. package/dist/src/python/components/function-declaration/function-declaration.test.d.ts.map +1 -0
  100. package/dist/src/python/components/function-declaration/function-declaration.test.js +682 -0
  101. package/dist/src/python/components/function-declaration/function-declaration.test.js.map +1 -0
  102. package/dist/src/python/components/function-declaration/index.d.ts +2 -0
  103. package/dist/src/python/components/function-declaration/index.d.ts.map +1 -0
  104. package/dist/src/python/components/function-declaration/index.js +2 -0
  105. package/dist/src/python/components/function-declaration/index.js.map +1 -0
  106. package/dist/src/python/components/index.d.ts +12 -0
  107. package/dist/src/python/components/index.d.ts.map +1 -0
  108. package/dist/src/python/components/index.js +12 -0
  109. package/dist/src/python/components/index.js.map +1 -0
  110. package/dist/src/python/components/protocol-declaration/callable-parameters.d.ts +25 -0
  111. package/dist/src/python/components/protocol-declaration/callable-parameters.d.ts.map +1 -0
  112. package/dist/src/python/components/protocol-declaration/callable-parameters.js +33 -0
  113. package/dist/src/python/components/protocol-declaration/callable-parameters.js.map +1 -0
  114. package/dist/src/python/components/protocol-declaration/index.d.ts +3 -0
  115. package/dist/src/python/components/protocol-declaration/index.d.ts.map +1 -0
  116. package/dist/src/python/components/protocol-declaration/index.js +3 -0
  117. package/dist/src/python/components/protocol-declaration/index.js.map +1 -0
  118. package/dist/src/python/components/protocol-declaration/protocol-declaration.d.ts +8 -0
  119. package/dist/src/python/components/protocol-declaration/protocol-declaration.d.ts.map +1 -0
  120. package/dist/src/python/components/protocol-declaration/protocol-declaration.js +86 -0
  121. package/dist/src/python/components/protocol-declaration/protocol-declaration.js.map +1 -0
  122. package/dist/src/python/components/protocol-declaration/protocol-declaration.test.d.ts +2 -0
  123. package/dist/src/python/components/protocol-declaration/protocol-declaration.test.d.ts.map +1 -0
  124. package/dist/src/python/components/protocol-declaration/protocol-declaration.test.js +117 -0
  125. package/dist/src/python/components/protocol-declaration/protocol-declaration.test.js.map +1 -0
  126. package/dist/src/python/components/record-expression/index.d.ts +2 -0
  127. package/dist/src/python/components/record-expression/index.d.ts.map +1 -0
  128. package/dist/src/python/components/record-expression/index.js +2 -0
  129. package/dist/src/python/components/record-expression/index.js.map +1 -0
  130. package/dist/src/python/components/record-expression/record-expression.d.ts +6 -0
  131. package/dist/src/python/components/record-expression/record-expression.d.ts.map +1 -0
  132. package/dist/src/python/components/record-expression/record-expression.js +13 -0
  133. package/dist/src/python/components/record-expression/record-expression.js.map +1 -0
  134. package/dist/src/python/components/record-expression/record-expression.test.d.ts +2 -0
  135. package/dist/src/python/components/record-expression/record-expression.test.d.ts.map +1 -0
  136. package/dist/src/python/components/record-expression/record-expression.test.js +19 -0
  137. package/dist/src/python/components/record-expression/record-expression.test.js.map +1 -0
  138. package/dist/src/python/components/type-alias-declaration/index.d.ts +2 -0
  139. package/dist/src/python/components/type-alias-declaration/index.d.ts.map +1 -0
  140. package/dist/src/python/components/type-alias-declaration/index.js +2 -0
  141. package/dist/src/python/components/type-alias-declaration/index.js.map +1 -0
  142. package/dist/src/python/components/type-alias-declaration/type-alias-declaration.d.ts +16 -0
  143. package/dist/src/python/components/type-alias-declaration/type-alias-declaration.d.ts.map +1 -0
  144. package/dist/src/python/components/type-alias-declaration/type-alias-declaration.js +75 -0
  145. package/dist/src/python/components/type-alias-declaration/type-alias-declaration.js.map +1 -0
  146. package/dist/src/python/components/type-alias-declaration/type-alias-declaration.test.d.ts +2 -0
  147. package/dist/src/python/components/type-alias-declaration/type-alias-declaration.test.d.ts.map +1 -0
  148. package/dist/src/python/components/type-alias-declaration/type-alias-declaration.test.js +140 -0
  149. package/dist/src/python/components/type-alias-declaration/type-alias-declaration.test.js.map +1 -0
  150. package/dist/src/python/components/type-declaration/index.d.ts +2 -0
  151. package/dist/src/python/components/type-declaration/index.d.ts.map +1 -0
  152. package/dist/src/python/components/type-declaration/index.js +2 -0
  153. package/dist/src/python/components/type-declaration/index.js.map +1 -0
  154. package/dist/src/python/components/type-declaration/type-declaration.d.ts +11 -0
  155. package/dist/src/python/components/type-declaration/type-declaration.d.ts.map +1 -0
  156. package/dist/src/python/components/type-declaration/type-declaration.js +31 -0
  157. package/dist/src/python/components/type-declaration/type-declaration.js.map +1 -0
  158. package/dist/src/python/components/type-declaration/type-declaration.test.d.ts +2 -0
  159. package/dist/src/python/components/type-declaration/type-declaration.test.d.ts.map +1 -0
  160. package/dist/src/python/components/type-declaration/type-declaration.test.js +69 -0
  161. package/dist/src/python/components/type-declaration/type-declaration.test.js.map +1 -0
  162. package/dist/src/python/components/type-expression/index.d.ts +2 -0
  163. package/dist/src/python/components/type-expression/index.d.ts.map +1 -0
  164. package/dist/src/python/components/type-expression/index.js +2 -0
  165. package/dist/src/python/components/type-expression/index.js.map +1 -0
  166. package/dist/src/python/components/type-expression/type-expression.d.ts +12 -0
  167. package/dist/src/python/components/type-expression/type-expression.d.ts.map +1 -0
  168. package/dist/src/python/components/type-expression/type-expression.js +348 -0
  169. package/dist/src/python/components/type-expression/type-expression.js.map +1 -0
  170. package/dist/src/python/components/type-expression/type-expression.test.d.ts +2 -0
  171. package/dist/src/python/components/type-expression/type-expression.test.d.ts.map +1 -0
  172. package/dist/src/python/components/type-expression/type-expression.test.js +503 -0
  173. package/dist/src/python/components/type-expression/type-expression.test.js.map +1 -0
  174. package/dist/src/python/index.d.ts +4 -0
  175. package/dist/src/python/index.d.ts.map +1 -0
  176. package/dist/src/python/index.js +4 -0
  177. package/dist/src/python/index.js.map +1 -0
  178. package/dist/src/python/lib.d.ts +68 -0
  179. package/dist/src/python/lib.d.ts.map +1 -0
  180. package/dist/src/python/lib.js +38 -0
  181. package/dist/src/python/lib.js.map +1 -0
  182. package/dist/src/python/test-utils.d.ts +8 -0
  183. package/dist/src/python/test-utils.d.ts.map +1 -0
  184. package/dist/src/python/test-utils.js +25 -0
  185. package/dist/src/python/test-utils.js.map +1 -0
  186. package/dist/src/python/utils/index.d.ts +4 -0
  187. package/dist/src/python/utils/index.d.ts.map +1 -0
  188. package/dist/src/python/utils/index.js +4 -0
  189. package/dist/src/python/utils/index.js.map +1 -0
  190. package/dist/src/python/utils/operation.d.ts +27 -0
  191. package/dist/src/python/utils/operation.d.ts.map +1 -0
  192. package/dist/src/python/utils/operation.js +139 -0
  193. package/dist/src/python/utils/operation.js.map +1 -0
  194. package/dist/src/python/utils/refkey.d.ts +23 -0
  195. package/dist/src/python/utils/refkey.d.ts.map +1 -0
  196. package/dist/src/python/utils/refkey.js +36 -0
  197. package/dist/src/python/utils/refkey.js.map +1 -0
  198. package/dist/src/python/utils/type.d.ts +19 -0
  199. package/dist/src/python/utils/type.d.ts.map +1 -0
  200. package/dist/src/python/utils/type.js +24 -0
  201. package/dist/src/python/utils/type.js.map +1 -0
  202. package/package.json +9 -1
  203. package/package.json.bak +10 -1
  204. package/src/python/builtins.ts +43 -0
  205. package/src/python/components/array-expression/array-expression.test.tsx +14 -0
  206. package/src/python/components/array-expression/array-expression.tsx +11 -0
  207. package/src/python/components/array-expression/index.ts +1 -0
  208. package/src/python/components/atom/atom.test.tsx +244 -0
  209. package/src/python/components/atom/atom.tsx +95 -0
  210. package/src/python/components/atom/index.ts +1 -0
  211. package/src/python/components/class-declaration/class-bases.tsx +92 -0
  212. package/src/python/components/class-declaration/class-body.tsx +56 -0
  213. package/src/python/components/class-declaration/class-declaration.test.tsx +1414 -0
  214. package/src/python/components/class-declaration/class-declaration.tsx +116 -0
  215. package/src/python/components/class-declaration/class-member.test.tsx +194 -0
  216. package/src/python/components/class-declaration/class-member.tsx +67 -0
  217. package/src/python/components/class-declaration/class-method.test.tsx +250 -0
  218. package/src/python/components/class-declaration/class-method.tsx +97 -0
  219. package/src/python/components/class-declaration/index.ts +6 -0
  220. package/src/python/components/class-declaration/primitive-initializer.tsx +62 -0
  221. package/src/python/components/doc-element/doc-element.tsx +83 -0
  222. package/src/python/components/doc-element/index.ts +1 -0
  223. package/src/python/components/enum-declaration/enum-declaration.test.tsx +319 -0
  224. package/src/python/components/enum-declaration/enum-declaration.tsx +77 -0
  225. package/src/python/components/enum-declaration/enum-member.tsx +21 -0
  226. package/src/python/components/enum-declaration/index.ts +2 -0
  227. package/src/python/components/function-declaration/function-declaration.test.tsx +582 -0
  228. package/src/python/components/function-declaration/function-declaration.tsx +90 -0
  229. package/src/python/components/function-declaration/index.ts +1 -0
  230. package/src/python/components/index.ts +11 -0
  231. package/src/python/components/protocol-declaration/callable-parameters.tsx +44 -0
  232. package/src/python/components/protocol-declaration/index.ts +2 -0
  233. package/src/python/components/protocol-declaration/protocol-declaration.test.tsx +106 -0
  234. package/src/python/components/protocol-declaration/protocol-declaration.tsx +73 -0
  235. package/src/python/components/record-expression/index.ts +1 -0
  236. package/src/python/components/record-expression/record-expression.test.tsx +14 -0
  237. package/src/python/components/record-expression/record-expression.tsx +13 -0
  238. package/src/python/components/type-alias-declaration/index.ts +1 -0
  239. package/src/python/components/type-alias-declaration/type-alias-declaration.test.tsx +117 -0
  240. package/src/python/components/type-alias-declaration/type-alias-declaration.tsx +67 -0
  241. package/src/python/components/type-declaration/index.ts +1 -0
  242. package/src/python/components/type-declaration/type-declaration.test.tsx +58 -0
  243. package/src/python/components/type-declaration/type-declaration.tsx +26 -0
  244. package/src/python/components/type-expression/index.ts +1 -0
  245. package/src/python/components/type-expression/type-expression.test.tsx +463 -0
  246. package/src/python/components/type-expression/type-expression.tsx +333 -0
  247. package/src/python/index.ts +3 -0
  248. package/src/python/lib.ts +40 -0
  249. package/src/python/test-utils.tsx +31 -0
  250. package/src/python/utils/index.ts +3 -0
  251. package/src/python/utils/operation.ts +161 -0
  252. package/src/python/utils/refkey.ts +36 -0
  253. package/src/python/utils/type.ts +31 -0
@@ -0,0 +1,1527 @@
1
+ import { createComponent as _$createComponent, createIntrinsic as _$createIntrinsic } from "@alloy-js/core/jsx-runtime";
2
+ import { Tester } from "#test/test-host.js";
3
+ import { List } from "@alloy-js/core";
4
+ import * as py from "@alloy-js/python";
5
+ import { t } from "@typespec/compiler/testing";
6
+ import { describe, expect, it } from "vitest";
7
+ import { ClassDeclaration } from "../../../../src/python/components/class-declaration/class-declaration.js";
8
+ import { Method } from "../../../../src/python/components/class-declaration/class-method.js";
9
+ import { EnumDeclaration } from "../../../../src/python/components/enum-declaration/enum-declaration.js";
10
+ import { getOutput } from "../../test-utils.js";
11
+ import { TypeAliasDeclaration } from "../type-alias-declaration/type-alias-declaration.js";
12
+ describe("Python Class from model", () => {
13
+ it("creates a class", async () => {
14
+ const {
15
+ program,
16
+ Widget
17
+ } = await Tester.compile(t.code`
18
+
19
+ model ${t.model("Widget")} {
20
+ id: string;
21
+ weight: int32;
22
+ aliases: string[];
23
+ isActive: boolean;
24
+ color: "blue" | "red";
25
+ promotionalPrice: float64;
26
+ description?: string = "This is a widget";
27
+ createdAt: int64 = 1717334400;
28
+ tags: string[] = #["tag1", "tag2"];
29
+ isDeleted: boolean = false;
30
+ alternativeColor: "green" | "yellow" = "green";
31
+ price: float64 = 100.0;
32
+ }
33
+ `);
34
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
35
+ type: Widget
36
+ })])).toRenderTo(`
37
+ from dataclasses import dataclass
38
+ from typing import Literal
39
+ from typing import Optional
40
+
41
+
42
+ @dataclass(kw_only=True)
43
+ class Widget:
44
+ id: str
45
+ weight: int
46
+ aliases: list[str]
47
+ is_active: bool
48
+ color: Literal["blue", "red"]
49
+ promotional_price: float
50
+ description: Optional[str] = "This is a widget"
51
+ created_at: int = 1717334400
52
+ tags: list[str] = ["tag1", "tag2"]
53
+ is_deleted: bool = False
54
+ alternative_color: Literal["green", "yellow"] = "green"
55
+ price: float = 100.0
56
+
57
+ `);
58
+ });
59
+ it("creates a class with non-default values followed by default values", async () => {
60
+ const {
61
+ program,
62
+ Widget
63
+ } = await Tester.compile(t.code`
64
+
65
+ model ${t.model("Widget")} {
66
+ id: string;
67
+ description?: string = "This is a widget";
68
+ }
69
+ `);
70
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
71
+ type: Widget
72
+ })])).toRenderTo(`
73
+ from dataclasses import dataclass
74
+ from typing import Optional
75
+
76
+
77
+ @dataclass(kw_only=True)
78
+ class Widget:
79
+ id: str
80
+ description: Optional[str] = "This is a widget"
81
+
82
+ `);
83
+ });
84
+ it("creates a class with non-default values followed by default values", async () => {
85
+ const {
86
+ program,
87
+ Widget
88
+ } = await Tester.compile(t.code`
89
+
90
+ model ${t.model("Widget")} {
91
+ description?: string = "This is a widget";
92
+ id: string;
93
+ }
94
+ `);
95
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
96
+ type: Widget
97
+ })])).toRenderTo(`
98
+ from dataclasses import dataclass
99
+ from typing import Optional
100
+
101
+
102
+ @dataclass(kw_only=True)
103
+ class Widget:
104
+ description: Optional[str] = "This is a widget"
105
+ id: str
106
+
107
+ `);
108
+ });
109
+ it("declares a class with multi line docs", async () => {
110
+ const {
111
+ program,
112
+ Foo
113
+ } = await Tester.compile(t.code`
114
+ /**
115
+ * This is a test
116
+ * with multiple lines
117
+ */
118
+ model ${t.model("Foo")} {
119
+ knownProp: string;
120
+ }
121
+ `);
122
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
123
+ type: Foo
124
+ })])).toRenderTo(`
125
+ from dataclasses import dataclass
126
+
127
+
128
+ @dataclass(kw_only=True)
129
+ class Foo:
130
+ """
131
+ This is a test
132
+ with multiple lines
133
+ """
134
+
135
+ known_prop: str
136
+
137
+ `);
138
+ });
139
+ it("declares a class overriding docs", async () => {
140
+ const {
141
+ program,
142
+ Foo
143
+ } = await Tester.compile(t.code`
144
+ /**
145
+ * This is a test
146
+ * with multiple lines
147
+ */
148
+ model ${t.model("Foo")} {
149
+ knownProp: string;
150
+ }
151
+ `);
152
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
153
+ type: Foo,
154
+ doc: ["This is an overridden doc comment\nwith multiple lines"]
155
+ })])).toRenderTo(`
156
+ from dataclasses import dataclass
157
+
158
+
159
+ @dataclass(kw_only=True)
160
+ class Foo:
161
+ """
162
+ This is an overridden doc comment
163
+ with multiple lines
164
+ """
165
+
166
+ known_prop: str
167
+
168
+ `);
169
+ });
170
+ it("declares a class overriding docs with paragraphs array", async () => {
171
+ const {
172
+ program,
173
+ Foo
174
+ } = await Tester.compile(t.code`
175
+ /**
176
+ * Base doc will be overridden
177
+ */
178
+ model ${t.model("Foo")} {
179
+ knownProp: string;
180
+ }
181
+ `);
182
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
183
+ type: Foo,
184
+ doc: ["First paragraph", "Second paragraph"]
185
+ })])).toRenderTo(`
186
+ from dataclasses import dataclass
187
+
188
+
189
+ @dataclass(kw_only=True)
190
+ class Foo:
191
+ """
192
+ First paragraph
193
+
194
+ Second paragraph
195
+ """
196
+
197
+ known_prop: str
198
+
199
+ `);
200
+ });
201
+ it("declares a class overriding docs with prebuilt ClassDoc", async () => {
202
+ const {
203
+ program,
204
+ Foo
205
+ } = await Tester.compile(t.code`
206
+ /**
207
+ * Base doc will be overridden
208
+ */
209
+ model ${t.model("Foo")} {
210
+ knownProp: string;
211
+ }
212
+ `);
213
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
214
+ type: Foo,
215
+ get doc() {
216
+ return _$createComponent(py.ClassDoc, {
217
+ get description() {
218
+ return [["Alpha"], ["Beta"]];
219
+ }
220
+ });
221
+ }
222
+ })])).toRenderTo(`
223
+ from dataclasses import dataclass
224
+
225
+
226
+ @dataclass(kw_only=True)
227
+ class Foo:
228
+ """
229
+ Alpha
230
+
231
+ Beta
232
+ """
233
+
234
+ known_prop: str
235
+
236
+ `);
237
+ });
238
+ it("declares a class from a model with @doc", async () => {
239
+ const {
240
+ program,
241
+ Foo
242
+ } = await Tester.compile(t.code`
243
+ @doc("This is a test")
244
+ model ${t.model("Foo")} {
245
+ knownProp: string;
246
+ }
247
+ `);
248
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
249
+ type: Foo
250
+ })])).toRenderTo(`
251
+ from dataclasses import dataclass
252
+
253
+
254
+ @dataclass(kw_only=True)
255
+ class Foo:
256
+ """
257
+ This is a test
258
+ """
259
+
260
+ known_prop: str
261
+
262
+ `);
263
+ });
264
+ it("declares a model property with @doc", async () => {
265
+ const {
266
+ program,
267
+ Foo
268
+ } = await Tester.compile(t.code`
269
+ /**
270
+ * This is a test
271
+ */
272
+ model ${t.model("Foo")} {
273
+ @doc("This is a known property")
274
+ knownProp: string;
275
+ }
276
+ `);
277
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
278
+ type: Foo
279
+ })])).toRenderTo(`
280
+ from dataclasses import dataclass
281
+
282
+
283
+ @dataclass(kw_only=True)
284
+ class Foo:
285
+ """
286
+ This is a test
287
+ """
288
+
289
+ # This is a known property
290
+ known_prop: str
291
+
292
+ `);
293
+ });
294
+ it("throws error for model is Record<T>", async () => {
295
+ const {
296
+ program,
297
+ Person
298
+ } = await Tester.compile(t.code`
299
+ model ${t.model("Person")} is Record<string>;
300
+ `);
301
+ expect(() => {
302
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
303
+ type: Person
304
+ })])).toRenderTo("");
305
+ }).toThrow(/Models with additional properties \(Record\[…\]\) are not supported/);
306
+ });
307
+ it("throws error for model is Record<string> with properties", async () => {
308
+ const {
309
+ program,
310
+ Person
311
+ } = await Tester.compile(t.code`
312
+ model ${t.model("Person")} is Record<string> {
313
+ name: string;
314
+ }
315
+ `);
316
+ expect(() => {
317
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
318
+ type: Person
319
+ })])).toRenderTo("");
320
+ }).toThrow(/Models with additional properties \(Record\[…\]\) are not supported/);
321
+ });
322
+ it("throws error for model extends Record<string>", async () => {
323
+ const {
324
+ program,
325
+ Person
326
+ } = await Tester.compile(t.code`
327
+ model ${t.model("Person")} extends Record<string> {
328
+ name: string;
329
+ }
330
+ `);
331
+ expect(() => {
332
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
333
+ type: Person
334
+ })])).toRenderTo("");
335
+ }).toThrow(/Models with additional properties \(Record\[…\]\) are not supported/);
336
+ });
337
+ it("throws error for model with ...Record<string>", async () => {
338
+ const {
339
+ program,
340
+ Person
341
+ } = await Tester.compile(t.code`
342
+ model ${t.model("Person")} {
343
+ age: int32;
344
+ ...Record<string>;
345
+ }
346
+ `);
347
+ expect(() => {
348
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
349
+ type: Person
350
+ })])).toRenderTo("");
351
+ }).toThrow(/Models with additional properties \(Record\[…\]\) are not supported/);
352
+ });
353
+ it("creates a class from a model that 'is' an array ", async () => {
354
+ const {
355
+ program,
356
+ Foo
357
+ } = await Tester.compile(t.code`
358
+ model ${t.model("Foo")} is Array<string>;
359
+ `);
360
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
361
+ type: Foo
362
+ })])).toRenderTo(`
363
+ class Foo(list[str]):
364
+ pass
365
+
366
+ `);
367
+ });
368
+ it("handles a type reference to a union variant in a class property", async () => {
369
+ const {
370
+ program,
371
+ Color,
372
+ Widget
373
+ } = await Tester.compile(t.code`
374
+ union ${t.union("Color")} {
375
+ red: "RED",
376
+ blue: "BLUE",
377
+ }
378
+
379
+ model ${t.model("Widget")} {
380
+ id: string = "123";
381
+ weight: int32 = 100;
382
+ color: Color.blue;
383
+ }
384
+ `);
385
+ expect(getOutput(program, [_$createComponent(EnumDeclaration, {
386
+ type: Color
387
+ }), _$createComponent(ClassDeclaration, {
388
+ type: Widget
389
+ })])).toRenderTo(`
390
+ from dataclasses import dataclass
391
+ from enum import StrEnum
392
+ from typing import Literal
393
+
394
+
395
+ class Color(StrEnum):
396
+ RED = "RED"
397
+ BLUE = "BLUE"
398
+
399
+
400
+ @dataclass(kw_only=True)
401
+ class Widget:
402
+ id: str = "123"
403
+ weight: int = 100
404
+ color: Literal[Color.BLUE]
405
+
406
+ `);
407
+ });
408
+ it("handles a union of variant references in a class property", async () => {
409
+ const {
410
+ program,
411
+ Color,
412
+ Widget
413
+ } = await Tester.compile(t.code`
414
+ union ${t.union("Color")} {
415
+ red: "RED",
416
+ blue: "BLUE",
417
+ green: "GREEN",
418
+ }
419
+
420
+ model ${t.model("Widget")} {
421
+ id: string;
422
+ primaryColor: Color.red | Color.blue;
423
+ }
424
+ `);
425
+ expect(getOutput(program, [_$createComponent(EnumDeclaration, {
426
+ type: Color
427
+ }), _$createComponent(ClassDeclaration, {
428
+ type: Widget
429
+ })])).toRenderTo(`
430
+ from dataclasses import dataclass
431
+ from enum import StrEnum
432
+ from typing import Literal
433
+
434
+
435
+ class Color(StrEnum):
436
+ RED = "RED"
437
+ BLUE = "BLUE"
438
+ GREEN = "GREEN"
439
+
440
+
441
+ @dataclass(kw_only=True)
442
+ class Widget:
443
+ id: str
444
+ primary_color: Literal[Color.RED, Color.BLUE]
445
+
446
+ `);
447
+ });
448
+ it("handles a union of integer literals in a class property", async () => {
449
+ const {
450
+ program,
451
+ Widget
452
+ } = await Tester.compile(t.code`
453
+ model ${t.model("Widget")} {
454
+ id: string;
455
+ priority: 1 | 2 | 3;
456
+ }
457
+ `);
458
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
459
+ type: Widget
460
+ })])).toRenderTo(`
461
+ from dataclasses import dataclass
462
+ from typing import Literal
463
+
464
+
465
+ @dataclass(kw_only=True)
466
+ class Widget:
467
+ id: str
468
+ priority: Literal[1, 2, 3]
469
+
470
+ `);
471
+ });
472
+ it("handles a union of boolean literals in a class property", async () => {
473
+ const {
474
+ program,
475
+ Widget
476
+ } = await Tester.compile(t.code`
477
+ model ${t.model("Widget")} {
478
+ id: string;
479
+ isActiveOrEnabled: true | false;
480
+ }
481
+ `);
482
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
483
+ type: Widget
484
+ })])).toRenderTo(`
485
+ from dataclasses import dataclass
486
+ from typing import Literal
487
+
488
+
489
+ @dataclass(kw_only=True)
490
+ class Widget:
491
+ id: str
492
+ is_active_or_enabled: Literal[True, False]
493
+
494
+ `);
495
+ });
496
+ it("handles a mixed union of literals and variant references", async () => {
497
+ const {
498
+ program,
499
+ Color,
500
+ Widget
501
+ } = await Tester.compile(t.code`
502
+ union ${t.union("Color")} {
503
+ red: "RED",
504
+ blue: "BLUE",
505
+ }
506
+
507
+ model ${t.model("Widget")} {
508
+ id: string;
509
+ mixedValue: "custom" | 42 | true | Color.red;
510
+ }
511
+ `);
512
+ expect(getOutput(program, [_$createComponent(EnumDeclaration, {
513
+ type: Color
514
+ }), _$createComponent(ClassDeclaration, {
515
+ type: Widget
516
+ })])).toRenderTo(`
517
+ from dataclasses import dataclass
518
+ from enum import StrEnum
519
+ from typing import Literal
520
+
521
+
522
+ class Color(StrEnum):
523
+ RED = "RED"
524
+ BLUE = "BLUE"
525
+
526
+
527
+ @dataclass(kw_only=True)
528
+ class Widget:
529
+ id: str
530
+ mixed_value: Literal["custom", 42, True, Color.RED]
531
+
532
+ `);
533
+ });
534
+ it("renders a never-typed member as typing.Never", async () => {
535
+ const {
536
+ program,
537
+ Widget
538
+ } = await Tester.compile(t.code`
539
+ model ${t.model("Widget")} {
540
+ property: never;
541
+ }
542
+ `);
543
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
544
+ type: Widget
545
+ })])).toRenderTo(`
546
+ from dataclasses import dataclass
547
+ from typing import Never
548
+
549
+
550
+ @dataclass(kw_only=True)
551
+ class Widget:
552
+ property: Never
553
+
554
+ `);
555
+ });
556
+ it("can override class name", async () => {
557
+ const {
558
+ program,
559
+ Widget
560
+ } = await Tester.compile(t.code`
561
+ model ${t.model("Widget")} {
562
+ id: string;
563
+ weight: int32;
564
+ color: "blue" | "red";
565
+ }
566
+ `);
567
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
568
+ name: "MyOperations",
569
+ type: Widget
570
+ })])).toRenderTo(`
571
+ from dataclasses import dataclass
572
+ from typing import Literal
573
+
574
+
575
+ @dataclass(kw_only=True)
576
+ class MyOperations:
577
+ id: str
578
+ weight: int
579
+ color: Literal["blue", "red"]
580
+
581
+ `);
582
+ });
583
+ it("can add a members to the class", async () => {
584
+ const {
585
+ program,
586
+ Widget
587
+ } = await Tester.compile(t.code`
588
+ model ${t.model("Widget")} {
589
+ id: string;
590
+ weight: int32;
591
+ color: "blue" | "red";
592
+ }
593
+ `);
594
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
595
+ name: "MyOperations",
596
+ type: Widget,
597
+ get children() {
598
+ return [_$createIntrinsic("hbr", {}), _$createComponent(List, {
599
+ get children() {
600
+ return ["custom_property: str"];
601
+ }
602
+ })];
603
+ }
604
+ })])).toRenderTo(`
605
+ from dataclasses import dataclass
606
+ from typing import Literal
607
+
608
+
609
+ @dataclass(kw_only=True)
610
+ class MyOperations:
611
+ id: str
612
+ weight: int
613
+ color: Literal["blue", "red"]
614
+ custom_property: str
615
+
616
+ `);
617
+ });
618
+ it("creates a class from a model with extends", async () => {
619
+ const {
620
+ program,
621
+ Widget,
622
+ ErrorWidget
623
+ } = await Tester.compile(t.code`
624
+ model ${t.model("Widget")} {
625
+ id: string;
626
+ weight: int32;
627
+ color: "blue" | "red";
628
+ }
629
+
630
+ model ${t.model("ErrorWidget")} extends Widget {
631
+ code: int32;
632
+ message: string;
633
+ }
634
+ `);
635
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
636
+ type: Widget
637
+ }), _$createComponent(ClassDeclaration, {
638
+ type: ErrorWidget
639
+ })])).toRenderTo(`
640
+ from dataclasses import dataclass
641
+ from typing import Literal
642
+
643
+
644
+ @dataclass(kw_only=True)
645
+ class Widget:
646
+ id: str
647
+ weight: int
648
+ color: Literal["blue", "red"]
649
+
650
+
651
+ @dataclass(kw_only=True)
652
+ class ErrorWidget(Widget):
653
+ code: int
654
+ message: str
655
+
656
+ `);
657
+ });
658
+ });
659
+ describe("Python Class from interface", () => {
660
+ it("creates a class from an interface declaration", async () => {
661
+ const {
662
+ program,
663
+ WidgetOperations
664
+ } = await Tester.compile(t.code`
665
+ interface ${t.interface("WidgetOperations")} {
666
+ op getName(id: string): string;
667
+ }
668
+ `);
669
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
670
+ type: WidgetOperations
671
+ })])).toRenderTo(`
672
+ from abc import ABC
673
+ from abc import abstractmethod
674
+
675
+
676
+ class WidgetOperations(ABC):
677
+ @abstractmethod
678
+ def get_name(self, id: str) -> str:
679
+ pass
680
+
681
+
682
+ `);
683
+ });
684
+ it("should handle spread and non spread interface parameters", async () => {
685
+ const {
686
+ program,
687
+ Foo,
688
+ WidgetOperations
689
+ } = await Tester.compile(t.code`
690
+ model ${t.model("Foo")} {
691
+ name: string
692
+ }
693
+
694
+ interface ${t.interface("WidgetOperations")} {
695
+ op getName(foo: Foo): string;
696
+ op getOtherName(...Foo): string
697
+ }
698
+ `);
699
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
700
+ type: Foo
701
+ }), _$createComponent(ClassDeclaration, {
702
+ type: WidgetOperations
703
+ })])).toRenderTo(`
704
+ from abc import ABC
705
+ from abc import abstractmethod
706
+ from dataclasses import dataclass
707
+
708
+
709
+ @dataclass(kw_only=True)
710
+ class Foo:
711
+ name: str
712
+
713
+
714
+ class WidgetOperations(ABC):
715
+ @abstractmethod
716
+ def get_name(self, foo: Foo) -> str:
717
+ pass
718
+
719
+ @abstractmethod
720
+ def get_other_name(self, name: str) -> str:
721
+ pass
722
+
723
+
724
+ `);
725
+ });
726
+ it("creates a class from an interface with Model references", async () => {
727
+ const {
728
+ program,
729
+ WidgetOperations,
730
+ Widget
731
+ } = await Tester.compile(t.code`
732
+ /**
733
+ * Operations for Widget
734
+ */
735
+ interface ${t.interface("WidgetOperations")} {
736
+ /**
737
+ * Get the name of the widget
738
+ */
739
+ op getName(
740
+ /**
741
+ * The id of the widget
742
+ */
743
+ id: string
744
+ ): Widget;
745
+ }
746
+
747
+ model ${t.model("Widget")} {
748
+ id: string;
749
+ weight: int32;
750
+ color: "blue" | "red";
751
+ }
752
+ `);
753
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
754
+ type: WidgetOperations
755
+ }), _$createComponent(ClassDeclaration, {
756
+ type: Widget
757
+ })])).toRenderTo(`
758
+ from abc import ABC
759
+ from abc import abstractmethod
760
+ from dataclasses import dataclass
761
+ from typing import Literal
762
+
763
+
764
+ class WidgetOperations(ABC):
765
+ """
766
+ Operations for Widget
767
+ """
768
+
769
+ @abstractmethod
770
+ def get_name(self, id: str) -> Widget:
771
+ """
772
+ Get the name of the widget
773
+ """
774
+ pass
775
+
776
+
777
+
778
+ @dataclass(kw_only=True)
779
+ class Widget:
780
+ id: str
781
+ weight: int
782
+ color: Literal["blue", "red"]
783
+
784
+ `);
785
+ });
786
+ it("creates a class from an interface that extends another", async () => {
787
+ const {
788
+ program,
789
+ WidgetOperations,
790
+ WidgetOperationsExtended,
791
+ Widget
792
+ } = await Tester.compile(t.code`
793
+ interface ${t.interface("WidgetOperations")} {
794
+ op getName(id: string): Widget;
795
+ }
796
+
797
+ interface ${t.interface("WidgetOperationsExtended")} extends WidgetOperations{
798
+ op delete(id: string): void;
799
+ }
800
+
801
+ model ${t.model("Widget")} {
802
+ id: string;
803
+ weight: int32;
804
+ color: "blue" | "red";
805
+ }
806
+ `);
807
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
808
+ type: WidgetOperations
809
+ }), _$createComponent(ClassDeclaration, {
810
+ type: WidgetOperationsExtended
811
+ }), _$createComponent(ClassDeclaration, {
812
+ type: Widget
813
+ })])).toRenderTo(`
814
+ from abc import ABC
815
+ from abc import abstractmethod
816
+ from dataclasses import dataclass
817
+ from typing import Literal
818
+
819
+
820
+ class WidgetOperations(ABC):
821
+ @abstractmethod
822
+ def get_name(self, id: str) -> Widget:
823
+ pass
824
+
825
+
826
+
827
+ class WidgetOperationsExtended(ABC):
828
+ @abstractmethod
829
+ def get_name(self, id: str) -> Widget:
830
+ pass
831
+
832
+ @abstractmethod
833
+ def delete(self, id: str) -> None:
834
+ pass
835
+
836
+
837
+
838
+ @dataclass(kw_only=True)
839
+ class Widget:
840
+ id: str
841
+ weight: int
842
+ color: Literal["blue", "red"]
843
+
844
+ `);
845
+ });
846
+ });
847
+ describe("Python Class overrides", () => {
848
+ it("creates a class with a method if a model is provided and a class method is provided", async () => {
849
+ const {
850
+ program,
851
+ WidgetOperations
852
+ } = await Tester.compile(t.code`
853
+ model ${t.model("WidgetOperations")} {
854
+ id: string;
855
+ weight: int32;
856
+ }
857
+ `);
858
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
859
+ type: WidgetOperations,
860
+ get children() {
861
+ return [_$createIntrinsic("hbr", {}), _$createIntrinsic("hbr", {}), _$createComponent(List, {
862
+ get children() {
863
+ return _$createComponent(Method, {
864
+ name: "do_work",
865
+ returnType: "None",
866
+ doc: "This is a test"
867
+ });
868
+ }
869
+ })];
870
+ }
871
+ })])).toRenderTo(`
872
+ from dataclasses import dataclass
873
+
874
+
875
+ @dataclass(kw_only=True)
876
+ class WidgetOperations:
877
+ id: str
878
+ weight: int
879
+
880
+ def do_work(self) -> None:
881
+ """
882
+ This is a test
883
+ """
884
+ pass
885
+
886
+
887
+ `);
888
+ });
889
+ it("creates a class with a method if a model is provided and a class method is provided and methodType is set to method", async () => {
890
+ const {
891
+ program,
892
+ WidgetOperations
893
+ } = await Tester.compile(t.code`
894
+ model ${t.model("WidgetOperations")} {
895
+ id: string;
896
+ weight: int32;
897
+ }
898
+ `);
899
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
900
+ type: WidgetOperations,
901
+ methodType: "method",
902
+ get children() {
903
+ return [_$createIntrinsic("hbr", {}), _$createIntrinsic("hbr", {}), _$createComponent(List, {
904
+ get children() {
905
+ return _$createComponent(Method, {
906
+ name: "do_work",
907
+ returnType: "None",
908
+ doc: "This is a test"
909
+ });
910
+ }
911
+ })];
912
+ }
913
+ })])).toRenderTo(`
914
+ from dataclasses import dataclass
915
+
916
+
917
+ @dataclass(kw_only=True)
918
+ class WidgetOperations:
919
+ id: str
920
+ weight: int
921
+
922
+ def do_work(self) -> None:
923
+ """
924
+ This is a test
925
+ """
926
+ pass
927
+
928
+
929
+ `);
930
+ });
931
+ it("creates a class with a classmethod if a model is provided, a class method is provided and methodType is set to class", async () => {
932
+ const {
933
+ program,
934
+ WidgetOperations
935
+ } = await Tester.compile(t.code`
936
+ model ${t.model("WidgetOperations")} {
937
+ id: string;
938
+ weight: int32;
939
+ }
940
+ `);
941
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
942
+ type: WidgetOperations,
943
+ methodType: "class",
944
+ get children() {
945
+ return [_$createIntrinsic("hbr", {}), _$createIntrinsic("hbr", {}), _$createComponent(List, {
946
+ get children() {
947
+ return _$createComponent(Method, {
948
+ name: "do_work",
949
+ returnType: "None",
950
+ doc: "This is a test"
951
+ });
952
+ }
953
+ })];
954
+ }
955
+ })])).toRenderTo(`
956
+ from dataclasses import dataclass
957
+
958
+
959
+ @dataclass(kw_only=True)
960
+ class WidgetOperations:
961
+ id: str
962
+ weight: int
963
+
964
+ @classmethod
965
+ def do_work(cls) -> None:
966
+ """
967
+ This is a test
968
+ """
969
+ pass
970
+
971
+
972
+ `);
973
+ });
974
+ it("creates a class with a staticmethod if a model is provided, a class method is provided and methodType is set to static", async () => {
975
+ const {
976
+ program,
977
+ WidgetOperations
978
+ } = await Tester.compile(t.code`
979
+ model ${t.model("WidgetOperations")} {
980
+ id: string;
981
+ weight: int32;
982
+ }
983
+ `);
984
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
985
+ type: WidgetOperations,
986
+ methodType: "static",
987
+ get children() {
988
+ return [_$createIntrinsic("hbr", {}), _$createIntrinsic("hbr", {}), _$createComponent(List, {
989
+ get children() {
990
+ return _$createComponent(Method, {
991
+ name: "do_work",
992
+ returnType: "None",
993
+ doc: "This is a test"
994
+ });
995
+ }
996
+ })];
997
+ }
998
+ })])).toRenderTo(`
999
+ from dataclasses import dataclass
1000
+
1001
+
1002
+ @dataclass(kw_only=True)
1003
+ class WidgetOperations:
1004
+ id: str
1005
+ weight: int
1006
+
1007
+ @staticmethod
1008
+ def do_work() -> None:
1009
+ """
1010
+ This is a test
1011
+ """
1012
+ pass
1013
+
1014
+
1015
+ `);
1016
+ });
1017
+ it("creates a class with abstract method if an interface is provided", async () => {
1018
+ const {
1019
+ program,
1020
+ WidgetOperations
1021
+ } = await Tester.compile(t.code`
1022
+ interface ${t.interface("WidgetOperations")} {
1023
+ op getName(id: string): string;
1024
+ }
1025
+ `);
1026
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1027
+ type: WidgetOperations
1028
+ })])).toRenderTo(`
1029
+ from abc import ABC
1030
+ from abc import abstractmethod
1031
+
1032
+
1033
+ class WidgetOperations(ABC):
1034
+ @abstractmethod
1035
+ def get_name(self, id: str) -> str:
1036
+ pass
1037
+
1038
+
1039
+ `);
1040
+ });
1041
+ it("creates a class with abstract method if an interface is provided and methodType is set to method", async () => {
1042
+ const {
1043
+ program,
1044
+ WidgetOperations
1045
+ } = await Tester.compile(t.code`
1046
+ interface ${t.interface("WidgetOperations")} {
1047
+ op getName(id: string): string;
1048
+ }
1049
+ `);
1050
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1051
+ type: WidgetOperations,
1052
+ methodType: "method"
1053
+ })])).toRenderTo(`
1054
+ from abc import ABC
1055
+ from abc import abstractmethod
1056
+
1057
+
1058
+ class WidgetOperations(ABC):
1059
+ @abstractmethod
1060
+ def get_name(self, id: str) -> str:
1061
+ pass
1062
+
1063
+
1064
+ `);
1065
+ });
1066
+ it("creates a class with abstract classmethod if an interface is provided and methodType is set to class", async () => {
1067
+ const {
1068
+ program,
1069
+ WidgetOperations
1070
+ } = await Tester.compile(t.code`
1071
+ interface ${t.interface("WidgetOperations")} {
1072
+ op getName(id: string): string;
1073
+ }
1074
+ `);
1075
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1076
+ type: WidgetOperations,
1077
+ methodType: "class"
1078
+ })])).toRenderTo(`
1079
+ from abc import ABC
1080
+ from abc import abstractmethod
1081
+
1082
+
1083
+ class WidgetOperations(ABC):
1084
+ @classmethod
1085
+ @abstractmethod
1086
+ def get_name(cls, id: str) -> str:
1087
+ pass
1088
+
1089
+
1090
+ `);
1091
+ });
1092
+ it("creates a class with abstract staticmethod if an interface is provided and methodType is set to static", async () => {
1093
+ const {
1094
+ program,
1095
+ WidgetOperations
1096
+ } = await Tester.compile(t.code`
1097
+ interface ${t.interface("WidgetOperations")} {
1098
+ op getName(id: string): string;
1099
+ }
1100
+ `);
1101
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1102
+ type: WidgetOperations,
1103
+ methodType: "static"
1104
+ })])).toRenderTo(`
1105
+ from abc import ABC
1106
+ from abc import abstractmethod
1107
+
1108
+
1109
+ class WidgetOperations(ABC):
1110
+ @staticmethod
1111
+ @abstractmethod
1112
+ def get_name(id: str) -> str:
1113
+ pass
1114
+
1115
+
1116
+ `);
1117
+ });
1118
+ it("Emits type alias to template instance as dataclass", async () => {
1119
+ const {
1120
+ program,
1121
+ StringResponse
1122
+ } = await Tester.compile(t.code`
1123
+ model Response<T> {
1124
+ data: T;
1125
+ status: string;
1126
+ }
1127
+
1128
+ alias ${t.type("StringResponse")} = Response<string>;
1129
+ `);
1130
+
1131
+ // Type alias to a template instance is emitted as a dataclass,
1132
+ // since Python doesn't support parameterized type aliases like TypeScript.
1133
+ // This is equivalent to: model StringResponse is Response<string>;
1134
+ expect(getOutput(program, [_$createComponent(TypeAliasDeclaration, {
1135
+ type: StringResponse
1136
+ })])).toRenderTo(`
1137
+ from dataclasses import dataclass
1138
+
1139
+
1140
+ @dataclass(kw_only=True)
1141
+ class StringResponse:
1142
+ data: str
1143
+ status: str
1144
+
1145
+ `);
1146
+ });
1147
+ it("Emits multiple concrete models from template instances using 'is'", async () => {
1148
+ const {
1149
+ program,
1150
+ StringResult,
1151
+ IntResult
1152
+ } = await Tester.compile(t.code`
1153
+ model Result<T, E> {
1154
+ value: T;
1155
+ error: E;
1156
+ }
1157
+
1158
+ model ${t.model("StringResult")} is Result<string, string>;
1159
+ model ${t.model("IntResult")} is Result<int32, string>;
1160
+ `);
1161
+
1162
+ // TypeSpec 'is' copies all properties from the template instance.
1163
+ // Each concrete model gets fully expanded properties with substituted types.
1164
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1165
+ type: StringResult
1166
+ }), _$createComponent(ClassDeclaration, {
1167
+ type: IntResult
1168
+ })])).toRenderTo(`
1169
+ from dataclasses import dataclass
1170
+
1171
+
1172
+ @dataclass(kw_only=True)
1173
+ class StringResult:
1174
+ value: str
1175
+ error: str
1176
+
1177
+
1178
+ @dataclass(kw_only=True)
1179
+ class IntResult:
1180
+ value: int
1181
+ error: str
1182
+
1183
+ `);
1184
+ });
1185
+ it("Emits concrete model using 'is' from template instance", async () => {
1186
+ const {
1187
+ program,
1188
+ StringResponse
1189
+ } = await Tester.compile(t.code`
1190
+ model Response<T> {
1191
+ data: T;
1192
+ status: string;
1193
+ }
1194
+
1195
+ model ${t.model("StringResponse")} is Response<string>;
1196
+ `);
1197
+
1198
+ // Using 'is' copies all properties from the template instance.
1199
+ // StringResponse becomes a concrete model with all properties from Response<string>.
1200
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1201
+ type: StringResponse
1202
+ })])).toRenderTo(`
1203
+ from dataclasses import dataclass
1204
+
1205
+
1206
+ @dataclass(kw_only=True)
1207
+ class StringResponse:
1208
+ data: str
1209
+ status: str
1210
+
1211
+ `);
1212
+ });
1213
+ it("Emits concrete interface extending template (operations use concrete types)", async () => {
1214
+ const {
1215
+ program,
1216
+ StringRepository
1217
+ } = await Tester.compile(t.code`
1218
+ interface Repository<T> {
1219
+ get(id: string): T;
1220
+ list(): T[];
1221
+ }
1222
+
1223
+ interface ${t.interface("StringRepository")} extends Repository<string> {
1224
+ findByPrefix(prefix: string): string[];
1225
+ }
1226
+ `);
1227
+
1228
+ // TypeSpec flattens interface inheritance - StringRepository gets all operations
1229
+ // from Repository<string> with T replaced by string.
1230
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1231
+ type: StringRepository
1232
+ })])).toRenderTo(`
1233
+ from abc import ABC
1234
+ from abc import abstractmethod
1235
+
1236
+
1237
+ class StringRepository(ABC):
1238
+ @abstractmethod
1239
+ def get(self, id: str) -> str:
1240
+ pass
1241
+
1242
+ @abstractmethod
1243
+ def list(self) -> list[str]:
1244
+ pass
1245
+
1246
+ @abstractmethod
1247
+ def find_by_prefix(self, prefix: str) -> list[str]:
1248
+ pass
1249
+
1250
+
1251
+ `);
1252
+ });
1253
+ it("Emits multiple concrete interfaces (templates are macros, no generics)", async () => {
1254
+ const {
1255
+ program,
1256
+ UserRepository,
1257
+ ProductRepository
1258
+ } = await Tester.compile(t.code`
1259
+ interface Repository<T> {
1260
+ get(id: string): T;
1261
+ }
1262
+
1263
+ interface ${t.interface("UserRepository")} extends Repository<string> {
1264
+ findByEmail(email: string): string;
1265
+ }
1266
+
1267
+ interface ${t.interface("ProductRepository")} extends Repository<int32> {
1268
+ findByCategory(category: string): int32[];
1269
+ }
1270
+ `);
1271
+
1272
+ // Each concrete interface extends the template with different type arguments.
1273
+ // TypeSpec flattens the inheritance with concrete types substituted.
1274
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1275
+ type: UserRepository
1276
+ }), _$createComponent(ClassDeclaration, {
1277
+ type: ProductRepository
1278
+ })])).toRenderTo(`
1279
+ from abc import ABC
1280
+ from abc import abstractmethod
1281
+
1282
+
1283
+ class UserRepository(ABC):
1284
+ @abstractmethod
1285
+ def get(self, id: str) -> str:
1286
+ pass
1287
+
1288
+ @abstractmethod
1289
+ def find_by_email(self, email: str) -> str:
1290
+ pass
1291
+
1292
+
1293
+
1294
+ class ProductRepository(ABC):
1295
+ @abstractmethod
1296
+ def get(self, id: str) -> int:
1297
+ pass
1298
+
1299
+ @abstractmethod
1300
+ def find_by_category(self, category: str) -> list[int]:
1301
+ pass
1302
+
1303
+
1304
+ `);
1305
+ });
1306
+ it("Handles template instance with 'is' (copies properties)", async () => {
1307
+ const {
1308
+ program,
1309
+ CanadaAddress
1310
+ } = await Tester.compile(t.code`
1311
+ model Address<TState> {
1312
+ state: TState;
1313
+ city: string;
1314
+ street: string;
1315
+ }
1316
+
1317
+ model ${t.model("CanadaAddress")} is Address<never>;
1318
+ `);
1319
+
1320
+ // TypeSpec 'is' copies all properties from the template instance.
1321
+ // CanadaAddress is a concrete type with all properties from Address<never>.
1322
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1323
+ type: CanadaAddress
1324
+ })])).toRenderTo(`
1325
+ from dataclasses import dataclass
1326
+ from typing import Never
1327
+
1328
+
1329
+ @dataclass(kw_only=True)
1330
+ class CanadaAddress:
1331
+ state: Never
1332
+ city: str
1333
+ street: str
1334
+
1335
+ `);
1336
+ });
1337
+ it("Handles template with bounded type parameter using 'is'", async () => {
1338
+ const {
1339
+ program,
1340
+ StringContainer
1341
+ } = await Tester.compile(t.code`
1342
+ model Container<T extends string> {
1343
+ value: T;
1344
+ label: string;
1345
+ }
1346
+
1347
+ model ${t.model("StringContainer")} is Container<string>;
1348
+ `);
1349
+
1350
+ // Bounded type parameters (T extends string) work the same as unbounded -
1351
+ // the constraint is enforced by TypeSpec at compile time, and the concrete
1352
+ // type gets the substituted value.
1353
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1354
+ type: StringContainer
1355
+ })])).toRenderTo(`
1356
+ from dataclasses import dataclass
1357
+
1358
+
1359
+ @dataclass(kw_only=True)
1360
+ class StringContainer:
1361
+ value: str
1362
+ label: str
1363
+
1364
+ `);
1365
+ });
1366
+ it("Handles template with multiple bounded and unbounded parameters using 'is'", async () => {
1367
+ const {
1368
+ program,
1369
+ MyResult
1370
+ } = await Tester.compile(t.code`
1371
+ model Result<T extends string, E> {
1372
+ value: T;
1373
+ error: E;
1374
+ }
1375
+
1376
+ model ${t.model("MyResult")} is Result<string, int32>;
1377
+ `);
1378
+
1379
+ // Mixed bounded/unbounded parameters are handled the same way -
1380
+ // TypeSpec expands the template with concrete types.
1381
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1382
+ type: MyResult
1383
+ })])).toRenderTo(`
1384
+ from dataclasses import dataclass
1385
+
1386
+
1387
+ @dataclass(kw_only=True)
1388
+ class MyResult:
1389
+ value: str
1390
+ error: int
1391
+
1392
+ `);
1393
+ });
1394
+ it("Handles 'extends' with concrete model (Python inheritance)", async () => {
1395
+ const {
1396
+ program,
1397
+ Address,
1398
+ CanadaAddress
1399
+ } = await Tester.compile(t.code`
1400
+ model ${t.model("Address")} {
1401
+ city: string;
1402
+ }
1403
+
1404
+ model ${t.model("CanadaAddress")} extends Address {
1405
+ street: string;
1406
+ }
1407
+ `);
1408
+
1409
+ // TypeSpec 'extends' creates Python class inheritance when the base is a concrete model.
1410
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1411
+ type: Address
1412
+ }), _$createComponent(ClassDeclaration, {
1413
+ type: CanadaAddress
1414
+ })])).toRenderTo(`
1415
+ from dataclasses import dataclass
1416
+
1417
+
1418
+ @dataclass(kw_only=True)
1419
+ class Address:
1420
+ city: str
1421
+
1422
+
1423
+ @dataclass(kw_only=True)
1424
+ class CanadaAddress(Address):
1425
+ street: str
1426
+
1427
+ `);
1428
+ });
1429
+ it("Handles 'extends' with template instance base (references concrete base)", async () => {
1430
+ const {
1431
+ program,
1432
+ Response,
1433
+ ConcreteResponse
1434
+ } = await Tester.compile(t.code`
1435
+ model ${t.model("Response")} {
1436
+ data: string;
1437
+ status: string;
1438
+ }
1439
+
1440
+ model ${t.model("ConcreteResponse")} extends Response {
1441
+ timestamp: string;
1442
+ }
1443
+ `);
1444
+
1445
+ // When extending a concrete model, Python inheritance is used.
1446
+ // The base class must be emitted first for the reference to resolve.
1447
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1448
+ type: Response
1449
+ }), _$createComponent(ClassDeclaration, {
1450
+ type: ConcreteResponse
1451
+ })])).toRenderTo(`
1452
+ from dataclasses import dataclass
1453
+
1454
+
1455
+ @dataclass(kw_only=True)
1456
+ class Response:
1457
+ data: str
1458
+ status: str
1459
+
1460
+
1461
+ @dataclass(kw_only=True)
1462
+ class ConcreteResponse(Response):
1463
+ timestamp: str
1464
+
1465
+ `);
1466
+ });
1467
+ it("Handles template instance with 'extends' and never type using 'is' pattern", async () => {
1468
+ const {
1469
+ program,
1470
+ CanadaAddress
1471
+ } = await Tester.compile(t.code`
1472
+ model Address<TState> {
1473
+ state: TState;
1474
+ city: string;
1475
+ }
1476
+
1477
+ // Using 'is' instead of 'extends' to copy all properties from the template instance
1478
+ model ${t.model("CanadaAddress")} is Address<never> {
1479
+ street: string;
1480
+ }
1481
+ `);
1482
+
1483
+ // When extending template instances, prefer 'is' pattern which copies all properties.
1484
+ // The 'extends' keyword with template instances would require the template declaration
1485
+ // to be emitted, which is not supported since templates are macros.
1486
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1487
+ type: CanadaAddress
1488
+ })])).toRenderTo(`
1489
+ from dataclasses import dataclass
1490
+ from typing import Never
1491
+
1492
+
1493
+ @dataclass(kw_only=True)
1494
+ class CanadaAddress:
1495
+ state: Never
1496
+ city: str
1497
+ street: str
1498
+
1499
+ `);
1500
+ });
1501
+ it("creates an abstract dataclass when abstract prop is true with a model", async () => {
1502
+ const {
1503
+ program,
1504
+ BaseEntity
1505
+ } = await Tester.compile(t.code`
1506
+ model ${t.model("BaseEntity")} {
1507
+ id: string;
1508
+ createdAt: string;
1509
+ }
1510
+ `);
1511
+ expect(getOutput(program, [_$createComponent(ClassDeclaration, {
1512
+ type: BaseEntity,
1513
+ abstract: true
1514
+ })])).toRenderTo(`
1515
+ from abc import ABC
1516
+ from dataclasses import dataclass
1517
+
1518
+
1519
+ @dataclass(kw_only=True)
1520
+ class BaseEntity(ABC):
1521
+ id: str
1522
+ created_at: str
1523
+
1524
+ `);
1525
+ });
1526
+ });
1527
+ //# sourceMappingURL=class-declaration.test.js.map