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

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 (153) hide show
  1. package/dist/dev/src/builtins/python.js +46 -0
  2. package/dist/dev/src/builtins/python.js.map +1 -1
  3. package/dist/dev/src/components/ClassDeclaration.js +19 -10
  4. package/dist/dev/src/components/ClassDeclaration.js.map +1 -1
  5. package/dist/dev/src/components/ClassMethodDeclaration.js +20 -5
  6. package/dist/dev/src/components/ClassMethodDeclaration.js.map +1 -1
  7. package/dist/dev/src/components/DataclassDeclaration.js +14 -12
  8. package/dist/dev/src/components/DataclassDeclaration.js.map +1 -1
  9. package/dist/dev/src/components/DecoratorList.js +55 -0
  10. package/dist/dev/src/components/DecoratorList.js.map +1 -0
  11. package/dist/dev/src/components/EnumDeclaration.js +21 -12
  12. package/dist/dev/src/components/EnumDeclaration.js.map +1 -1
  13. package/dist/dev/src/components/FunctionBase.js +21 -10
  14. package/dist/dev/src/components/FunctionBase.js.map +1 -1
  15. package/dist/dev/src/components/FutureStatement.js +1 -1
  16. package/dist/dev/src/components/MethodBase.js +16 -4
  17. package/dist/dev/src/components/MethodBase.js.map +1 -1
  18. package/dist/dev/src/components/PropertyDeclaration.js +68 -17
  19. package/dist/dev/src/components/PropertyDeclaration.js.map +1 -1
  20. package/dist/dev/src/components/PydanticClassDeclaration.js +136 -0
  21. package/dist/dev/src/components/PydanticClassDeclaration.js.map +1 -0
  22. package/dist/dev/src/components/StaticMethodDeclaration.js +19 -5
  23. package/dist/dev/src/components/StaticMethodDeclaration.js.map +1 -1
  24. package/dist/dev/src/components/index.js +1 -0
  25. package/dist/dev/src/components/index.js.map +1 -1
  26. package/dist/dev/test/classdeclarations.test.js +85 -52
  27. package/dist/dev/test/classdeclarations.test.js.map +1 -1
  28. package/dist/dev/test/dataclassdeclarations.test.js +122 -89
  29. package/dist/dev/test/dataclassdeclarations.test.js.map +1 -1
  30. package/dist/dev/test/decoratorlist.test.js +84 -0
  31. package/dist/dev/test/decoratorlist.test.js.map +1 -0
  32. package/dist/dev/test/enums.test.js +41 -10
  33. package/dist/dev/test/enums.test.js.map +1 -1
  34. package/dist/dev/test/functiondeclaration.test.js +81 -61
  35. package/dist/dev/test/functiondeclaration.test.js.map +1 -1
  36. package/dist/dev/test/methoddeclaration.test.js +117 -26
  37. package/dist/dev/test/methoddeclaration.test.js.map +1 -1
  38. package/dist/dev/test/propertydeclaration.test.js +109 -7
  39. package/dist/dev/test/propertydeclaration.test.js.map +1 -1
  40. package/dist/dev/test/pydanticclassdeclarations.test.js +1137 -0
  41. package/dist/dev/test/pydanticclassdeclarations.test.js.map +1 -0
  42. package/dist/src/builtins/python.d.ts +30 -0
  43. package/dist/src/builtins/python.d.ts.map +1 -1
  44. package/dist/src/builtins/python.js +46 -0
  45. package/dist/src/builtins/python.js.map +1 -1
  46. package/dist/src/components/ClassDeclaration.d.ts +21 -0
  47. package/dist/src/components/ClassDeclaration.d.ts.map +1 -1
  48. package/dist/src/components/ClassDeclaration.js +6 -1
  49. package/dist/src/components/ClassDeclaration.js.map +1 -1
  50. package/dist/src/components/ClassMethodDeclaration.d.ts +5 -1
  51. package/dist/src/components/ClassMethodDeclaration.d.ts.map +1 -1
  52. package/dist/src/components/ClassMethodDeclaration.js +14 -3
  53. package/dist/src/components/ClassMethodDeclaration.js.map +1 -1
  54. package/dist/src/components/DataclassDeclaration.d.ts.map +1 -1
  55. package/dist/src/components/DataclassDeclaration.js +10 -4
  56. package/dist/src/components/DataclassDeclaration.js.map +1 -1
  57. package/dist/src/components/DecoratorList.d.ts +43 -0
  58. package/dist/src/components/DecoratorList.d.ts.map +1 -0
  59. package/dist/src/components/DecoratorList.js +47 -0
  60. package/dist/src/components/DecoratorList.js.map +1 -0
  61. package/dist/src/components/EnumDeclaration.d.ts +9 -0
  62. package/dist/src/components/EnumDeclaration.d.ts.map +1 -1
  63. package/dist/src/components/EnumDeclaration.js +6 -1
  64. package/dist/src/components/EnumDeclaration.js.map +1 -1
  65. package/dist/src/components/FunctionBase.d.ts +31 -1
  66. package/dist/src/components/FunctionBase.d.ts.map +1 -1
  67. package/dist/src/components/FunctionBase.js +9 -2
  68. package/dist/src/components/FunctionBase.js.map +1 -1
  69. package/dist/src/components/FutureStatement.d.ts +1 -1
  70. package/dist/src/components/FutureStatement.js +1 -1
  71. package/dist/src/components/MethodBase.d.ts.map +1 -1
  72. package/dist/src/components/MethodBase.js +10 -2
  73. package/dist/src/components/MethodBase.js.map +1 -1
  74. package/dist/src/components/PropertyDeclaration.d.ts +29 -0
  75. package/dist/src/components/PropertyDeclaration.d.ts.map +1 -1
  76. package/dist/src/components/PropertyDeclaration.js +48 -1
  77. package/dist/src/components/PropertyDeclaration.js.map +1 -1
  78. package/dist/src/components/PydanticClassDeclaration.d.ts +120 -0
  79. package/dist/src/components/PydanticClassDeclaration.d.ts.map +1 -0
  80. package/dist/src/components/PydanticClassDeclaration.js +116 -0
  81. package/dist/src/components/PydanticClassDeclaration.js.map +1 -0
  82. package/dist/src/components/StaticMethodDeclaration.d.ts +3 -0
  83. package/dist/src/components/StaticMethodDeclaration.d.ts.map +1 -1
  84. package/dist/src/components/StaticMethodDeclaration.js +13 -3
  85. package/dist/src/components/StaticMethodDeclaration.js.map +1 -1
  86. package/dist/src/components/index.d.ts +1 -0
  87. package/dist/src/components/index.d.ts.map +1 -1
  88. package/dist/src/components/index.js +1 -0
  89. package/dist/src/components/index.js.map +1 -1
  90. package/dist/test/classdeclarations.test.js +25 -0
  91. package/dist/test/classdeclarations.test.js.map +1 -1
  92. package/dist/test/dataclassdeclarations.test.js +25 -0
  93. package/dist/test/dataclassdeclarations.test.js.map +1 -1
  94. package/dist/test/decoratorlist.test.d.ts +2 -0
  95. package/dist/test/decoratorlist.test.d.ts.map +1 -0
  96. package/dist/test/decoratorlist.test.js +60 -0
  97. package/dist/test/decoratorlist.test.js.map +1 -0
  98. package/dist/test/enums.test.js +27 -0
  99. package/dist/test/enums.test.js.map +1 -1
  100. package/dist/test/functiondeclaration.test.js +16 -0
  101. package/dist/test/functiondeclaration.test.js.map +1 -1
  102. package/dist/test/methoddeclaration.test.js +67 -0
  103. package/dist/test/methoddeclaration.test.js.map +1 -1
  104. package/dist/test/propertydeclaration.test.js +71 -1
  105. package/dist/test/propertydeclaration.test.js.map +1 -1
  106. package/dist/test/pydanticclassdeclarations.test.d.ts +2 -0
  107. package/dist/test/pydanticclassdeclarations.test.d.ts.map +1 -0
  108. package/dist/test/pydanticclassdeclarations.test.js +829 -0
  109. package/dist/test/pydanticclassdeclarations.test.js.map +1 -0
  110. package/dist/tsconfig.tsbuildinfo +1 -1
  111. package/docs/api/components/ClassDeclaration.md +10 -7
  112. package/docs/api/components/ClassEnumDeclaration.md +9 -6
  113. package/docs/api/components/ClassMethodDeclaration.md +7 -5
  114. package/docs/api/components/DataclassDeclaration.md +9 -5
  115. package/docs/api/components/DunderMethodDeclaration.md +7 -5
  116. package/docs/api/components/FunctionDeclaration.md +9 -5
  117. package/docs/api/components/FutureStatement.md +1 -1
  118. package/docs/api/components/MethodDeclaration.md +11 -6
  119. package/docs/api/components/PropertyDeclaration.md +11 -8
  120. package/docs/api/components/PydanticClassDeclaration.md +146 -0
  121. package/docs/api/components/StaticMethodDeclaration.md +7 -5
  122. package/docs/api/components/index.md +1 -0
  123. package/docs/api/index.md +3 -3
  124. package/docs/api/types/CommonFunctionProps.md +4 -3
  125. package/docs/api/types/PydanticModelConfigDictProps.md +32 -0
  126. package/docs/api/types/index.md +1 -0
  127. package/docs/api/variables/index.md +3 -0
  128. package/docs/api/variables/pydanticModule.md +27 -0
  129. package/docs/api/variables/pydanticSettingsModule.md +7 -0
  130. package/docs/api/variables/typingModule.md +9 -0
  131. package/package.json +4 -4
  132. package/src/builtins/python.ts +539 -1
  133. package/src/components/ClassDeclaration.tsx +23 -0
  134. package/src/components/ClassMethodDeclaration.tsx +9 -1
  135. package/src/components/DataclassDeclaration.tsx +18 -11
  136. package/src/components/DecoratorList.tsx +50 -0
  137. package/src/components/EnumDeclaration.tsx +11 -0
  138. package/src/components/FunctionBase.tsx +34 -3
  139. package/src/components/FutureStatement.tsx +1 -1
  140. package/src/components/MethodBase.tsx +6 -2
  141. package/src/components/PropertyDeclaration.tsx +48 -1
  142. package/src/components/PydanticClassDeclaration.tsx +222 -0
  143. package/src/components/StaticMethodDeclaration.tsx +7 -1
  144. package/src/components/index.ts +1 -0
  145. package/temp/api.json +1142 -84
  146. package/test/classdeclarations.test.tsx +27 -0
  147. package/test/dataclassdeclarations.test.tsx +25 -0
  148. package/test/decoratorlist.test.tsx +95 -0
  149. package/test/enums.test.tsx +29 -0
  150. package/test/functiondeclaration.test.tsx +17 -0
  151. package/test/methoddeclaration.test.tsx +70 -0
  152. package/test/propertydeclaration.test.tsx +66 -1
  153. package/test/pydanticclassdeclarations.test.tsx +836 -0
