@typespec/emitter-framework 0.15.0-dev.3 → 0.15.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 (253) hide show
  1. package/CHANGELOG.md +14 -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 +12 -5
  203. package/package.json.bak +14 -6
  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,333 @@
1
+ import { Experimental_OverridableComponent } from "#core/components/index.js";
2
+ import { useTsp } from "#core/context/index.js";
3
+ import { reportPythonDiagnostic } from "#python/lib.js";
4
+ import { code, For, List, mapJoin } from "@alloy-js/core";
5
+ import * as py from "@alloy-js/python";
6
+ import {
7
+ isNeverType,
8
+ type IntrinsicType,
9
+ type Model,
10
+ type Scalar,
11
+ type Type,
12
+ } from "@typespec/compiler";
13
+ import type { TemplateParameterDeclarationNode } from "@typespec/compiler/ast";
14
+ import type { Typekit } from "@typespec/compiler/typekit";
15
+ import { datetimeModule, decimalModule, typingModule } from "../../builtins.js";
16
+ import { efRefkey } from "../../utils/refkey.js";
17
+ import { ArrayExpression } from "../array-expression/array-expression.js";
18
+ import { RecordExpression } from "../record-expression/record-expression.js";
19
+
20
+ export interface TypeExpressionProps {
21
+ type: Type;
22
+
23
+ /**
24
+ * Whether to disallow references. Setting this will force the type to be
25
+ * emitted inline, even if it is a declaration that would otherwise be
26
+ * referenced.
27
+ */
28
+ noReference?: boolean;
29
+ }
30
+
31
+ export function TypeExpression(props: TypeExpressionProps) {
32
+ const { $ } = useTsp();
33
+ const type = props.type;
34
+ if (!props.noReference && isDeclaration($, type)) {
35
+ return (
36
+ <Experimental_OverridableComponent reference type={type}>
37
+ <py.Reference refkey={efRefkey(type)} />
38
+ </Experimental_OverridableComponent>
39
+ );
40
+ }
41
+
42
+ switch (type.kind) {
43
+ case "Scalar": // Custom types based on primitives (Intrinsics)
44
+ case "Intrinsic": // Language primitives like `string`, `number`, etc.
45
+ if (isNeverType(type)) {
46
+ return typingModule["."]["Never"];
47
+ }
48
+ return <>{getScalarIntrinsicExpression($, type)}</>;
49
+ case "Boolean":
50
+ case "Number":
51
+ case "String":
52
+ // Single literal values are wrapped in Literal[...]
53
+ return (
54
+ <>
55
+ {typingModule["."]["Literal"]}[{formatLiteralValue(type)}]
56
+ </>
57
+ );
58
+ case "Tuple":
59
+ return (
60
+ <>
61
+ tuple[
62
+ <For each={type.values} comma space>
63
+ {(element) => <TypeExpression type={element} />}
64
+ </For>
65
+ ]
66
+ </>
67
+ );
68
+ case "Union": {
69
+ const variants = Array.from((type as any).variants?.values?.() ?? []);
70
+
71
+ // Check if all variants are literals or named union variant refs with literal values
72
+ const isLiteralOrVariantRef = (t: Type): boolean => {
73
+ if (!t) return false;
74
+ if (isLiteral($, t)) return true;
75
+ // Named union variant with a literal inner value
76
+ if (t.kind === "UnionVariant" && (t as any).union?.name) {
77
+ return isLiteral($, (t as any).type);
78
+ }
79
+ return false;
80
+ };
81
+
82
+ const innerTypes = variants.map((v: any) => v.type);
83
+ if (innerTypes.every(isLiteralOrVariantRef)) {
84
+ // All literals - render as Literal[...]
85
+ const literalValues = variants
86
+ .map((v: any) => {
87
+ const innerType = v.type;
88
+ // Named union variant ref with literal value
89
+ if (innerType.kind === "UnionVariant" && innerType.union?.name) {
90
+ const variantName = String(innerType.name).toUpperCase();
91
+ return code`${efRefkey(innerType.union)}.${variantName}`;
92
+ }
93
+ if (isLiteral($, innerType)) {
94
+ return formatLiteralValue(innerType);
95
+ }
96
+ return undefined;
97
+ })
98
+ .filter(Boolean);
99
+
100
+ return (
101
+ <>
102
+ {typingModule["."]["Literal"]}[<List children={literalValues} joiner=", " />]
103
+ </>
104
+ );
105
+ }
106
+
107
+ // Not all literals - render as union type
108
+ return mapJoin(
109
+ () => variants,
110
+ (v: any) => <TypeExpression type={v.type} />,
111
+ { joiner: " | " },
112
+ );
113
+ }
114
+ case "UnionVariant": {
115
+ // Union variant from a named union with a literal value
116
+ if (type.union && (type.union as any).name && isLiteral($, type.type)) {
117
+ // Use the variant's name (e.g., "red", "active"), converted to UPPER_CASE by the enum
118
+ const variantName = String(type.name).toUpperCase();
119
+ return (
120
+ <>
121
+ {typingModule["."]["Literal"]}[{efRefkey(type.union)}.{variantName}]
122
+ </>
123
+ );
124
+ }
125
+ // Unnamed union variant or non-literal value, unwrap to its inner type
126
+ return <TypeExpression type={type.type} />;
127
+ }
128
+ case "ModelProperty":
129
+ return <TypeExpression type={type.type} />;
130
+ case "Model":
131
+ if ($.array.is(type)) {
132
+ const elementType = type.indexer!.value;
133
+ return <ArrayExpression elementType={elementType} />;
134
+ }
135
+
136
+ if ($.record.is(type)) {
137
+ const elementType = (type as Model).indexer!.value;
138
+ return <RecordExpression elementType={elementType} />;
139
+ }
140
+
141
+ // TODO: When TypeSpec adds true generics support, handle generic type references here.
142
+ // Currently, TypeSpec templates are macros that expand to concrete types, so template
143
+ // instances (e.g., Response<string>) are treated as regular concrete types, not as
144
+ // parameterized generic types (e.g., Response[str]).
145
+ // When generics are implemented, this is where we would render: ClassName[TypeArg, ...]
146
+
147
+ // Regular named models should be handled as references
148
+ if (type.name) {
149
+ return (
150
+ <Experimental_OverridableComponent reference type={type}>
151
+ <py.Reference refkey={efRefkey(type)} />
152
+ </Experimental_OverridableComponent>
153
+ );
154
+ }
155
+
156
+ reportPythonDiagnostic($.program, { code: "python-unsupported-type", target: type });
157
+ return <></>;
158
+ case "TemplateParameter":
159
+ return code`${String((type.node as TemplateParameterDeclarationNode).id.sv)}`;
160
+
161
+ case "Operation": {
162
+ // Render function types as typing.Callable[[ArgTypes...], ReturnType]
163
+ // If parameters cannot be enumerated, fall back to Callable[..., ReturnType]
164
+ let paramTypes: Type[] | null = null;
165
+ const op: any = type as any;
166
+ if (op.parameters) {
167
+ try {
168
+ const { $ } = useTsp();
169
+ const modelProps = $.model.getProperties(op.parameters);
170
+ paramTypes = Array.from(modelProps.values()).map((p: any) => p.type);
171
+ } catch {
172
+ // Unknown/unsupported params shape
173
+ paramTypes = null;
174
+ }
175
+ } else {
176
+ paramTypes = [];
177
+ }
178
+
179
+ return (
180
+ <>
181
+ {typingModule["."]["Callable"]}[
182
+ {paramTypes === null ? (
183
+ <>...</>
184
+ ) : paramTypes.length > 0 ? (
185
+ <>
186
+ [
187
+ <For each={paramTypes} comma space>
188
+ {(t) => <TypeExpression type={t} />}
189
+ </For>
190
+ ]
191
+ </>
192
+ ) : (
193
+ <>[]</>
194
+ )}
195
+ {", "}
196
+ <TypeExpression type={(type as any).returnType} />]
197
+ </>
198
+ );
199
+ }
200
+ default:
201
+ reportPythonDiagnostic($.program, { code: "python-unsupported-type", target: type });
202
+ // Return empty fragment - the diagnostic has already been reported
203
+ return <></>;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Checks if a type is a literal (string, numeric, or boolean).
209
+ */
210
+ function isLiteral($: Typekit, type: Type): boolean {
211
+ return $.literal.isString(type) || $.literal.isNumeric(type) || $.literal.isBoolean(type);
212
+ }
213
+
214
+ /**
215
+ * Formats a literal type value for use in Python's Literal[...] syntax.
216
+ */
217
+ function formatLiteralValue(type: { kind: string; value: unknown }): string {
218
+ switch (type.kind) {
219
+ case "String":
220
+ return JSON.stringify(type.value);
221
+ case "Boolean":
222
+ return type.value ? "True" : "False";
223
+ case "Number":
224
+ return String(type.value);
225
+ default:
226
+ return String(type.value);
227
+ }
228
+ }
229
+
230
+ const intrinsicNameToPythonType = new Map<string, string | null>([
231
+ // Core types
232
+ ["unknown", "Any"], // Matches Python's `Any`
233
+ ["string", "str"], // Matches Python's `str`
234
+ ["boolean", "bool"], // Matches Python's `bool`
235
+ ["null", "None"], // Matches Python's `None`
236
+ ["void", "None"], // Matches Python's `None`
237
+ ["never", "Never"], // Matches Python's `Never`
238
+ ["bytes", "bytes"], // Matches Python's `bytes`
239
+
240
+ // Numeric types
241
+ ["numeric", "number"], // Parent type for all numeric types
242
+ ["integer", "int"], // Broad integer category, maps to `int`
243
+ ["float", "float"], // Broad float category, maps to `float`
244
+ ["decimal", "Decimal"], // Broad decimal category, maps to `Decimal`
245
+ ["decimal128", "Decimal"], // 128-bit decimal category, maps to `Decimal`
246
+ ["int64", "int"], // Use `int` to handle large 64-bit integers
247
+ ["int32", "int"], // 32-bit integer fits in Python's `int`
248
+ ["int16", "int"], // 16-bit integer
249
+ ["int8", "int"], // 8-bit integer
250
+ ["safeint", "int"], // Safe integer fits within Python limits
251
+ ["uint64", "int"], // Use `int` for unsigned 64-bit integers
252
+ ["uint32", "int"], // 32-bit unsigned integer
253
+ ["uint16", "int"], // 16-bit unsigned integer
254
+ ["uint8", "int"], // 8-bit unsigned integer
255
+ ["float32", "float"], // Maps to Python's `float`
256
+ ["float64", "float"], // Maps to Python's `float`.
257
+
258
+ // Date and time types
259
+ ["plainDate", "str"], // Use `str` for plain calendar dates
260
+ ["plainTime", "str"], // Use `str` for plain clock times
261
+ ["utcDateTime", "datetime"], // Use `datetime` for UTC date-times
262
+ ["offsetDateTime", "str"], // Use `str` for timezone-specific date-times
263
+ ["duration", "str"], // Duration as an ISO 8601 string or custom format
264
+
265
+ // String types
266
+ ["url", "str"], // Matches Python's `str`
267
+ ]);
268
+
269
+ const pythonTypeToImport = new Map<string, any>([
270
+ ["Any", typingModule["."]["Any"]],
271
+ ["Never", typingModule["."]["Never"]],
272
+ ["datetime", datetimeModule["."]["datetime"]],
273
+ ["Decimal", decimalModule["."]["Decimal"]],
274
+ ]);
275
+
276
+ function getScalarIntrinsicExpression($: Typekit, type: Scalar | IntrinsicType): string | null {
277
+ let intrinsicName: string;
278
+ if ($.scalar.is(type)) {
279
+ if ($.scalar.isUtcDateTime(type) || $.scalar.extendsUtcDateTime(type)) {
280
+ const encoding = $.scalar.getEncoding(type);
281
+ intrinsicName = "utcDateTime";
282
+ switch (encoding?.encoding) {
283
+ case "unixTimestamp":
284
+ case "rfc7231":
285
+ case "rfc3339":
286
+ default:
287
+ intrinsicName = `utcDateTime`;
288
+ break;
289
+ }
290
+ }
291
+ intrinsicName = $.scalar.getStdBase(type)?.name ?? "";
292
+ } else {
293
+ intrinsicName = type.name;
294
+ }
295
+
296
+ let pythonType = intrinsicNameToPythonType.get(intrinsicName);
297
+ const importModule = pythonTypeToImport.get(pythonType ?? "");
298
+ pythonType = importModule ? importModule : pythonType;
299
+
300
+ if (!pythonType) {
301
+ reportPythonDiagnostic($.program, { code: "python-unsupported-scalar", target: type });
302
+ return "any";
303
+ }
304
+
305
+ return pythonType;
306
+ }
307
+
308
+ // TODO: When TypeSpec adds true generics support, add helper to detect generic type instances.
309
+ // Currently, TypeSpec templates expand to concrete types at compile time, so we treat all
310
+ // template instances as regular concrete types.
311
+
312
+ function isDeclaration($: Typekit, type: Type): boolean {
313
+ switch (type.kind) {
314
+ case "Namespace":
315
+ case "Interface":
316
+ case "Enum":
317
+ case "Operation":
318
+ case "EnumMember":
319
+ case "UnionVariant":
320
+ return false;
321
+
322
+ case "Model":
323
+ if ($.array.is(type) || $.record.is(type)) {
324
+ return false;
325
+ }
326
+
327
+ return Boolean(type.name);
328
+ case "Union":
329
+ return Boolean(type.name);
330
+ default:
331
+ return false;
332
+ }
333
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./builtins.js";
2
+ export * from "./components/index.js";
3
+ export * from "./utils/index.js";
@@ -0,0 +1,40 @@
1
+ import { createTypeSpecLibrary } from "@typespec/compiler";
2
+
3
+ export const $pythonLib = createTypeSpecLibrary({
4
+ name: "emitter-framework",
5
+ diagnostics: {
6
+ "python-unsupported-scalar": {
7
+ severity: "warning",
8
+ messages: {
9
+ default: "Unsupported scalar type, falling back to Any",
10
+ },
11
+ },
12
+ "python-unsupported-type": {
13
+ severity: "error",
14
+ messages: {
15
+ default: "Unsupported type, falling back to Any",
16
+ },
17
+ description: "This type is not supported by the Python emitter",
18
+ },
19
+ "python-unsupported-model-discriminator": {
20
+ severity: "error",
21
+ messages: {
22
+ default:
23
+ "Unsupported model discriminator, falling back to not discriminating on serialization/deserialization",
24
+ },
25
+ description: "Discriminators at the model are not supported",
26
+ },
27
+ "python-unsupported-type-transform": {
28
+ severity: "error",
29
+ messages: {
30
+ default: "Unsupported type for transformation, falling back to not transforming this type",
31
+ },
32
+ description: "This type cannot be transformed",
33
+ },
34
+ },
35
+ });
36
+
37
+ export const {
38
+ reportDiagnostic: reportPythonDiagnostic,
39
+ createDiagnostic: createPythonDiagnostic,
40
+ } = $pythonLib;
@@ -0,0 +1,31 @@
1
+ import { Output } from "#core/components/index.js";
2
+ import { type Children } from "@alloy-js/core";
3
+ import * as py from "@alloy-js/python";
4
+ import type { Program } from "@typespec/compiler";
5
+ import { abcModule, datetimeModule, decimalModule, typingModule } from "./builtins.js";
6
+
7
+ export const renderOptions = {
8
+ printWidth: 80,
9
+ tabWidth: 4,
10
+ };
11
+
12
+ export function getOutput(program: Program, children: Children[]): Children {
13
+ const policy = py.createPythonNamePolicy();
14
+ return (
15
+ <Output
16
+ program={program}
17
+ externals={[
18
+ abcModule,
19
+ datetimeModule,
20
+ decimalModule,
21
+ typingModule,
22
+ py.abcModule,
23
+ py.dataclassesModule,
24
+ py.enumModule,
25
+ ]}
26
+ namePolicy={policy}
27
+ >
28
+ <py.SourceFile path="test.py">{children}</py.SourceFile>
29
+ </Output>
30
+ );
31
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./operation.js";
2
+ export * from "./refkey.js";
3
+ export * from "./type.js";
@@ -0,0 +1,161 @@
1
+ import { typingModule } from "#python/builtins.js";
2
+ import { refkey, type Children, type Refkey } from "@alloy-js/core";
3
+ import * as py from "@alloy-js/python";
4
+ import type { Model, ModelProperty, Operation, Type } from "@typespec/compiler";
5
+ import { useTsp } from "../../core/index.js";
6
+ import { Atom } from "../components/atom/atom.js";
7
+ import { TypeExpression } from "../components/type-expression/type-expression.js";
8
+ import { efRefkey } from "./refkey.js";
9
+
10
+ export function getReturnType(
11
+ type: Operation,
12
+ options: { skipErrorFiltering: boolean } = { skipErrorFiltering: false },
13
+ ): Type {
14
+ const { $ } = useTsp();
15
+ let returnType = type.returnType;
16
+
17
+ if (!options.skipErrorFiltering && type.returnType.kind === "Union") {
18
+ returnType = $.union.filter(type.returnType, (variant) => !$.type.isError(variant.type));
19
+ }
20
+
21
+ return returnType;
22
+ }
23
+
24
+ export interface BuildParameterDescriptorsOptions {
25
+ params?: (py.ParameterDescriptor | string)[];
26
+ suffixRefkey?: Refkey;
27
+ /** If true, params replaces operation parameters instead of adding to them */
28
+ replaceParameters?: boolean;
29
+ }
30
+
31
+ /**
32
+ * Build a parameter descriptor array from a TypeSpec Model.
33
+ *
34
+ * Parameter ordering (unless replaceParameters is true):
35
+ * - Operation params without defaults: positional (e.g., URL path params)
36
+ * - "*" marker (if any keyword-only params exist)
37
+ * - Operation params with defaults: keyword-only (e.g., query params)
38
+ * - Additional params: keyword-only
39
+ */
40
+ export function buildParameterDescriptors(
41
+ type: Model,
42
+ options: BuildParameterDescriptorsOptions = {},
43
+ ): (py.ParameterDescriptor | string)[] | undefined {
44
+ const { $ } = useTsp();
45
+ const suffixRefkey = options.suffixRefkey ?? refkey();
46
+ const optionsParams: py.ParameterDescriptor[] = normalizeParameters(options.params ?? []);
47
+
48
+ // If replaceParameters is true, ignore operation params and just return options params
49
+ // All replacement parameters are keyword-only (following "additional parameters are keyword-only" principle)
50
+ if (options.replaceParameters) {
51
+ const withoutDefaults = optionsParams.filter((p) => p.default === undefined);
52
+ const withDefaults = optionsParams.filter((p) => p.default !== undefined);
53
+
54
+ // Always add "*" marker since all replacement params should be keyword-only
55
+ const allParams: (py.ParameterDescriptor | string)[] =
56
+ optionsParams.length > 0 ? ["*", ...withoutDefaults, ...withDefaults] : [];
57
+
58
+ return allParams;
59
+ }
60
+
61
+ const modelProperties = $.model.getProperties(type);
62
+ const operationParams: py.ParameterDescriptor[] = [...modelProperties.values()].map((m) =>
63
+ buildParameterDescriptor(m, suffixRefkey),
64
+ );
65
+
66
+ // Split operation params: params without defaults are positional, params with defaults are keyword-only
67
+ const opParamsWithoutDefaults = operationParams.filter((p) => p.default === undefined);
68
+ const opParamsWithDefaults = operationParams.filter((p) => p.default !== undefined);
69
+
70
+ // Reorder additional params: params without defaults before params with defaults
71
+ const optionsWithoutDefaults = optionsParams.filter((p) => p.default === undefined);
72
+ const optionsWithDefaults = optionsParams.filter((p) => p.default !== undefined);
73
+
74
+ // Build final parameter list:
75
+ // - Operation params without defaults: positional (e.g., URL path params)
76
+ // - "*" marker (if there are keyword-only params)
77
+ // - Operation params with defaults: keyword-only (e.g., query params with defaults)
78
+ // - Additional params: keyword-only
79
+
80
+ // If there are no operation params at all, treat additional params without defaults as positional
81
+ const hasOperationParams = operationParams.length > 0;
82
+
83
+ const positionalParams = hasOperationParams
84
+ ? opParamsWithoutDefaults
85
+ : [...opParamsWithoutDefaults, ...optionsWithoutDefaults];
86
+
87
+ const keywordOnlyParams = hasOperationParams
88
+ ? [...optionsWithoutDefaults, ...opParamsWithDefaults, ...optionsWithDefaults]
89
+ : [...opParamsWithDefaults, ...optionsWithDefaults];
90
+
91
+ // Add "*" marker if we have keyword-only params
92
+ // This enforces that params with defaults are truly keyword-only
93
+ const allParams: (py.ParameterDescriptor | string)[] =
94
+ keywordOnlyParams.length > 0
95
+ ? [...positionalParams, "*", ...keywordOnlyParams]
96
+ : [...positionalParams];
97
+
98
+ return allParams;
99
+ }
100
+
101
+ /**
102
+ * Convert a TypeSpec ModelProperty into a Python ParameterDescriptor.
103
+ */
104
+ export function buildParameterDescriptor(
105
+ modelProperty: ModelProperty,
106
+ suffixRefkey: Refkey,
107
+ ): py.ParameterDescriptor {
108
+ const { $ } = useTsp();
109
+ const namePolicy = py.usePythonNamePolicy();
110
+ const paramName = namePolicy.getName(modelProperty.name, "parameter");
111
+ const isOptional = modelProperty.optional || modelProperty.defaultValue !== undefined;
112
+ const doc = $.type.getDoc(modelProperty);
113
+ let defaultValueNode: Children | undefined = undefined;
114
+ const hasDefault =
115
+ modelProperty.defaultValue !== undefined && modelProperty.defaultValue !== null;
116
+ if (hasDefault) {
117
+ defaultValueNode = Atom({ value: (modelProperty as any).defaultValue });
118
+ } else if (isOptional) {
119
+ // Render Python None for optional parameters without explicit default
120
+ defaultValueNode = py.Atom({ jsValue: null }) as any;
121
+ }
122
+ return {
123
+ doc,
124
+ name: paramName,
125
+ refkey: efRefkey(modelProperty, suffixRefkey),
126
+ type: TypeExpression({ type: modelProperty.type }),
127
+ ...(defaultValueNode !== undefined ? { default: defaultValueNode } : {}),
128
+ };
129
+ }
130
+
131
+ const rawTypeMap = {
132
+ string: "str",
133
+ number: "float",
134
+ boolean: "bool",
135
+ any: typingModule["."]["Any"],
136
+ never: typingModule["."]["Never"],
137
+ };
138
+
139
+ /**
140
+ * Convert a parameter descriptor array to normalized parameter descriptors.
141
+ * String parameter names are converted to basic parameter descriptors.
142
+ */
143
+ function normalizeParameters(
144
+ params: (py.ParameterDescriptor | string)[],
145
+ ): py.ParameterDescriptor[] {
146
+ if (!params) return [];
147
+
148
+ return params.map((param) => {
149
+ if (typeof param === "string") {
150
+ // Convert string names to parameter descriptors
151
+ return { name: param };
152
+ }
153
+ if (typeof (param as any).type === "string") {
154
+ return {
155
+ ...param,
156
+ type: rawTypeMap[param.type as keyof typeof rawTypeMap] ?? param.type,
157
+ } as py.ParameterDescriptor;
158
+ }
159
+ return param;
160
+ });
161
+ }
@@ -0,0 +1,36 @@
1
+ import { refkey as ayRefkey, type Refkey } from "@alloy-js/core";
2
+
3
+ const refKeyPrefix = Symbol.for("emitter-framework:python");
4
+
5
+ /**
6
+ * A wrapper around `refkey` that uses a custom symbol to avoid collisions with
7
+ * other libraries that use `refkey`.
8
+ *
9
+ * @remarks
10
+ *
11
+ * The underlying refkey function is called with the {@link refKeyPrefix} symbol as the first argument.
12
+ *
13
+ * @param args The parameters of the refkey.
14
+ * @returns A refkey object that can be used to identify the value.
15
+ */
16
+ export function efRefkey(...args: unknown[]): Refkey {
17
+ if (args.length === 0) {
18
+ return ayRefkey(); // Generates a unique refkey
19
+ }
20
+ return ayRefkey(refKeyPrefix, ...args);
21
+ }
22
+
23
+ /**
24
+ * Creates a refkey for a declaration by combining the provided refkey with an internal
25
+ * refkey generated from the provided arguments.
26
+ *
27
+ * @param refkey The refkey provided by the user to be passed as is.
28
+ * @param args The parameters of the refkey.
29
+ * @returns An array of refkeys that can be passed to an Alloy declaration.
30
+ */
31
+ export function declarationRefkeys(refkey?: Refkey | Refkey[], ...args: unknown[]): Refkey[] {
32
+ if (refkey) {
33
+ return [refkey, efRefkey(...args)].flat();
34
+ }
35
+ return [efRefkey(...args)];
36
+ }
@@ -0,0 +1,31 @@
1
+ import type { Typekit } from "@typespec/compiler/typekit";
2
+
3
+ /**
4
+ * Check if a type is a literal type (string, numeric, boolean, or union variant).
5
+ * This is useful for determining if a type can be used in a Python Literal[] type.
6
+ *
7
+ * @param $ - The Typekit instance
8
+ * @param type - The type to check
9
+ * @returns true if the type is a literal type
10
+ */
11
+ export function isLiteral($: Typekit, type: any): boolean {
12
+ if (!type) return false;
13
+
14
+ return (
15
+ $.literal.isString(type) ||
16
+ $.literal.isNumeric(type) ||
17
+ $.literal.isBoolean(type) ||
18
+ type.kind === "UnionVariant"
19
+ );
20
+ }
21
+
22
+ /**
23
+ * Check if all types in an array are literal types.
24
+ *
25
+ * @param $ - The Typekit instance
26
+ * @param types - Array of types to check
27
+ * @returns true if all types are literals
28
+ */
29
+ export function areAllLiterals($: Typekit, types: any[]): boolean {
30
+ return types.every((type) => isLiteral($, type));
31
+ }