msra-codegen 0.1.0__py3-none-any.whl

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 (68) hide show
  1. msra_codegen/README.md +23 -0
  2. msra_codegen/__init__.py +6 -0
  3. msra_codegen/__main__.py +5 -0
  4. msra_codegen/bridge.py +29 -0
  5. msra_codegen/cli.py +105 -0
  6. msra_codegen/codegen_context.py +1690 -0
  7. msra_codegen/config.toml +164 -0
  8. msra_codegen/core_naming.py +155 -0
  9. msra_codegen/docs_generator.py +346 -0
  10. msra_codegen/file_utils.py +8 -0
  11. msra_codegen/funcresult.py +156 -0
  12. msra_codegen/generator.py +6 -0
  13. msra_codegen/generator_config.py +35 -0
  14. msra_codegen/github_workflows.py +129 -0
  15. msra_codegen/gitignore.py +31 -0
  16. msra_codegen/issue_templates.py +100 -0
  17. msra_codegen/logo_assets.py +99 -0
  18. msra_codegen/msra_serializer.py +205 -0
  19. msra_codegen/node_export.js +296 -0
  20. msra_codegen/package_metadata.py +306 -0
  21. msra_codegen/package_writer.py +175 -0
  22. msra_codegen/project_model.py +490 -0
  23. msra_codegen/python_formatting.py +88 -0
  24. msra_codegen/python_render.py +242 -0
  25. msra_codegen/readme_pipeline.py +519 -0
  26. msra_codegen/requirements.txt +5 -0
  27. msra_codegen/template_engine.py +26 -0
  28. msra_codegen/templates/Makefile.tpl +44 -0
  29. msra_codegen/templates/README.md.tpl +55 -0
  30. msra_codegen/templates/abstraction/__init__.py.tpl +188 -0
  31. msra_codegen/templates/abstraction/regexes.py.tpl +25 -0
  32. msra_codegen/templates/docs/requirements.txt.tpl +3 -0
  33. msra_codegen/templates/docs/source/Makefile.tpl +20 -0
  34. msra_codegen/templates/docs/source/api.rst.tpl +9 -0
  35. msra_codegen/templates/docs/source/conf.py.tpl +88 -0
  36. msra_codegen/templates/docs/source/index.rst.tpl +14 -0
  37. msra_codegen/templates/docs/source/module.rst.tpl +34 -0
  38. msra_codegen/templates/docs/source/quick_start.rst.tpl +19 -0
  39. msra_codegen/templates/endpoints_init.py.tpl +15 -0
  40. msra_codegen/templates/example.py.tpl +1 -0
  41. msra_codegen/templates/function.py.tpl +364 -0
  42. msra_codegen/templates/github/issue_templates/bug_report.yml.tpl +55 -0
  43. msra_codegen/templates/github/issue_templates/config.yml.tpl +8 -0
  44. msra_codegen/templates/github/issue_templates/documentation_issue.yml.tpl +33 -0
  45. msra_codegen/templates/github/issue_templates/feature_request.yml.tpl +36 -0
  46. msra_codegen/templates/github/workflows/publish.yml.tpl +100 -0
  47. msra_codegen/templates/github/workflows/source-sync.yml.tpl +177 -0
  48. msra_codegen/templates/github/workflows/tests.yml.tpl +69 -0
  49. msra_codegen/templates/gitignore.tpl +3 -0
  50. msra_codegen/templates/group.py.tpl +56 -0
  51. msra_codegen/templates/group_init.py.tpl +14 -0
  52. msra_codegen/templates/init.py.tpl +4 -0
  53. msra_codegen/templates/licenses/GPL-3.0-or-later.txt.tpl +674 -0
  54. msra_codegen/templates/licenses/MIT.txt.tpl +21 -0
  55. msra_codegen/templates/manager.py.tpl +257 -0
  56. msra_codegen/templates/pyproject.toml.tpl +38 -0
  57. msra_codegen/templates/tests/api_test.py.tpl +49 -0
  58. msra_codegen/templates/tests/conftest.py.tpl +21 -0
  59. msra_codegen/templates/variable.py.tpl +54 -0
  60. msra_codegen/tests_generator.py +988 -0
  61. msra_codegen/typespec.py +275 -0
  62. msra_codegen/validation.py +118 -0
  63. msra_codegen-0.1.0.dist-info/METADATA +47 -0
  64. msra_codegen-0.1.0.dist-info/RECORD +68 -0
  65. msra_codegen-0.1.0.dist-info/WHEEL +5 -0
  66. msra_codegen-0.1.0.dist-info/entry_points.txt +2 -0
  67. msra_codegen-0.1.0.dist-info/licenses/LICENSE +674 -0
  68. msra_codegen-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,205 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+
