@alloy-js/python 0.0.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 (206) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/LICENSE +7 -0
  3. package/api-extractor.json +4 -0
  4. package/dist/src/builtins/python.d.ts +9 -0
  5. package/dist/src/builtins/python.d.ts.map +1 -0
  6. package/dist/src/builtins/python.js +17 -0
  7. package/dist/src/components/Atom.d.ts +19 -0
  8. package/dist/src/components/Atom.d.ts.map +1 -0
  9. package/dist/src/components/Atom.js +82 -0
  10. package/dist/src/components/CallSignature.d.ts +79 -0
  11. package/dist/src/components/CallSignature.d.ts.map +1 -0
  12. package/dist/src/components/CallSignature.js +201 -0
  13. package/dist/src/components/ClassDeclaration.d.ts +37 -0
  14. package/dist/src/components/ClassDeclaration.d.ts.map +1 -0
  15. package/dist/src/components/ClassDeclaration.js +83 -0
  16. package/dist/src/components/ClassInstantiation.d.ts +24 -0
  17. package/dist/src/components/ClassInstantiation.d.ts.map +1 -0
  18. package/dist/src/components/ClassInstantiation.js +35 -0
  19. package/dist/src/components/Declaration.d.ts +48 -0
  20. package/dist/src/components/Declaration.d.ts.map +1 -0
  21. package/dist/src/components/Declaration.js +37 -0
  22. package/dist/src/components/EnumDeclaration.d.ts +164 -0
  23. package/dist/src/components/EnumDeclaration.d.ts.map +1 -0
  24. package/dist/src/components/EnumDeclaration.js +278 -0
  25. package/dist/src/components/EnumMember.d.ts +46 -0
  26. package/dist/src/components/EnumMember.d.ts.map +1 -0
  27. package/dist/src/components/EnumMember.js +67 -0
  28. package/dist/src/components/FunctionCallExpression.d.ts +19 -0
  29. package/dist/src/components/FunctionCallExpression.d.ts.map +1 -0
  30. package/dist/src/components/FunctionCallExpression.js +40 -0
  31. package/dist/src/components/FunctionDeclaration.d.ts +47 -0
  32. package/dist/src/components/FunctionDeclaration.d.ts.map +1 -0
  33. package/dist/src/components/FunctionDeclaration.js +107 -0
  34. package/dist/src/components/ImportStatement.d.ts +39 -0
  35. package/dist/src/components/ImportStatement.d.ts.map +1 -0
  36. package/dist/src/components/ImportStatement.js +104 -0
  37. package/dist/src/components/MemberExpression.d.ts +97 -0
  38. package/dist/src/components/MemberExpression.d.ts.map +1 -0
  39. package/dist/src/components/MemberExpression.js +308 -0
  40. package/dist/src/components/NoNamePolicy.d.ts +23 -0
  41. package/dist/src/components/NoNamePolicy.d.ts.map +1 -0
  42. package/dist/src/components/NoNamePolicy.js +27 -0
  43. package/dist/src/components/PyDoc.d.ts +90 -0
  44. package/dist/src/components/PyDoc.d.ts.map +1 -0
  45. package/dist/src/components/PyDoc.js +280 -0
  46. package/dist/src/components/PythonBlock.d.ts +23 -0
  47. package/dist/src/components/PythonBlock.d.ts.map +1 -0
  48. package/dist/src/components/PythonBlock.js +31 -0
  49. package/dist/src/components/Reference.d.ts +13 -0
  50. package/dist/src/components/Reference.d.ts.map +1 -0
  51. package/dist/src/components/Reference.js +18 -0
  52. package/dist/src/components/SourceFile.d.ts +46 -0
  53. package/dist/src/components/SourceFile.d.ts.map +1 -0
  54. package/dist/src/components/SourceFile.js +75 -0
  55. package/dist/src/components/StatementList.d.ts +25 -0
  56. package/dist/src/components/StatementList.d.ts.map +1 -0
  57. package/dist/src/components/StatementList.js +29 -0
  58. package/dist/src/components/VariableDeclaration.d.ts +62 -0
  59. package/dist/src/components/VariableDeclaration.d.ts.map +1 -0
  60. package/dist/src/components/VariableDeclaration.js +131 -0
  61. package/dist/src/components/index.d.ts +19 -0
  62. package/dist/src/components/index.d.ts.map +1 -0
  63. package/dist/src/components/index.js +18 -0
  64. package/dist/src/create-module.d.ts +16 -0
  65. package/dist/src/create-module.d.ts.map +1 -0
  66. package/dist/src/create-module.js +64 -0
  67. package/dist/src/index.d.ts +8 -0
  68. package/dist/src/index.d.ts.map +1 -0
  69. package/dist/src/index.js +7 -0
  70. package/dist/src/name-policy.d.ts +5 -0
  71. package/dist/src/name-policy.d.ts.map +1 -0
  72. package/dist/src/name-policy.js +47 -0
  73. package/dist/src/parameter-descriptor.d.ts +31 -0
  74. package/dist/src/parameter-descriptor.d.ts.map +1 -0
  75. package/dist/src/parameter-descriptor.js +1 -0
  76. package/dist/src/symbol-creation.d.ts +4 -0
  77. package/dist/src/symbol-creation.d.ts.map +1 -0
  78. package/dist/src/symbol-creation.js +24 -0
  79. package/dist/src/symbols/custom-output-scope.d.ts +10 -0
  80. package/dist/src/symbols/custom-output-scope.d.ts.map +1 -0
  81. package/dist/src/symbols/custom-output-scope.js +25 -0
  82. package/dist/src/symbols/index.d.ts +7 -0
  83. package/dist/src/symbols/index.d.ts.map +1 -0
  84. package/dist/src/symbols/index.js +6 -0
  85. package/dist/src/symbols/python-member-scope.d.ts +7 -0
  86. package/dist/src/symbols/python-member-scope.d.ts.map +1 -0
  87. package/dist/src/symbols/python-member-scope.js +9 -0
  88. package/dist/src/symbols/python-module-scope.d.ts +25 -0
  89. package/dist/src/symbols/python-module-scope.d.ts.map +1 -0
  90. package/dist/src/symbols/python-module-scope.js +52 -0
  91. package/dist/src/symbols/python-output-symbol.d.ts +19 -0
  92. package/dist/src/symbols/python-output-symbol.d.ts.map +1 -0
  93. package/dist/src/symbols/python-output-symbol.js +22 -0
  94. package/dist/src/symbols/reference.d.ts +4 -0
  95. package/dist/src/symbols/reference.d.ts.map +1 -0
  96. package/dist/src/symbols/reference.js +60 -0
  97. package/dist/src/symbols/scopes.d.ts +5 -0
  98. package/dist/src/symbols/scopes.d.ts.map +1 -0
  99. package/dist/src/symbols/scopes.js +4 -0
  100. package/dist/src/utils.d.ts +7 -0
  101. package/dist/src/utils.d.ts.map +1 -0
  102. package/dist/src/utils.js +12 -0
  103. package/dist/test/callsignatures.test.d.ts +2 -0
  104. package/dist/test/callsignatures.test.d.ts.map +1 -0
  105. package/dist/test/callsignatures.test.js +276 -0
  106. package/dist/test/classdeclarations.test.d.ts +2 -0
  107. package/dist/test/classdeclarations.test.d.ts.map +1 -0
  108. package/dist/test/classdeclarations.test.js +397 -0
  109. package/dist/test/classinstantiations.test.d.ts +2 -0
  110. package/dist/test/classinstantiations.test.d.ts.map +1 -0
  111. package/dist/test/classinstantiations.test.js +168 -0
  112. package/dist/test/enums.test.d.ts +2 -0
  113. package/dist/test/enums.test.d.ts.map +1 -0
  114. package/dist/test/enums.test.js +211 -0
  115. package/dist/test/externals.test.d.ts +2 -0
  116. package/dist/test/externals.test.d.ts.map +1 -0
  117. package/dist/test/externals.test.js +219 -0
  118. package/dist/test/functioncallexpressions.test.d.ts +2 -0
  119. package/dist/test/functioncallexpressions.test.d.ts.map +1 -0
  120. package/dist/test/functioncallexpressions.test.js +156 -0
  121. package/dist/test/functiondeclaration.test.d.ts +2 -0
  122. package/dist/test/functiondeclaration.test.d.ts.map +1 -0
  123. package/dist/test/functiondeclaration.test.js +363 -0
  124. package/dist/test/imports.test.d.ts +2 -0
  125. package/dist/test/imports.test.d.ts.map +1 -0
  126. package/dist/test/imports.test.js +262 -0
  127. package/dist/test/memberexpressions.test.d.ts +2 -0
  128. package/dist/test/memberexpressions.test.d.ts.map +1 -0
  129. package/dist/test/memberexpressions.test.js +879 -0
  130. package/dist/test/namepolicies.test.d.ts +2 -0
  131. package/dist/test/namepolicies.test.d.ts.map +1 -0
  132. package/dist/test/namepolicies.test.js +109 -0
  133. package/dist/test/pydocs.test.d.ts +2 -0
  134. package/dist/test/pydocs.test.d.ts.map +1 -0
  135. package/dist/test/pydocs.test.js +500 -0
  136. package/dist/test/references.test.d.ts +2 -0
  137. package/dist/test/references.test.d.ts.map +1 -0
  138. package/dist/test/references.test.js +49 -0
  139. package/dist/test/sourcefiles.test.d.ts +2 -0
  140. package/dist/test/sourcefiles.test.d.ts.map +1 -0
  141. package/dist/test/sourcefiles.test.js +198 -0
  142. package/dist/test/utils.d.ts +23 -0
  143. package/dist/test/utils.d.ts.map +1 -0
  144. package/dist/test/utils.js +88 -0
  145. package/dist/test/values.test.d.ts +2 -0
  146. package/dist/test/values.test.d.ts.map +1 -0
  147. package/dist/test/values.test.js +78 -0
  148. package/dist/test/variables.test.d.ts +2 -0
  149. package/dist/test/variables.test.d.ts.map +1 -0
  150. package/dist/test/variables.test.js +173 -0
  151. package/dist/tsconfig.tsbuildinfo +1 -0
  152. package/package.json +39 -0
  153. package/src/builtins/python.ts +20 -0
  154. package/src/components/Atom.tsx +76 -0
  155. package/src/components/CallSignature.tsx +251 -0
  156. package/src/components/ClassDeclaration.tsx +98 -0
  157. package/src/components/ClassInstantiation.tsx +54 -0
  158. package/src/components/Declaration.tsx +91 -0
  159. package/src/components/EnumDeclaration.tsx +291 -0
  160. package/src/components/EnumMember.tsx +92 -0
  161. package/src/components/FunctionCallExpression.tsx +36 -0
  162. package/src/components/FunctionDeclaration.tsx +121 -0
  163. package/src/components/ImportStatement.tsx +134 -0
  164. package/src/components/MemberExpression.tsx +456 -0
  165. package/src/components/NoNamePolicy.tsx +31 -0
  166. package/src/components/PyDoc.tsx +331 -0
  167. package/src/components/PythonBlock.tsx +26 -0
  168. package/src/components/Reference.tsx +21 -0
  169. package/src/components/SourceFile.tsx +93 -0
  170. package/src/components/StatementList.tsx +28 -0
  171. package/src/components/VariableDeclaration.tsx +180 -0
  172. package/src/components/index.ts +18 -0
  173. package/src/create-module.ts +102 -0
  174. package/src/index.ts +7 -0
  175. package/src/name-policy.ts +101 -0
  176. package/src/parameter-descriptor.ts +36 -0
  177. package/src/symbol-creation.ts +36 -0
  178. package/src/symbols/custom-output-scope.ts +35 -0
  179. package/src/symbols/index.ts +6 -0
  180. package/src/symbols/python-member-scope.ts +12 -0
  181. package/src/symbols/python-module-scope.ts +89 -0
  182. package/src/symbols/python-output-symbol.ts +36 -0
  183. package/src/symbols/reference.ts +99 -0
  184. package/src/symbols/scopes.ts +9 -0
  185. package/src/utils.ts +27 -0
  186. package/temp/api.json +7207 -0
  187. package/test/callsignatures.test.tsx +256 -0
  188. package/test/classdeclarations.test.tsx +320 -0
  189. package/test/classinstantiations.test.tsx +159 -0
  190. package/test/enums.test.tsx +203 -0
  191. package/test/externals.test.tsx +190 -0
  192. package/test/functioncallexpressions.test.tsx +145 -0
  193. package/test/functiondeclaration.test.tsx +327 -0
  194. package/test/imports.test.tsx +214 -0
  195. package/test/memberexpressions.test.tsx +725 -0
  196. package/test/namepolicies.test.tsx +109 -0
  197. package/test/pydocs.test.tsx +528 -0
  198. package/test/references.test.tsx +36 -0
  199. package/test/sourcefiles.test.tsx +131 -0
  200. package/test/utils.tsx +131 -0
  201. package/test/values.test.tsx +61 -0
  202. package/test/variables.test.tsx +153 -0
  203. package/tsconfig.json +12 -0
  204. package/tsdoc-metadata.json +11 -0
  205. package/vitest.config.ts +10 -0
  206. package/vitest.setup.ts +1 -0