@@ -0,0 +1,50 @@
1
+ import { Children, For } from "@alloy-js/core";
2
+
3
+ /**
4
+ * Props for {@link DecoratorList}.
5
+ */
6
+ export interface DecoratorListProps {
7
+ /**
8
+ * Decorators rendered top-to-bottom. The first entry is rendered topmost,
9
+ * which (by Python's bottom-up application order) is the outermost decorator
10
+ * — i.e. it is applied **last** and wraps the result of everything below it.
11
+ *
12
+ * Falsy entries (other than `0`) are skipped, so conditional decorators can
13
+ * be written inline as `condition && <Decorator/>`.
14
+ */
15
+ decorators?: Children[];
16
+ }
17
+
18
+ /**
19
+ * Renders a list of decorators, one per line, with a single hardline between
20
+ * adjacent decorators and a trailing hardline only when at least one decorator
21
+ * is present.
22
+ *
23
+ * This is the canonical decorator-rendering primitive for method-, function-,
24
+ * and property-like components. Centralizing it ensures the decorator stack
25
+ * composes consistently — no stray blank lines between decorators, and no
26
+ * trailing newline when the list is empty.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * <DecoratorList decorators={[code`@field_validator("name")`, "@classmethod"]} />
31
+ * ```
32
+ * Renders:
33
+ * ```python
34
+ * @field_validator("name")
35
+ * @classmethod
36
+ * ```
37
+ *
38
+ * @remarks
39
+ * Internal helper, not exported from the package. Used by
40
+ * `MethodDeclarationBase`, `ClassMethodDeclaration`, `StaticMethodDeclaration`,
41
+ * and `PropertyDeclaration` to render their `decorators` prop above the
42
+ * intrinsic decorator (`@classmethod`, `@staticmethod`, `@property`) or `def`.
43
+ */
44
+ export function DecoratorList(props: DecoratorListProps) {
45
+ return (
46
+ <For each={props.decorators ?? []} hardline ender={<hbr />} skipFalsy>
47
+ {(dec) => dec}
48
+ </For>
49
+ );
50
+ }
@@ -7,6 +7,7 @@ import {
7
7
  import { enumModule } from "../builtins/python.js";
8
8
  import { createPythonSymbol } from "../symbol-creation.js";
9
9
  import { BaseDeclarationProps } from "./Declaration.js";
10
+ import { DecoratorList } from "./DecoratorList.jsx";
10
11
  import { EnumMember, EnumMemberProps } from "./EnumMember.js";
11
12
  import { MemberScope } from "./MemberScope.jsx";
12
13
  import { PythonBlock } from "./PythonBlock.jsx";
@@ -113,6 +114,15 @@ export interface ClassEnumProps extends EnumPropsBase {
113
114
  * Indicates that the enum members should be auto-generated.
114
115
  */
115
116
  auto?: boolean;
117
+ /**
118
+ * Decorators rendered above `class <Name>(<BaseType>):`. See
119
+ * {@link ClassDeclarationProps.decorators} for the source-order and falsy
120
+ * skip semantics.
121
+ *
122
+ * Not available on functional-enum syntax (`Name = Enum(...)`), which is a
123
+ * variable assignment rather than a class definition.
124
+ */
125
+ decorators?: Children[];
116
126
  }
117
127
 
118
128
  /**
@@ -203,6 +213,7 @@ export function ClassEnumDeclaration(props: ClassEnumProps) {
203
213
  }
204
214
  return (
205
215
  <CoreDeclaration symbol={sym}>
216
+ <DecoratorList decorators={props.decorators} />
206
217
  class {sym.name}({enumModule["."][baseType]})
207
218
  <MemberScope ownerSymbol={sym}>
208
219
  <PythonBlock opener=":">
@@ -1,8 +1,9 @@
1
- import { createContentSlot, Name, Show } from "@alloy-js/core";
1
+ import { createContentSlot, Name, Show, type Children } from "@alloy-js/core";
2
2
  import { PythonOutputSymbol } from "../index.js";
3
3
  import { getCallSignatureProps } from "../utils.js";
4
4
  import { CallSignature, CallSignatureProps } from "./CallSignature.jsx";
5
5
  import { BaseDeclarationProps, Declaration } from "./Declaration.js";
6
+ import { DecoratorList } from "./DecoratorList.jsx";
6
7
  import { LexicalScope } from "./LexicalScope.jsx";
7
8
  import { PythonBlock } from "./PythonBlock.jsx";
8
9
 
@@ -19,6 +20,35 @@ export interface CommonFunctionProps
19
20
  CallSignatureProps {
20
21
  /** Indicates that the function is async. */
21
22
  async?: boolean;
23
+ /**
24
+ * Decorators rendered above `def`, in source order — `decorators[0]` is
25
+ * topmost. By Python's bottom-up application order, the topmost entry is
26
+ * the **outermost** decorator (applied last) and wraps the result of every
27
+ * decorator below it.
28
+ *
29
+ * Each entry should produce a complete decorator line (typically starting
30
+ * with `@`). Falsy entries (other than `0`) are skipped, so conditional
31
+ * decorators can be provided inline when needed.
32
+ *
33
+ * When used through wrappers that emit an intrinsic decorator
34
+ * (`ClassMethodDeclaration` → `@classmethod`,
35
+ * `StaticMethodDeclaration` → `@staticmethod`,
36
+ * `PropertyDeclaration` → `@property`), these decorators are rendered
37
+ * **above** the intrinsic line — the correct position for Pydantic's
38
+ * `@field_validator` / `@model_validator` and other wrappers that must
39
+ * see the underlying function, not a descriptor.
40
+ *
41
+ * When used on plain `MethodDeclaration` / `FunctionDeclaration`, these
42
+ * decorators are rendered above `@abstractmethod` (if `abstract` is set)
43
+ * and above `def`.
44
+ *
45
+ * Do **not** pass intrinsic decorators here — i.e. `@classmethod`,
46
+ * `@staticmethod`, `@property`, or `@abstractmethod`. Those are emitted by
47
+ * the matching component (`ClassMethodDeclaration`, `StaticMethodDeclaration`,
48
+ * `PropertyDeclaration`, or the `abstract` flag) and would otherwise be
49
+ * stacked twice in the output, producing invalid Python.
50
+ */
51
+ decorators?: Children[];
22
52
  }