9
+ IDENTIFIER_RE = re.compile(r"^[^\W\d][\w-]*$", re.UNICODE)
10
+
11
+
12
+ def render_merged_msra_document(ast: dict[str, Any]) -> str:
13
+ tables = sorted(
14
+ (table for table in ast.get("tables", []) if isinstance(table, dict)),
15
+ key=_table_sort_key,
16
+ )
17
+ blocks: list[str] = []
18
+ for table in tables:
19
+ # Serialize each transformed table as a standalone block in path order.
20
+ block_lines = [f"[{render_table_path(table)}]"]
21
+ assignments = table.get("assignments", [])
22
+ for assignment in assignments if isinstance(assignments, list) else []:
23
+ line = render_assignment(assignment)
24
+ if line:
25
+ block_lines.append(line)
26
+ blocks.append("\n".join(block_lines))
27
+ if not blocks:
28
+ return ""
29
+ return "\n\n".join(blocks) + "\n"
30
+
31
+
32
+ def write_merged_msra_document(ast: dict[str, Any], output_path: Path) -> None:
33
+ output_path = output_path.resolve()
34
+ output_path.parent.mkdir(parents=True, exist_ok=True)
35
+ output_path.write_text(render_merged_msra_document(ast), encoding="utf-8")
36
+
37
+
38
+ def _table_sort_key(table: dict[str, Any]) -> tuple[str, ...]:
39
+ path_segments = table.get("pathSegments")
40
+ if isinstance(path_segments, list) and path_segments:
41
+ return tuple(str(segment.get("value", "")) for segment in path_segments if isinstance(segment, dict))
42
+ path = table.get("path")
43
+ if isinstance(path, list):
44
+ return tuple(str(segment) for segment in path)
45
+ return tuple()
46
+
47
+
48
+ def render_table_path(table: dict[str, Any]) -> str:
49
+ path_segments = table.get("pathSegments")
50
+ if isinstance(path_segments, list) and path_segments:
51
+ return ".".join(render_path_segment(segment) for segment in path_segments if isinstance(segment, dict))
52
+ path = table.get("path") or []
53
+ return ".".join(render_path_segment({"value": segment, "quoted": False}) for segment in path)
54
+
55
+
56
+ def render_path_segment(segment: dict[str, Any]) -> str:
57
+ value = str(segment.get("value", ""))
58
+ quoted = bool(segment.get("quoted", False))
59
+ if quoted or not IDENTIFIER_RE.match(value):
60
+ return json.dumps(value, ensure_ascii=False)
61
+ return value
62
+
63
+
64
+ def render_assignment(assignment: dict[str, Any]) -> str:
65
+ if not isinstance(assignment, dict):
66
+ return ""
67
+ if assignment.get("annotation"):
68
+ annotation_name = str(assignment.get("annotationName") or assignment.get("key") or "")
69
+ annotation_args = assignment.get("annotationArgs")
70
+ if annotation_name.lower() == "docs" and isinstance(annotation_args, list):
71
+ rendered_args = ", ".join(
72
+ render_named_arg(arg) for arg in annotation_args if isinstance(arg, dict)
73
+ )
74
+ if assignment.get("annotationHasArguments"):
75
+ return f"@{annotation_name}({rendered_args})"
76
+ return f"@{annotation_name}"
77
+ if assignment.get("annotationHasArguments"):
78
+ value = assignment.get("value")
79
+ if isinstance(value, dict) and value.get("kind") == "bool" and value.get("value") is True:
80
+ return f"@{annotation_name}()"
81
+ return f"@{annotation_name}({render_expr(assignment.get('value'))})"
82
+ return f"@{annotation_name}"
83
+ key = render_assignment_key(str(assignment.get("key", "")), bool(assignment.get("quoted", False)))
84
+ return f"{key}={render_expr(assignment.get('value'))}"
85
+
86
+
87
+ def render_assignment_key(key: str, quoted: bool = False) -> str:
88
+ if quoted or not IDENTIFIER_RE.match(key):
89
+ return json.dumps(key, ensure_ascii=False)
90
+ return key
91
+
92
+
93
+ def render_expr(expr: Any) -> str:
94
+ if expr is None:
95
+ return "null"
96
+ if not isinstance(expr, dict):
97
+ if isinstance(expr, bool):
98
+ return "true" if expr else "false"
99
+ if isinstance(expr, (int, float)) and not isinstance(expr, bool):
100
+ return repr(expr)
101
+ if expr is None:
102
+ return "null"
103
+ return json.dumps(expr, ensure_ascii=False)
104
+
105
+ kind = expr.get("kind")
106
+ if kind == "string":
107
+ if not bool(expr.get("quoted", True)):
108
+ return str(expr.get("value", ""))
109
+ return json.dumps(expr.get("value", ""), ensure_ascii=False)
110
+ if kind == "number":
111
+ raw = expr.get("raw")
112
+ if isinstance(raw, str) and raw:
113
+ return raw
114
+ return repr(expr.get("value"))
115
+ if kind == "bool":
116
+ return "true" if expr.get("value") else "false"
117
+ if kind == "null":
118
+ return "null"
119
+ if kind in {"ident", "identifier"}:
120
+ return str(expr.get("value", ""))
121
+ if kind == "ref":
122
+ return render_ref(expr)
123
+ if kind == "array":
124
+ items = expr.get("items", [])
125
+ rendered = ", ".join(render_expr(item) for item in items if item is not None)
126
+ return f"[{rendered}]"
127
+ if kind == "inline_table":
128
+ items = expr.get("items", [])
129
+ rendered = ", ".join(
130
+ f"{render_assignment_key(str(item.get('key', '')), bool(item.get('quoted', False)))}={render_expr(item.get('value'))}"
131
+ for item in items
132
+ if isinstance(item, dict)
133
+ )
134
+ return f"{{{rendered}}}"
135
+ if kind == "sequence":
136
+ items = expr.get("items", [])
137
+ list_rendered = render_list_type_sequence(items)
138
+ if list_rendered is not None:
139
+ return list_rendered
140
+ return " + ".join(render_expr(item) for item in items if item is not None)
141
+ if kind == "merge":
142
+ parts = expr.get("parts", [])
143
+ return " + ".join(render_expr(part) for part in parts if part is not None)
144
+ if kind == "call":
145
+ callee = render_expr(expr.get("callee"))
146
+ args = ", ".join(render_named_arg(arg) for arg in expr.get("args", []) if isinstance(arg, dict))
147
+ return f"{callee}({args})"
148
+ if kind == "index":
149
+ return render_expr(expr.get("value"))
150
+ return "null"
151
+
152
+
153
+ def render_named_arg(arg: dict[str, Any]) -> str:
154
+ name = render_assignment_key(str(arg.get("name", "")))
155
+ return f"{name}={render_expr(arg.get('value'))}"
156
+
157
+
158
+ def render_list_type_sequence(items: list[Any]) -> str | None:
159
+ if len(items) != 2:
160
+ return None
161
+ prefix, suffix = items
162
+ if not is_bare_list_prefix(prefix):
163
+ return None
164
+ if not isinstance(suffix, dict) or suffix.get("kind") != "array":
165
+ return None
166
+ rendered_items = ", ".join(render_expr(item) for item in suffix.get("items", []) if item is not None)
167
+ return f"list[{rendered_items}]"
168
+
169
+
170
+ def is_bare_list_prefix(expr: Any) -> bool:
171
+ if not isinstance(expr, dict):
172
+ return False
173
+ kind = expr.get("kind")
174
+ if kind == "ident":
175
+ return str(expr.get("value", "")) == "list"
176
+ if kind == "string":
177
+ return not bool(expr.get("quoted", True)) and str(expr.get("value", "")) == "list"
178
+ return False
179
+
180
+
181
+ def render_ref(expr: dict[str, Any]) -> str:
182
+ parts = expr.get("parts", [])
183
+ rendered: list[str] = []
184
+ for index, part in enumerate(parts):
185
+ if not isinstance(part, dict):
186
+ continue
187
+ kind = part.get("kind")
188
+ if kind == "name":
189
+ segment = render_path_segment(part)
190
+ if index == 0:
191
+ rendered.append(segment)
192
+ else:
193
+ rendered.append(f".{segment}")
194
+ continue
195
+ if kind == "index":
196
+ rendered.append(f"[{render_expr(part.get('value'))}]")
197
+ continue
198
+ if kind == "key":
199
+ rendered.append(f"[@Key({render_expr(part.get('value'))})]")
200
+ continue
201
+ if kind == "call":
202
+ args = part.get("value", [])
203
+ rendered.append(f"({', '.join(render_named_arg(arg) for arg in args if isinstance(arg, dict))})")
204
+ continue
205
+ return f"<{''.join(rendered)}>"
@@ -0,0 +1,296 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const {
7
+ ArrayExpr,
8
+ BoolExpr,
9
+ CallExpr,
10
+ InlineEntry,
11
+ InlineTableExpr,
12
+ IdentExpr,
13
+ MergeExpr,
14
+ NamedArg,
15
+ NullExpr,
16
+ NumberExpr,
17
+ RefExpr,
18
+ RefSegment,
19
+ SequenceExpr,
20
+ StringExpr,
21
+ } = require(path.resolve(__dirname, "..", "vscode-extension", "lsp", "model"));
22
+ const { analyzeDocument } = require(path.resolve(__dirname, "..", "vscode-extension", "lsp", "analysis"));
23
+ const { loadProject } = require(path.resolve(__dirname, "..", "vscode-extension", "lsp", "project-loader"));
24
+
25
+ function serializePosition(position) {
26
+ if (!position) {
27
+ return null;
28
+ }
29
+ return {
30
+ line: position.line,
31
+ character: position.character,
32
+ };
33
+ }
34
+
35
+ function serializeRange(range) {
36
+ if (!range) {
37
+ return null;
38
+ }
39
+ return {
40
+ start: serializePosition(range.start),
41
+ end: serializePosition(range.end),
42
+ };
43
+ }
44
+
45
+ function serializeNamedArg(arg) {
46
+ return {
47
+ name: arg.name,
48
+ nameRange: serializeRange(arg.nameRange),
49
+ value: serializeExpr(arg.value),
50
+ };
51
+ }
52
+
53
+ function serializeRefSegment(segment) {
54
+ if (segment.kind === "name") {
55
+ return {
56
+ kind: segment.kind,
57
+ value: segment.value,
58
+ range: serializeRange(segment.range),
59
+ quoted: Boolean(segment.quoted),
60
+ };
61
+ }
62
+ if (segment.kind === "index") {
63
+ return {
64
+ kind: segment.kind,
65
+ value: serializeExpr(segment.value),
66
+ range: serializeRange(segment.range),
67
+ };
68
+ }
69
+ if (segment.kind === "key") {
70
+ return {
71
+ kind: segment.kind,
72
+ value: serializeExpr(segment.value),
73
+ range: serializeRange(segment.range),
74
+ };
75
+ }
76
+ if (segment.kind === "call") {
77
+ return {
78
+ kind: segment.kind,
79
+ value: segment.value.map(serializeNamedArg),
80
+ range: serializeRange(segment.range),
81
+ };
82
+ }
83
+ return {
84
+ kind: segment.kind,
85
+ value: segment.value,
86
+ range: serializeRange(segment.range),
87
+ };
88
+ }
89
+
90
+ function serializeInlineEntry(entry) {
91
+ return {
92
+ key: entry.key,
93
+ keyRange: serializeRange(entry.keyRange),
94
+ value: serializeExpr(entry.value),
95
+ quoted: Boolean(entry.quoted),
96
+ };
97
+ }
98
+
99
+ function serializeExpr(expr) {
100
+ if (expr === null || expr === undefined) {
101
+ return null;
102
+ }
103
+ if (expr instanceof StringExpr) {
104
+ return {
105
+ kind: "string",
106
+ value: expr.value,
107
+ raw: expr.raw,
108
+ quoted: Boolean(expr.quoted),
109
+ range: serializeRange(expr.range),
110
+ };
111
+ }
112
+ if (expr instanceof NumberExpr) {
113
+ return {
114
+ kind: "number",
115
+ value: expr.value,
116
+ raw: expr.raw,
117
+ range: serializeRange(expr.range),
118
+ };
119
+ }
120
+ if (expr instanceof BoolExpr) {
121
+ return {
122
+ kind: "bool",
123
+ value: expr.value,
124
+ range: serializeRange(expr.range),
125
+ };
126
+ }
127
+ if (expr instanceof NullExpr) {
128
+ return {
129
+ kind: "null",
130
+ range: serializeRange(expr.range),
131
+ };
132
+ }
133
+ if (expr instanceof IdentExpr) {
134
+ return {
135
+ kind: "ident",
136
+ value: expr.name,
137
+ range: serializeRange(expr.range),
138
+ };
139
+ }
140
+ if (expr instanceof RefExpr) {
141
+ return {
142
+ kind: "ref",
143
+ parts: expr.parts.map(serializeRefSegment),
144
+ range: serializeRange(expr.range),
145
+ };
146
+ }
147
+ if (expr instanceof SequenceExpr) {
148
+ return {
149
+ kind: "sequence",
150
+ items: expr.items.map(serializeExpr),
151
+ range: serializeRange(expr.range),
152
+ };
153
+ }
154
+ if (expr instanceof MergeExpr) {
155
+ return {
156
+ kind: "merge",
157
+ parts: expr.parts.map(serializeExpr),
158
+ range: serializeRange(expr.range),
159
+ };
160
+ }
161
+ if (expr instanceof ArrayExpr) {
162
+ return {
163
+ kind: "array",
164
+ items: expr.items.map(serializeExpr),
165
+ range: serializeRange(expr.range),
166
+ };
167
+ }
168
+ if (expr instanceof InlineTableExpr) {
169
+ return {
170
+ kind: "inline_table",
171
+ items: expr.items.map(serializeInlineEntry),
172
+ range: serializeRange(expr.range),
173
+ };
174
+ }
175
+ if (expr instanceof CallExpr) {
176
+ return {
177
+ kind: "call",
178
+ callee: serializeExpr(expr.callee),
179
+ args: expr.args.map(serializeNamedArg),
180
+ range: serializeRange(expr.range),
181
+ };
182
+ }
183
+ if (expr instanceof InlineEntry) {
184
+ return serializeInlineEntry(expr);
185
+ }
186
+ if (expr instanceof RefSegment) {
187
+ return serializeRefSegment(expr);
188
+ }
189
+ if (expr instanceof NamedArg) {
190
+ return serializeNamedArg(expr);
191
+ }
192
+ return {
193
+ kind: "unknown",
194
+ range: serializeRange(expr.range),
195
+ };
196
+ }
197
+
198
+ function serializeTable(table) {
199
+ return {
200
+ identityKey: table.identityKey || null,
201
+ path: table.path,
202
+ headerRange: serializeRange(table.headerRange),
203
+ isArray: Boolean(table.isArray),
204
+ pathSegments: (table.pathSegments || []).map((segment) => ({
205
+ value: segment.value,
206
+ quoted: Boolean(segment.quoted),
207
+ range: serializeRange(segment.range),
208
+ })),
209
+ assignments: (table.assignments || []).map(serializeAssignment),
210
+ };
211
+ }
212
+
213
+ function serializeAssignment(assignment) {
214
+ return {
215
+ tablePath: assignment.tablePath,
216
+ key: assignment.key,
217
+ keyRange: serializeRange(assignment.keyRange),
218
+ value: serializeExpr(assignment.value),
219
+ valueRange: serializeRange(assignment.valueRange),
220
+ fullPath: assignment.fullPath,
221
+ quoted: Boolean(assignment.quoted),
222
+ annotation: Boolean(assignment.annotation),
223
+ annotationName: assignment.annotationName || null,
224
+ annotationHasArguments: Boolean(assignment.annotationHasArguments),
225
+ annotationArgs: Array.isArray(assignment.annotationArgs) ? assignment.annotationArgs.map(serializeNamedArg) : [],
226
+ };
227
+ }
228
+
229
+ function serializeDiagnostic(diagnostic) {
230
+ return {
231
+ message: diagnostic.message,
232
+ severity: diagnostic.severity,
233
+ source: diagnostic.source,
234
+ code: diagnostic.code,
235
+ range: serializeRange(diagnostic.range),
236
+ };
237
+ }
238
+
239
+ function serializeReference(reference) {
240
+ return {
241
+ expr: serializeExpr(reference.expr),
242
+ range: serializeRange(reference.range),
243
+ tablePath: reference.tablePath,
244
+ resolvedPath: reference.resolvedPath,
245
+ resolvedKind: reference.resolvedKind,
246
+ valuePathSegments: reference.valuePathSegments,
247
+ };
248
+ }
249
+
250
+ function main() {
251
+ const inputPath = process.argv[2];
252
+ if (!inputPath) {
253
+ console.error("Usage: node node_export.js <file.msra>");
254
+ process.exit(1);
255
+ }
256
+ const project = loadProject(path.resolve(inputPath));
257
+ const document = {
258
+ uri: project.rootPath,
259
+ text: project.rootDocument ? project.rootDocument.transformed.text : "",
260
+ lineStarts: project.rootDocument ? project.rootDocument.transformed.lineStarts : [],
261
+ diagnostics: project.rootDocument ? project.rootDocument.transformed.diagnostics : [],
262
+ tables: project.mergedTables,
263
+ assignments: project.mergedAssignments,
264
+ references: project.mergedReferences,
265
+ directives: project.mergedDirectives,
266
+ errors: [],
267
+ };
268
+ const analysis = analyzeDocument(document);
269
+ if (analysis.diagnostics.length) {
270
+ console.error(JSON.stringify(analysis.diagnostics.map(serializeDiagnostic), null, 2));
271
+ process.exit(1);
272
+ }
273
+
274
+ const tables = [...project.mergedTables.values()].map(serializeTable);
275
+ const assignments = [...project.mergedAssignments.values()].map(serializeAssignment);
276
+ const references = (project.mergedReferences || []).map(serializeReference);
277
+
278
+ process.stdout.write(
279
+ JSON.stringify(
280
+ {
281
+ uri: project.rootPath,
282
+ text: document.text,
283
+ lineStarts: document.lineStarts,
284
+ tables,
285
+ assignments,
286
+ references,
287
+ directives: project.mergedDirectives || [],
288
+ rootPath: project.rootPath,
289
+ },
290
+ null,
291
+ 2,
292
+ ),
293
+ );
294
+ }
295
+
296
+ main();