@@ -0,0 +1,18 @@
1
+ export * from "./Atom.js";
2
+ export * from "./CallSignature.js";
3
+ export * from "./ClassDeclaration.js";
4
+ export * from "./ClassInstantiation.js";
5
+ export * from "./Declaration.js";
6
+ export * from "./EnumDeclaration.js";
7
+ export * from "./EnumMember.js";
8
+ export * from "./FunctionCallExpression.js";
9
+ export * from "./FunctionDeclaration.js";
10
+ export * from "./ImportStatement.js";
11
+ export * from "./MemberExpression.js";
12
+ export * from "./NoNamePolicy.js";
13
+ export * from "./PyDoc.js";
14
+ export * from "./PythonBlock.js";
15
+ export * from "./Reference.js";
16
+ export * from "./SourceFile.js";
17
+ export * from "./StatementList.js";
18
+ export * from "./VariableDeclaration.js";
@@ -0,0 +1,102 @@
1
+ import {
2
+ type Binder,
3
+ getSymbolCreatorSymbol,
4
+ type Refkey,
5
+ refkey,
6
+ SymbolCreator,
7
+ } from "@alloy-js/core";
8
+ import { createPythonSymbol } from "./symbol-creation.js";
9
+ import { PythonModuleScope } from "./symbols/index.js";
10
+
11
+ export interface ModuleDescriptor {
12
+ [path: string]: string[];
13
+ }
14
+
15
+ export interface CreateModuleProps<T extends ModuleDescriptor> {
16
+ name: string;
17
+ descriptor: T;
18
+ }
19
+
20
+ export type NamedMap<TDescriptor extends readonly string[]> = {
21
+ [S in TDescriptor[number]]: Refkey;
22
+ };
23
+
24
+ export type ModuleRefkeys<PD extends Record<string, string[]>> = {
25
+ [P in keyof PD]: NamedMap<PD[P]>;
26
+ };
27
+
28
+ function createSymbols(
29
+ binder: Binder,
30
+ props: CreateModuleProps<ModuleDescriptor>,
31
+ refkeys: Record<string, any>,
32
+ ) {
33
+ // Create a module scope for each path in the descriptor
34
+ for (const [path, symbols] of Object.entries(props.descriptor)) {
35
+ // If the path is ".", we use the module name directly
36
+ // Otherwise, we append the path to the module name
37
+ const fullModuleScopeName = props.name + (path === "." ? "" : `.${path}`);
38
+ const keys = refkeys[path];
39
+ const moduleScope = new PythonModuleScope(fullModuleScopeName, {
40
+ parent: undefined,
41
+ binder: binder,
42
+ });
43
+
44
+ // Create a symbol for each exported name
45
+ for (const exportedName of symbols ?? []) {
46
+ const key = keys[exportedName];
47
+
48
+ const _ = createPythonSymbol(
49
+ exportedName,
50
+ {
51
+ binder: binder,
52
+ scope: moduleScope,
53
+ refkeys: key,
54
+ module: moduleScope.name,
55
+ },
56
+ undefined,
57
+ false,
58
+ );
59
+ }
60
+ }
61
+ }
62
+
63
+ // A module is a map of refkeys for each path in the descriptor.
64
+ // Each path maps to a set of named refkeys for the exported symbols.
65
+ // When one of the symbols is used, the relevant import will be added
66
+ // to the source file, and the symbol will be available in the output.
67
+ //
68
+ // Most of the use cases will be for templates and not for use in JSX,
69
+ // so the symbols will not be normally used directly in the code, but
70
+ // there may be cases where you want to use them directly.
71
+ //
72
+ // Example:
73
+ // ```ts
74
+ // const requestsLib = createModule({
75
+ // name: "requests",
76
+ // descriptor: {
77
+ // ".": ["get", "post"],
78
+ // },
79
+ // });
80
+ // ```
81
+ //
82
+ // For this scenario, if requestsLib["."].get is used, it will add
83
+ // the import `from requests import get` and would render the symbol
84
+ // as `get` in the output.
85
+ export function createModule<const T extends ModuleDescriptor>(
86
+ props: CreateModuleProps<T>,
87
+ ): ModuleRefkeys<T> & SymbolCreator {
88
+ const refkeys: any = {
89
+ [getSymbolCreatorSymbol()](binder: Binder) {
90
+ createSymbols(binder, props, refkeys);
91
+ },
92
+ };
93
+
94
+ for (const [path, symbols] of Object.entries(props.descriptor)) {
95
+ const keys: Record<string, Refkey> = (refkeys[path] = {});
96
+ for (const named of symbols ?? []) {
97
+ keys[named] = refkey(props.descriptor, path, named);
98
+ }
99
+ }
100
+
101
+ return refkeys;
102
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./builtins/python.js";
2
+ export * from "./components/index.js";
3
+ export * from "./create-module.js";
4
+ export * from "./name-policy.js";
5
+ export * from "./parameter-descriptor.js";
6
+ export * from "./symbols/index.js";
7
+ export * from "./utils.js";
@@ -0,0 +1,101 @@
1
+ import { createNamePolicy, NamePolicy, useNamePolicy } from "@alloy-js/core";
2
+ import { constantCase, pascalCase, snakeCase } from "change-case";
3
+
4
+ export type PythonElements =
5
+ | "class" // Classes
6
+ | "class-member" // Class members (attributes, methods)
7
+ | "function" // Functions
8
+ | "variable" // Variables
9
+ | "object-member" // Object members (attributes, methods)
10
+ | "constant"
11
+ | "parameter"
12
+ | "method"
13
+ | "module"
14
+ | "enum" // Enums
15
+ | "enum-member"; // Enum members
16
+
17
+ // Reserved words
18
+ const GLOBAL_RESERVED_WORDS = new Set([
19
+ "False",
20
+ "None",
21
+ "True",
22
+ "and",
23
+ "as",
24
+ "assert",
25
+ "async",
26
+ "await",
27
+ "break",
28
+ "class",
29
+ "continue",
30
+ "def",
31
+ "del",
32
+ "elif",
33
+ "else",
34
+ "except",
35
+ "finally",
36
+ "for",
37
+ "from",
38
+ "global",
39
+ "if",
40
+ "import",
41
+ "in",
42
+ "is",
43
+ "lambda",
44
+ "nonlocal",
45
+ "not",
46
+ "or",
47
+ "pass",
48
+ "raise",
49
+ "return",
50
+ "try",
51
+ "while",
52
+ "with",
53
+ "and",
54
+ "yield",
55
+ ]);
56
+
57
+ /**
58
+ * Ensures a valid Python identifier for the given element kind.
59
+ * @param name - The name to validate.
60
+ * @param element - The Python element kind.
61
+ * @returns A Python-safe name.
62
+ */
63
+ function ensureNonReservedName(name: string): string {
64
+ const suffix = "_";
65
+
66
+ // Global reserved words always need handling
67
+ if (GLOBAL_RESERVED_WORDS.has(name)) {
68
+ return `${name}${suffix}`;
69
+ }
70
+
71
+ return name;
72
+ }
73
+
74
+ export function createPythonNamePolicy(): NamePolicy<PythonElements> {
75
+ return createNamePolicy((name, element) => {
76
+ let transformedName: string;
77
+
78
+ switch (element) {
79
+ case "class":
80
+ case "enum":
81
+ transformedName = pascalCase(name);
82
+ break;
83
+ case "constant":
84
+ case "enum-member":
85
+ transformedName = constantCase(name);
86
+ break;
87
+ case "module":
88
+ transformedName = snakeCase(name).toLowerCase();
89
+ break;
90
+ default:
91
+ transformedName = snakeCase(name);
92
+ break;
93
+ }
94
+
95
+ return ensureNonReservedName(transformedName);
96
+ });
97
+ }
98
+
99
+ export function usePythonNamePolicy(): NamePolicy<PythonElements> {
100
+ return useNamePolicy();
101
+ }
@@ -0,0 +1,36 @@
1
+ import type { Children, Refkey } from "@alloy-js/core";
2
+
3
+ /**
4
+ * Information for a Python function parameter.
5
+ */
6
+ export interface ParameterDescriptor {
7
+ /**
8
+ * The name of the parameter.
9
+ */
10
+ readonly name: string;
11
+
12
+ /**
13
+ * The type of the parameter.
14
+ */
15
+ readonly type?: Children;
16
+
17
+ /**
18
+ * The refkey for this parameter.
19
+ */
20
+ readonly refkey?: Refkey | Refkey[];
21
+
22
+ /**
23
+ * Whether the parameter is optional.
24
+ */
25
+ readonly optional?: boolean;
26
+
27
+ /**
28
+ * Documentation for the parameter.
29
+ */
30
+ readonly doc?: Children;
31
+
32
+ /**
33
+ * The default value of the parameter.
34
+ */
35
+ readonly default?: Children;
36
+ }
@@ -0,0 +1,36 @@
1
+ import { refkey, useContext } from "@alloy-js/core";
2
+ import { PythonSourceFileContext } from "./components/SourceFile.js";
3
+ import { PythonElements, usePythonNamePolicy } from "./name-policy.js";
4
+ import {
5
+ CreatePythonSymbolOptions,
6
+ PythonOutputSymbol,
7
+ } from "./symbols/index.js";
8
+
9
+ export function createPythonSymbol(
10
+ name: string,
11
+ options: CreatePythonSymbolOptions,
12
+ kind?: PythonElements,
13
+ createRefkeyIfNeeded = false,
14
+ ): PythonOutputSymbol {
15
+ let processedName = name;
16
+ const sfContext = useContext(PythonSourceFileContext);
17
+ // Only apply the name policy if a kind is provided and name policy context is available
18
+ if (kind) {
19
+ const namePolicy = usePythonNamePolicy();
20
+ if (namePolicy) {
21
+ processedName = namePolicy.getName(name, kind);
22
+ }
23
+ }
24
+
25
+ return new PythonOutputSymbol(processedName, {
26
+ binder: options.binder,
27
+ aliasTarget: options.aliasTarget,
28
+ scope: options.scope,
29
+ refkeys:
30
+ options.refkeys ??
31
+ (createRefkeyIfNeeded ? refkey(processedName) : undefined),
32
+ flags: options.flags,
33
+ metadata: options.metadata,
34
+ module: sfContext?.module,
35
+ });
36
+ }
@@ -0,0 +1,35 @@
1
+ import { OutputScope, OutputScopeOptions, SymbolTable } from "@alloy-js/core";
2
+
3
+ // This is named as CustomOutputScope and not PythonOutputScope to avoid confusion with the one defined in scopes.ts
4
+ // We are creating a custom output scope so we can add out custom name conflict resolution logic.
5
+ export class CustomOutputScope extends OutputScope {
6
+ #symbols: SymbolTable;
7
+ /**
8
+ * The symbols defined within this scope.
9
+ */
10
+ get symbols() {
11
+ return this.#symbols;
12
+ }
13
+
14
+ constructor(name: string, options: OutputScopeOptions = {}) {
15
+ super(name, options);
16
+ this.#symbols = new SymbolTable(this, {
17
+ nameConflictResolver: (_, symbols) => {
18
+ for (let i = 1; i < symbols.length; i++) {
19
+ // Rename all but the first symbol to have a suffix of _2, _3, plus the scope name if available.
20
+ const symbol = symbols[i] as unknown as {
21
+ originalName: string;
22
+ name: string;
23
+ module?: string;
24
+ };
25
+ symbol.name =
26
+ symbol.originalName +
27
+ "_" +
28
+ (i + 1) +
29
+ "_" +
30
+ (symbols[i].aliasTarget?.scope?.name ?? symbol.module ?? "");
31
+ }
32
+ },
33
+ });
34
+ }
35
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./custom-output-scope.js";
2
+ export * from "./python-member-scope.js";
3
+ export * from "./python-module-scope.js";
4
+ export * from "./python-output-symbol.js";
5
+ export * from "./reference.js";
6
+ export * from "./scopes.js";
@@ -0,0 +1,12 @@
1
+ import { CustomOutputScope } from "./custom-output-scope.js";
2
+ import { PythonOutputSymbol } from "./python-output-symbol.js";
3
+
4
+ export class PythonMemberScope extends CustomOutputScope {
5
+ get kind() {
6
+ return "member" as const;
7
+ }
8
+
9
+ get owner() {
10
+ return super.owner as PythonOutputSymbol;
11
+ }
12
+ }
@@ -0,0 +1,89 @@
1
+ import { reactive, shallowReactive } from "@alloy-js/core";
2
+ import { createPythonSymbol } from "../symbol-creation.js";
3
+ import { CustomOutputScope } from "./custom-output-scope.js";
4
+ import { PythonOutputSymbol } from "./python-output-symbol.js";
5
+
6
+ export class ImportedSymbol {
7
+ local: PythonOutputSymbol;
8
+ target: PythonOutputSymbol;
9
+
10
+ constructor(target: PythonOutputSymbol, local: PythonOutputSymbol) {
11
+ this.target = target;
12
+ this.local = local;
13
+ }
14
+
15
+ static from(target: PythonOutputSymbol, local: PythonOutputSymbol) {
16
+ return new ImportedSymbol(target, local);
17
+ }
18
+ }
19
+
20
+ export interface ImportRecordProps {
21
+ symbols: Set<ImportedSymbol>;
22
+ }
23
+
24
+ export type ImportRecords = Map<PythonModuleScope, ImportRecordProps>;
25
+
26
+ export const ImportRecords = Map as {
27
+ new (): ImportRecords;
28
+ new (
29
+ entries?:
30
+ | readonly (readonly [PythonModuleScope, ImportRecordProps])[]
31
+ | null,
32
+ ): ImportRecords;
33
+ prototype: Map<PythonModuleScope, ImportRecordProps>;
34
+ };
35
+
36
+ export class PythonModuleScope extends CustomOutputScope {
37
+ get kind() {
38
+ return "module" as const;
39
+ }
40
+
41
+ #importedSymbols: Map<PythonOutputSymbol, PythonOutputSymbol> =
42
+ shallowReactive(new Map());
43
+ get importedSymbols() {
44
+ return this.#importedSymbols;
45
+ }
46
+
47
+ #importedModules: ImportRecords = reactive(new Map());
48
+ get importedModules() {
49
+ return this.#importedModules;
50
+ }
51
+
52
+ addImport(targetSymbol: PythonOutputSymbol, targetModule: PythonModuleScope) {
53
+ const existing = this.importedSymbols.get(targetSymbol);
54
+ if (existing) {
55
+ return existing;
56
+ }
57
+
58
+ if (targetModule.kind !== "module") {
59
+ throw new Error("Cannot import symbol that isn't in module scope");
60
+ }
61
+
62
+ if (!this.importedModules.has(targetModule)) {
63
+ this.importedModules.set(targetModule, {
64
+ symbols: new Set<ImportedSymbol>(),
65
+ });
66
+ }
67
+
68
+ const localSymbol = createPythonSymbol(
69
+ targetSymbol.name,
70
+ {
71
+ binder: this.binder,
72
+ scope: this,
73
+ aliasTarget: targetSymbol,
74
+ },
75
+ undefined,
76
+ false,
77
+ );
78
+
79
+ targetSymbol.copyTo(localSymbol);
80
+
81
+ this.importedSymbols.set(targetSymbol, localSymbol);
82
+ this.importedModules.get(targetModule)!.symbols?.add({
83
+ local: localSymbol,
84
+ target: targetSymbol,
85
+ });
86
+
87
+ return localSymbol;
88
+ }
89
+ }
@@ -0,0 +1,36 @@
1
+ import { OutputSymbol, OutputSymbolOptions } from "@alloy-js/core";
2
+ import { PythonMemberScope } from "./python-member-scope.js";
3
+
4
+ export interface CreatePythonSymbolOptions extends OutputSymbolOptions {
5
+ module?: string;
6
+ }
7
+
8
+ export interface CreatePythonSymbolFunctionOptions
9
+ extends CreatePythonSymbolOptions {
10
+ name: string;
11
+ }
12
+
13
+ /**
14
+ * Represents an 'exported' symbol from a .py file. Class, enum, interface etc.
15
+ */
16
+ export class PythonOutputSymbol extends OutputSymbol {
17
+ constructor(name: string, options: CreatePythonSymbolOptions) {
18
+ super(name, options);
19
+ this.#module = options.module ?? undefined;
20
+ }
21
+
22
+ // The module in which the symbol is defined
23
+ #module?: string;
24
+
25
+ get module() {
26
+ return this.#module;
27
+ }
28
+
29
+ set module(value: string | undefined) {
30
+ this.#module = value;
31
+ }
32
+
33
+ get instanceMemberScope() {
34
+ return super.instanceMemberScope as PythonMemberScope | undefined;
35
+ }
36
+ }
@@ -0,0 +1,99 @@
1
+ import {
2
+ memo,
3
+ OutputSymbolFlags,
4
+ Refkey,
5
+ resolve,
6
+ untrack,
7
+ useContext,
8
+ useMemberScope,
9
+ } from "@alloy-js/core";
10
+ import { PythonSourceFileContext } from "../components/SourceFile.jsx";
11
+ import {
12
+ PythonMemberScope,
13
+ PythonModuleScope,
14
+ PythonOutputScope,
15
+ PythonOutputSymbol,
16
+ } from "./index.js";
17
+
18
+ export function ref(
19
+ refkey: Refkey,
20
+ ): () => [string, PythonOutputSymbol | undefined] {
21
+ const sourceFile = useContext(PythonSourceFileContext);
22
+ const resolveResult = resolve<PythonOutputScope, PythonOutputSymbol>(
23
+ refkey as Refkey,
24
+ );
25
+ const currentScope = useMemberScope();
26
+
27
+ return memo(() => {
28
+ if (resolveResult.value === undefined) {
29
+ return ["<Unresolved Symbol>", undefined];
30
+ }
31
+
32
+ const { targetDeclaration, pathDown, memberPath } = resolveResult.value;
33
+
34
+ // if we resolved a instance member, check if we should be able to access
35
+ // it.
36
+ if (targetDeclaration.flags & OutputSymbolFlags.InstanceMember) {
37
+ if (currentScope?.instanceMembers !== targetDeclaration.scope) {
38
+ throw new Error(
39
+ "Cannot resolve member symbols from a different member scope",
40
+ );
41
+ }
42
+ }
43
+
44
+ // Where the target declaration is relative to the referencing scope.
45
+ // * module: target symbol is in a different module
46
+ // * local: target symbol is within the current module
47
+ const targetLocation = pathDown[0]?.kind ?? "local";
48
+ let localSymbol: PythonOutputSymbol | undefined;
49
+
50
+ if (targetLocation === "module") {
51
+ // Handling of targets in other modules, either created with createModule()
52
+ // or from other files.
53
+ const symbolPath = [
54
+ ...(pathDown.slice(1) as PythonMemberScope[]).map((s) => s.owner),
55
+ targetDeclaration,
56
+ ];
57
+
58
+ const importSymbol = symbolPath[0];
59
+
60
+ localSymbol = untrack(() =>
61
+ sourceFile!.scope.addImport(
62
+ importSymbol,
63
+ pathDown[0] as PythonModuleScope,
64
+ ),
65
+ );
66
+ }
67
+
68
+ if (memberPath && memberPath.length > 0) {
69
+ if (localSymbol) {
70
+ memberPath[0] = localSymbol;
71
+ }
72
+
73
+ return [buildMemberExpression(memberPath), memberPath.at(-1)];
74
+ } else {
75
+ return [
76
+ buildMemberExpression([localSymbol ?? targetDeclaration]),
77
+ localSymbol ?? targetDeclaration,
78
+ ];
79
+ }
80
+ });
81
+ }
82
+
83
+ function buildMemberExpression(path: PythonOutputSymbol[]) {
84
+ let memberExpr = "";
85
+
86
+ const base: PythonOutputSymbol = path[0];
87
+ if (base.flags & OutputSymbolFlags.InstanceMember) {
88
+ memberExpr += "self";
89
+ } else {
90
+ memberExpr += base.name;
91
+ path = path.slice(1);
92
+ }
93
+
94
+ for (const sym of path) {
95
+ memberExpr += `.${sym.name}`;
96
+ }
97
+
98
+ return memberExpr;
99
+ }
@@ -0,0 +1,9 @@
1
+ import { useScope } from "@alloy-js/core";
2
+ import { PythonMemberScope } from "./python-member-scope.js";
3
+ import { PythonModuleScope } from "./python-module-scope.js";
4
+
5
+ export type PythonOutputScope = PythonModuleScope | PythonMemberScope;
6
+
7
+ export function usePythonScope() {
8
+ return useScope() as PythonOutputScope;
9
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { defaultProps, splitProps } from "@alloy-js/core";
2
+ import { CallSignatureProps } from "./components/index.js";
3
+
4
+ /**
5
+ * Extract only the call signature props from a props object which extends
6
+ * `CallSignatureProps`. You can provide default values for the props.
7
+ */
8
+ export function getCallSignatureProps(
9
+ props: CallSignatureProps,
10
+ defaults?: Partial<CallSignatureProps>,
11
+ ) {
12
+ const [callSignatureProps] = splitProps(props, [
13
+ "parameters",
14
+ "typeParameters",
15
+ "args",
16
+ "kwargs",
17
+ "instanceFunction",
18
+ "classFunction",
19
+ "returnType",
20
+ ]);
21
+
22
+ if (!defaults) {
23
+ return callSignatureProps;
24
+ }
25
+
26
+ return defaultProps(callSignatureProps, defaults);
27
+ }