23
53
 
24
54
  /**
@@ -54,6 +84,7 @@ export interface BaseFunctionDeclarationProps extends CommonFunctionProps {
54
84
  * ```
55
85
  */
56
86
  export function BaseFunctionDeclaration(props: BaseFunctionDeclarationProps) {
87
+ const { decorators, sym, ...declarationProps } = props;
57
88
  const asyncKwd = props.async ? "async " : "";
58
89
  let parameters;
59
90
  switch (props.functionType) {
@@ -66,11 +97,11 @@ export function BaseFunctionDeclaration(props: BaseFunctionDeclarationProps) {
66
97
  default:
67
98
  parameters = props.parameters;
68
99
  }
69
- const sym: PythonOutputSymbol = props.sym;
70
100
  const ContentSlot = createContentSlot();
71
101
  return (
72
102
  <>
73
- <Declaration {...props} nameKind="function" symbol={sym}>
103
+ <DecoratorList decorators={decorators} />
104
+ <Declaration {...declarationProps} nameKind="function" symbol={sym}>
74
105
  {asyncKwd}def <Name />
75
106
  <LexicalScope name={sym.name}>
76
107
  <CallSignature
@@ -18,7 +18,7 @@ export interface FutureStatementProps {
18
18
  *
19
19
  * @example
20
20
  * ```tsx
21
- * <SourceFile path="models.py" futureImports={<FutureStatement feature="annotations" />}>
21
+ * <SourceFile path="models.py" futureImports={[<FutureStatement feature="annotations" />]}>
22
22
  * <ClassDeclaration name="User">
23
23
  * <PropertyDeclaration name="manager" type="User" />
24
24
  * </ClassDeclaration>
@@ -1,4 +1,5 @@
1
1
  import { abcModule } from "../builtins/python.js";
2
+ import { DecoratorList } from "./DecoratorList.jsx";
2
3
  import {
3
4
  BaseFunctionDeclaration,
4
5
  BaseFunctionDeclarationProps,
@@ -36,8 +37,10 @@ export function MethodDeclarationBase(
36
37
  props: MethodDeclarationBaseProps &
37
38
  Pick<BaseFunctionDeclarationProps, "functionType" | "sym">,
38
39
  ) {
40
+ const { decorators, abstract, ...rest } = props;
41
+
39
42
  const abstractMethod =
40
- props.abstract ?
43
+ abstract ?
41
44
  <>
42
45
  @{abcModule["."].abstractmethod}
43
46
  <hbr />
@@ -46,8 +49,9 @@ export function MethodDeclarationBase(
46
49
 
47
50
  return (
48
51
  <>
52
+ <DecoratorList decorators={decorators} />
49
53
  {abstractMethod}
50
- <BaseFunctionDeclaration {...props} />
54
+ <BaseFunctionDeclaration {...rest} />
51
55
  </>
52
56
  );
53
57
  }
@@ -16,6 +16,7 @@ import { PythonOutputSymbol } from "../index.js";
16
16
  import { ParameterDescriptor } from "../parameter-descriptor.js";
17
17
  import { createMethodSymbol } from "../symbols/factories.js";
18
18
  import { Atom } from "./Atom.jsx";
19
+ import { DecoratorList } from "./DecoratorList.jsx";
19
20
  import { CommonFunctionProps } from "./FunctionBase.js";
20
21
  import { MethodDeclarationBase } from "./MethodBase.js";
21
22
 
@@ -72,6 +73,29 @@ const PropertyContext = createContext<Children | undefined>();
72
73
  * The property must be declared within a class. The getter method is
73
74
  * automatically generated using the `@property` decorator. Use the nested
74
75
  * `Setter` and `Deleter` components to add mutators.
76
+ *
77
+ * Use **`decorators`** for decorators that must appear **above** the intrinsic
78
+ * `@property` (for example Pydantic `@computed_field`, `@typing.final`,
79
+ * `@typing.override`). The first array entry is rendered topmost, matching
80
+ * Python source order.
81
+ *
82
+ * @example Pydantic computed field
83
+ * ```tsx
84
+ * <py.PropertyDeclaration
85
+ * name="area"
86
+ * type="float"
87
+ * decorators={[code`@${pydanticModule["."].computed_field}`]}
88
+ * >
89
+ * return self.width ** 2
90
+ * </py.PropertyDeclaration>
91
+ * ```
92
+ * Generates:
93
+ * ```python
94
+ * @computed_field
95
+ * @property
96
+ * def area(self) -> float:
97
+ * return self.width ** 2
98
+ * ```
75
99
  */
76
100
  export interface PropertyDeclarationProps {
77
101
  name: string;
@@ -80,6 +104,12 @@ export interface PropertyDeclarationProps {
80
104
  refkey?: Refkey;
81
105
  abstract?: boolean;
82
106
  doc?: Children;
107
+ /**
108
+ * Decorators rendered above the intrinsic `@property` line, in source order
109
+ * (`decorators[0]` is topmost / applied last). Use for decorators that wrap
110
+ * the resulting property, e.g. Pydantic's `@computed_field`.
111
+ */
112
+ decorators?: Children[];
83
113
  }
84
114
 
85
115
  export function PropertyDeclaration(props: PropertyDeclarationProps) {
@@ -120,6 +150,7 @@ export function PropertyDeclaration(props: PropertyDeclarationProps) {
120
150
  <PropertyMethodDeclaration
121
151
  abstract={props.abstract}
122
152
  doc={props.doc}
153
+ decorators={props.decorators}
123
154
  >
124
155
  {unkeyedChildren}
125
156
  </PropertyMethodDeclaration>
@@ -155,13 +186,15 @@ export interface PropertyMethodDeclarationProps
155
186
  function PropertyMethodDeclaration(props: PropertyMethodDeclarationProps) {
156
187
  const propertySymbol = useContext(DeclarationContext) as PythonOutputSymbol;
157
188
  const propertyType = useContext(PropertyContext);
189
+ const { decorators, ...rest } = props;
158
190
 
159
191
  return (
160
192
  <>
193
+ <DecoratorList decorators={decorators} />
161
194
  {code`@property`}
162
195
  <hbr />
163
196
  <MethodDeclarationBase
164
- {...props}
197
+ {...rest}
165
198
  name={propertySymbol.name}
166
199
  functionType="instance"
167
200
  returnType={propertyType}
@@ -219,6 +252,13 @@ function PropertyMethodBase(props: PropertyMethodBaseProps) {
219
252
  * def value(self, value: int) -> None:
220
253
  * self._value = value
221
254
  * ```
255
+ *
256
+ * @remarks
257
+ * The intrinsic `@<name>.setter` decorator is always rendered topmost (it must
258
+ * remain the outermost decorator). Any `decorators` passed to this component
259
+ * are rendered **between** `@<name>.setter` and `def`, which is the correct
260
+ * position for wrappers that should decorate the underlying function before
261
+ * `setter` re-binds it to the property (e.g. `@deprecated`, `@abstractmethod`).
222
262
  */
223
263
  PropertyDeclaration.Setter = taggedComponent(
224
264
  setterTag,
@@ -253,6 +293,13 @@ PropertyDeclaration.Setter = taggedComponent(
253
293
  * def value(self) -> None:
254
294
  * del self._value
255
295
  * ```
296
+ *
297
+ * @remarks
298
+ * The intrinsic `@<name>.deleter` decorator is always rendered topmost (it
299
+ * must remain the outermost decorator). Any `decorators` passed to this
300
+ * component are rendered **between** `@<name>.deleter` and `def`, which is
301
+ * the correct position for wrappers that should decorate the underlying
302
+ * function before `deleter` re-binds it to the property.
256
303
  */
257
304
  PropertyDeclaration.Deleter = taggedComponent(
258
305
  deleterTag,
@@ -0,0 +1,222 @@
1
+ import {
2
+ For,
3
+ List,
4
+ childrenArray,
5
+ splitProps,
6
+ type Children,
7
+ } from "@alloy-js/core";
8
+ import { snakeCase } from "change-case";
9
+ import { pydanticModule } from "../builtins/python.js";
10
+ import { Atom } from "./Atom.jsx";
11
+ import type { ClassDeclarationProps } from "./ClassDeclaration.js";
12
+ import { ClassDeclaration } from "./ClassDeclaration.js";
13
+
14
+ /**
15
+ * Keyword-style options for Pydantic v2 `ConfigDict`, using camelCase prop names
16
+ * that map to snake_case Python arguments (for example `validateAssignment` →
17
+ * `validate_assignment`).
18
+ */
19
+ export interface PydanticModelConfigDictProps {
20
+ /** Resolve field aliases from a configured alias generator. */
21
+ aliasGenerator?: string;
22
+ /** Allow non-pydantic/arbitrary Python types in field annotations. */
23
+ arbitraryTypesAllowed?: boolean;
24
+ /** Coerce numeric input values to strings for `str` fields. */
25
+ coerceNumbersToStr?: boolean;
26
+ /** Behavior for unknown input keys: allow, forbid, or ignore. */
27
+ extra?: "allow" | "forbid" | "ignore";
28
+ /** Populate models from object attributes (ORM-style) instead of mapping keys. */
29
+ fromAttributes?: boolean;
30
+ /** Make models immutable (`frozen=True`). */
31
+ frozen?: boolean;
32
+ /** Hide input values in validation error messages. */
33
+ hideInputInErrors?: boolean;
34
+ /** Include JSON schema extras via a plain JSON-serializable object. */
35
+ jsonSchemaExtra?: Record<string, unknown>;
36
+ /** Use aliases in error locations instead of field names. */
37
+ locByAlias?: boolean;
38
+ /** Allow population by field name even when aliases are defined. */
39
+ populateByName?: boolean;
40
+ /** Re-validate model/dataclass instances on assignment boundaries. */
41
+ revalidateInstances?: "always" | "never" | "subclass-instances";
42
+ /** JSON serialization format for bytes values. */
43
+ serJsonBytes?: "utf8" | "base64" | "hex";
44
+ /** JSON serialization behavior for Infinity/NaN values. */
45
+ serJsonInfNan?: "null" | "constants" | "strings";
46
+ /** Upper-bound for constrained string lengths at model level. */
47
+ strMaxLength?: number;
48
+ /** Lower-bound for constrained string lengths at model level. */
49
+ strMinLength?: number;
50
+ /** Strip leading/trailing whitespace from all `str` fields. */
51
+ strStripWhitespace?: boolean;
52
+ /** Convert all `str` values to lowercase. */
53
+ strToLower?: boolean;
54
+ /** Convert all `str` values to uppercase. */
55
+ strToUpper?: boolean;
56
+ /** Enable strict validation globally for the model. */
57
+ strict?: boolean;
58
+ /** Use enum `.value` instead of enum instances during serialization. */
59
+ useEnumValues?: boolean;
60
+ /** JSON validation format for bytes values. */
61
+ valJsonBytes?: "utf8" | "base64" | "hex";
62
+ /** Re-validate when attributes are assigned after model creation. */
63
+ validateAssignment?: boolean;
64
+ /** Validate default values in addition to provided input values. */
65
+ validateDefault?: boolean;
66
+ /** Validate return values for call validators. */
67
+ validateReturn?: boolean;
68
+ }
69
+
70
+ const PydanticModelConfigKeys = [
71
+ "aliasGenerator",
72
+ "arbitraryTypesAllowed",
73
+ "coerceNumbersToStr",
74
+ "frozen",
75
+ "extra",
76
+ "fromAttributes",
77
+ "hideInputInErrors",
78
+ "jsonSchemaExtra",
79
+ "locByAlias",
80
+ "populateByName",
81
+ "revalidateInstances",
82
+ "serJsonBytes",
83
+ "serJsonInfNan",
84
+ "strMinLength",
85
+ "strMaxLength",
86
+ "strStripWhitespace",
87
+ "strToLower",
88
+ "strToUpper",
89
+ "strict",
90
+ "useEnumValues",
91
+ "valJsonBytes",
92
+ "validateAssignment",
93
+ "validateDefault",
94
+ "validateReturn",
95
+ ] as const satisfies readonly (keyof PydanticModelConfigDictProps)[];
96
+
97
+ export interface PydanticClassDeclarationProps
98
+ extends ClassDeclarationProps,
99
+ PydanticModelConfigDictProps {
100
+ /**
101
+ * Canonical structured config object for `ConfigDict(...)`. Values here are
102
+ * merged with top-level config props.
103
+ *
104
+ * Top-level config props take precedence over `modelConfig` when the same key
105
+ * is provided in both places.
106
+ */
107
+ modelConfig?: PydanticModelConfigDictProps;
108
+ /**
109
+ * Emits `model_config = <expression>` verbatim (use for arbitrary `ConfigDict`
110
+ * kwargs or dynamic config). Takes precedence over both `modelConfig` and
111
+ * top-level config props.
112
+ *
113
+ * @example
114
+ * A typical value emits `ConfigDict(frozen=True, extra="forbid")`.
115
+ */
116
+ modelConfigExpression?: Children;
117
+ }
118
+
119
+ /**
120
+ * Renders a Python class that subclasses Pydantic's `BaseModel`:
121
+ * `class Name(BaseModel): ...`.
122
+ *
123
+ * When `bases` is omitted, the class extends `pydanticModule["."].BaseModel`.
124
+ * Pass `bases` to inherit from another generated class (or to combine bases explicitly).
125
+ *
126
+ * Optional `modelConfig={{...}}` and top-level config props (for example
127
+ * `frozen`, `strict`, `extra`) emit `model_config = ConfigDict(...)` for common
128
+ * Pydantic v2 model settings (see {@link PydanticModelConfigDictProps}).
129
+ * When both are used, top-level props override `modelConfig` keys.
130
+ *
131
+ * Fields are ordinary class body declarations; use `pydanticModule["."].Field` in
132
+ * initializers when you need `Field(...)`.
133
+ *
134
+ * @example
135
+ * ```tsx
136
+ * import { pydanticModule } from "@alloy-js/python";
137
+ * import * as py from "@alloy-js/python";
138
+ *
139
+ * <py.PydanticClassDeclaration name="User" frozen>
140
+ * <py.VariableDeclaration instanceVariable omitNone name="id" type="int" />
141
+ * <py.VariableDeclaration
142
+ * instanceVariable
143
+ * name="name"
144
+ * type="str"
145
+ * initializer={"Field(default=\"anonymous\")"}
146
+ * />
147
+ * </py.PydanticClassDeclaration>
148
+ * ```
149
+ *
150
+ * ```py
151
+ * from pydantic import BaseModel
152
+ * from pydantic import ConfigDict
153
+ * from pydantic import Field
154
+ *
155
+ * class User(BaseModel):
156
+ * model_config = ConfigDict(frozen=True)
157
+ * id: int
158
+ * name: str = Field(default="anonymous")
159
+ * ```
160
+ */
161
+ export function PydanticClassDeclaration(props: PydanticClassDeclarationProps) {
162
+ const [modelConfigProps, bodyProps, rest] = splitProps(
163
+ props,
164
+ PydanticModelConfigKeys,
165
+ ["modelConfig", "modelConfigExpression", "children"],
166
+ );
167
+ const bases = rest.bases ?? [pydanticModule["."].BaseModel];
168
+ const { modelConfig, modelConfigExpression, children } = bodyProps;
169
+
170
+ const configEntries: Array<[string, unknown]> = [];
171
+ if (modelConfigExpression === undefined) {
172
+ for (const key of PydanticModelConfigKeys) {
173
+ const value = modelConfigProps[key] ?? modelConfig?.[key];
174
+ if (value === undefined) continue;
175
+ configEntries.push([snakeCase(key), value]);
176
+ }
177
+ }
178
+ const hasStructuredModelConfig =
179
+ modelConfigExpression === undefined && configEntries.length > 0;
180
+ const hasExpressionModelConfig = modelConfigExpression !== undefined;
181
+
182
+ const bodyItems: Children[] = [];
183
+ if (hasExpressionModelConfig) {
184
+ bodyItems.push(
185
+ <>
186
+ {"model_config = "}
187
+ {modelConfigExpression}
188
+ </>,
189
+ );
190
+ } else if (hasStructuredModelConfig) {
191
+ bodyItems.push(
192
+ <>
193
+ {"model_config = "}
194
+ {pydanticModule["."].ConfigDict}
195
+ {"("}
196
+ <For each={configEntries} comma space>
197
+ {([k, v]) => (
198
+ <>
199
+ {k}=<Atom jsValue={v} />
200
+ </>
201
+ )}
202
+ </For>
203
+ {")"}
204
+ </>,
205
+ );
206
+ }
207
+ bodyItems.push(...childrenArray(() => children));
208
+
209
+ return (
210
+ <ClassDeclaration
211
+ name={rest.name}
212
+ bases={bases}
213
+ doc={rest.doc}
214
+ refkey={rest.refkey}
215
+ decorators={rest.decorators}
216
+ >
217
+ <List hardline>
218
+ <For each={bodyItems}>{(item) => item}</For>
219
+ </List>
220
+ </ClassDeclaration>
221
+ );
222
+ }
@@ -1,4 +1,5 @@
1
1
  import { createMethodSymbol } from "../symbols/factories.js";
2
+ import { DecoratorList } from "./DecoratorList.jsx";
2
3
  import type { CommonFunctionProps } from "./FunctionBase.js";
3
4
  import { MethodDeclarationBase } from "./MethodBase.js";
4
5
 
@@ -17,6 +18,9 @@ import { MethodDeclarationBase } from "./MethodBase.js";
17
18
  * def identity(value: int) -> None:
18
19
  * return value
19
20
  * ```
21
+ *
22
+ * @remarks
23
+ * Use **`decorators`** for decorators that must appear above `@staticmethod`.
20
24
  */
21
25
  export interface StaticMethodDeclarationProps extends CommonFunctionProps {
22
26
  abstract?: boolean;
@@ -24,11 +28,13 @@ export interface StaticMethodDeclarationProps extends CommonFunctionProps {
24
28
 
25
29
  export function StaticMethodDeclaration(props: StaticMethodDeclarationProps) {
26
30
  const sym = createMethodSymbol(props.name, { refkeys: props.refkey });
31
+ const { decorators, ...rest } = props;
27
32
  return (
28
33
  <>
34
+ <DecoratorList decorators={decorators} />
29
35
  {"@staticmethod"}
30
36
  <hbr />
31
- <MethodDeclarationBase functionType="static" {...props} sym={sym} />
37
+ <MethodDeclarationBase functionType="static" {...rest} sym={sym} />
32
38
  </>
33
39
  );
34
40
  }
@@ -19,6 +19,7 @@ export * from "./MemberScope.jsx";
19
19
  export type { MethodDeclarationBaseProps } from "./MethodBase.js";
20
20
  export * from "./MethodDeclaration.js";
21
21
  export * from "./PropertyDeclaration.js";
22
+ export * from "./PydanticClassDeclaration.js";
22
23
  export * from "./PyDoc.js";
23
24
  export * from "./PythonBlock.js";
24
25
  export * from "./Reference.js";