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.
- msra_codegen/README.md +23 -0
- msra_codegen/__init__.py +6 -0
- msra_codegen/__main__.py +5 -0
- msra_codegen/bridge.py +29 -0
- msra_codegen/cli.py +105 -0
- msra_codegen/codegen_context.py +1690 -0
- msra_codegen/config.toml +164 -0
- msra_codegen/core_naming.py +155 -0
- msra_codegen/docs_generator.py +346 -0
- msra_codegen/file_utils.py +8 -0
- msra_codegen/funcresult.py +156 -0
- msra_codegen/generator.py +6 -0
- msra_codegen/generator_config.py +35 -0
- msra_codegen/github_workflows.py +129 -0
- msra_codegen/gitignore.py +31 -0
- msra_codegen/issue_templates.py +100 -0
- msra_codegen/logo_assets.py +99 -0
- msra_codegen/msra_serializer.py +205 -0
- msra_codegen/node_export.js +296 -0
- msra_codegen/package_metadata.py +306 -0
- msra_codegen/package_writer.py +175 -0
- msra_codegen/project_model.py +490 -0
- msra_codegen/python_formatting.py +88 -0
- msra_codegen/python_render.py +242 -0
- msra_codegen/readme_pipeline.py +519 -0
- msra_codegen/requirements.txt +5 -0
- msra_codegen/template_engine.py +26 -0
- msra_codegen/templates/Makefile.tpl +44 -0
- msra_codegen/templates/README.md.tpl +55 -0
- msra_codegen/templates/abstraction/__init__.py.tpl +188 -0
- msra_codegen/templates/abstraction/regexes.py.tpl +25 -0
- msra_codegen/templates/docs/requirements.txt.tpl +3 -0
- msra_codegen/templates/docs/source/Makefile.tpl +20 -0
- msra_codegen/templates/docs/source/api.rst.tpl +9 -0
- msra_codegen/templates/docs/source/conf.py.tpl +88 -0
- msra_codegen/templates/docs/source/index.rst.tpl +14 -0
- msra_codegen/templates/docs/source/module.rst.tpl +34 -0
- msra_codegen/templates/docs/source/quick_start.rst.tpl +19 -0
- msra_codegen/templates/endpoints_init.py.tpl +15 -0
- msra_codegen/templates/example.py.tpl +1 -0
- msra_codegen/templates/function.py.tpl +364 -0
- msra_codegen/templates/github/issue_templates/bug_report.yml.tpl +55 -0
- msra_codegen/templates/github/issue_templates/config.yml.tpl +8 -0
- msra_codegen/templates/github/issue_templates/documentation_issue.yml.tpl +33 -0
- msra_codegen/templates/github/issue_templates/feature_request.yml.tpl +36 -0
- msra_codegen/templates/github/workflows/publish.yml.tpl +100 -0
- msra_codegen/templates/github/workflows/source-sync.yml.tpl +177 -0
- msra_codegen/templates/github/workflows/tests.yml.tpl +69 -0
- msra_codegen/templates/gitignore.tpl +3 -0
- msra_codegen/templates/group.py.tpl +56 -0
- msra_codegen/templates/group_init.py.tpl +14 -0
- msra_codegen/templates/init.py.tpl +4 -0
- msra_codegen/templates/licenses/GPL-3.0-or-later.txt.tpl +674 -0
- msra_codegen/templates/licenses/MIT.txt.tpl +21 -0
- msra_codegen/templates/manager.py.tpl +257 -0
- msra_codegen/templates/pyproject.toml.tpl +38 -0
- msra_codegen/templates/tests/api_test.py.tpl +49 -0
- msra_codegen/templates/tests/conftest.py.tpl +21 -0
- msra_codegen/templates/variable.py.tpl +54 -0
- msra_codegen/tests_generator.py +988 -0
- msra_codegen/typespec.py +275 -0
- msra_codegen/validation.py +118 -0
- msra_codegen-0.1.0.dist-info/METADATA +47 -0
- msra_codegen-0.1.0.dist-info/RECORD +68 -0
- msra_codegen-0.1.0.dist-info/WHEEL +5 -0
- msra_codegen-0.1.0.dist-info/entry_points.txt +2 -0
- msra_codegen-0.1.0.dist-info/licenses/LICENSE +674 -0
- msra_codegen-0.1.0.dist-info/top_level.txt +1 -0
msra_codegen/typespec.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .python_render import get_plain_value, regex_class_name, render_expr, render_simple_value
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def should_render_setter(variable: dict[str, Any]) -> bool:
|
|
10
|
+
if variable["name"] == "token":
|
|
11
|
+
return False
|
|
12
|
+
return not bool(variable.get("read_only", False))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def variable_type_names(variable: dict[str, Any]) -> set[str]:
|
|
16
|
+
types = variable.get("types")
|
|
17
|
+
if isinstance(types, dict):
|
|
18
|
+
kind = types.get("kind")
|
|
19
|
+
if kind == "array":
|
|
20
|
+
result = set()
|
|
21
|
+
for item in types.get("items", []):
|
|
22
|
+
if isinstance(item, dict) and item.get("kind") == "inline_table":
|
|
23
|
+
item_dict = inline_table_to_dict(item)
|
|
24
|
+
type_name = item_dict.get("type")
|
|
25
|
+
if type_name:
|
|
26
|
+
result.add(str(type_name))
|
|
27
|
+
return result
|
|
28
|
+
if kind == "inline_table":
|
|
29
|
+
item_dict = inline_table_to_dict(types)
|
|
30
|
+
type_name = item_dict.get("type")
|
|
31
|
+
return {str(type_name)} if type_name else set()
|
|
32
|
+
type_name = types.get("type")
|
|
33
|
+
return {str(type_name)} if type_name else set()
|
|
34
|
+
if isinstance(types, list):
|
|
35
|
+
result = set()
|
|
36
|
+
for item in types:
|
|
37
|
+
if isinstance(item, dict):
|
|
38
|
+
type_name = item.get("type")
|
|
39
|
+
if type_name:
|
|
40
|
+
result.add(str(type_name))
|
|
41
|
+
return result
|
|
42
|
+
if isinstance(types, str):
|
|
43
|
+
return {types}
|
|
44
|
+
return set()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def extract_variable_match(types_expr: dict[str, Any] | None) -> dict[str, Any] | None:
|
|
48
|
+
if not isinstance(types_expr, dict) or types_expr.get("kind") != "array":
|
|
49
|
+
return None
|
|
50
|
+
for item in types_expr.get("items", []):
|
|
51
|
+
if not isinstance(item, dict) or item.get("kind") != "inline_table":
|
|
52
|
+
continue
|
|
53
|
+
for inline_item in item.get("items", []):
|
|
54
|
+
if inline_item.get("key") == "match":
|
|
55
|
+
return inline_item.get("value")
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def type_annotation_from_types(type_names: set[str], *, nullable: bool = False) -> str:
|
|
60
|
+
if not type_names:
|
|
61
|
+
return "Any | None" if nullable else "Any"
|
|
62
|
+
non_null = {name for name in type_names if name != "null"}
|
|
63
|
+
base = {
|
|
64
|
+
"string": "str",
|
|
65
|
+
"integer": "int",
|
|
66
|
+
"boolean": "bool",
|
|
67
|
+
"number": "float",
|
|
68
|
+
"array": "list[Any]",
|
|
69
|
+
"object": "dict[str, Any]",
|
|
70
|
+
}
|
|
71
|
+
if not non_null:
|
|
72
|
+
return "Any | None"
|
|
73
|
+
annotation = base.get(next(iter(non_null)), "Any")
|
|
74
|
+
if nullable or "null" in type_names:
|
|
75
|
+
return f"{annotation} | None"
|
|
76
|
+
return annotation
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def type_annotation_from_expr(expr: dict[str, Any] | None) -> str:
|
|
80
|
+
if is_list_type_expr(expr):
|
|
81
|
+
item_expr = list_item_type_expr(expr)
|
|
82
|
+
return f"list[{type_annotation_from_expr(item_expr)}]"
|
|
83
|
+
if is_abstraction_reference_expr(expr):
|
|
84
|
+
return "Any"
|
|
85
|
+
type_names = type_names_from_expr(expr)
|
|
86
|
+
return type_annotation_from_types(type_names)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def primary_type_name(type_names: set[str]) -> str | None:
|
|
90
|
+
for candidate in ("integer", "number", "boolean", "string", "array", "object", "null"):
|
|
91
|
+
if candidate in type_names:
|
|
92
|
+
return candidate
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def inline_array_to_list(expr: dict[str, Any] | None) -> list[dict[str, Any]]:
|
|
97
|
+
if not expr or expr.get("kind") != "array":
|
|
98
|
+
return []
|
|
99
|
+
result: list[dict[str, Any]] = []
|
|
100
|
+
for item in expr.get("items", []):
|
|
101
|
+
if item and item.get("kind") == "inline_table":
|
|
102
|
+
result.append(inline_table_to_dict(item))
|
|
103
|
+
return result
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def inline_table_to_dict(expr: dict[str, Any]) -> dict[str, Any]:
|
|
107
|
+
result: dict[str, Any] = {"kind": expr.get("kind")}
|
|
108
|
+
for item in expr.get("items", []):
|
|
109
|
+
result[item["key"]] = get_plain_value(item["value"])
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def escape_docstring(text: str) -> str:
|
|
114
|
+
return text.replace('"""', '\\"\\"\\"')
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def variable_match_pattern(variable: dict[str, Any]) -> str | None:
|
|
118
|
+
match = variable.get("match")
|
|
119
|
+
return match_to_pattern(match)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def match_to_values(expr: dict[str, Any] | None) -> list[Any] | None:
|
|
123
|
+
if not expr or expr.get("kind") != "array":
|
|
124
|
+
return None
|
|
125
|
+
values: list[Any] = []
|
|
126
|
+
for item in expr.get("items", []):
|
|
127
|
+
value, ok = scalar_value_from_expr(item)
|
|
128
|
+
if not ok:
|
|
129
|
+
return None
|
|
130
|
+
values.append(value)
|
|
131
|
+
return values
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def match_to_pattern(expr: dict[str, Any] | None) -> str | None:
|
|
135
|
+
if not expr:
|
|
136
|
+
return None
|
|
137
|
+
if expr.get("kind") == "string":
|
|
138
|
+
return render_simple_value(expr.get("value"))
|
|
139
|
+
if expr.get("kind") == "ref":
|
|
140
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
141
|
+
if len(parts) >= 3 and parts[0] == "DOCUMENT" and parts[1] == "REGEXES":
|
|
142
|
+
return f"abstraction.{regex_class_name(parts[2])}.REGEX"
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def match_to_error(expr: dict[str, Any] | None) -> str | None:
|
|
147
|
+
if not expr or expr.get("kind") != "ref":
|
|
148
|
+
return None
|
|
149
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
150
|
+
if len(parts) >= 3 and parts[0] == "DOCUMENT" and parts[1] == "REGEXES":
|
|
151
|
+
return f"abstraction.{regex_class_name(parts[2])}.ERROR"
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def match_to_check_expr(expr: dict[str, Any] | None, value_expr: str) -> str | None:
|
|
156
|
+
if not expr:
|
|
157
|
+
return None
|
|
158
|
+
if expr.get("kind") == "string":
|
|
159
|
+
pattern = render_simple_value(expr.get("value"))
|
|
160
|
+
return f"re.fullmatch({pattern}, str({value_expr})) is not None"
|
|
161
|
+
if expr.get("kind") != "ref":
|
|
162
|
+
return None
|
|
163
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
164
|
+
if len(parts) >= 3 and parts[0] == "DOCUMENT" and parts[1] == "REGEXES":
|
|
165
|
+
return f"abstraction.{regex_class_name(parts[2])}.match({value_expr})"
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def match_to_range(expr: dict[str, Any] | None) -> tuple[int | float | None, int | float | None] | None:
|
|
170
|
+
if not expr or expr.get("kind") != "inline_table":
|
|
171
|
+
return None
|
|
172
|
+
values = inline_table_to_dict(expr)
|
|
173
|
+
has_lower = "from" in values
|
|
174
|
+
has_upper = "to" in values
|
|
175
|
+
if not has_lower and not has_upper:
|
|
176
|
+
return None
|
|
177
|
+
lower = values.get("from")
|
|
178
|
+
upper = values.get("to")
|
|
179
|
+
if has_lower and (isinstance(lower, bool) or not isinstance(lower, (int, float))):
|
|
180
|
+
return None
|
|
181
|
+
if has_upper and (isinstance(upper, bool) or not isinstance(upper, (int, float))):
|
|
182
|
+
return None
|
|
183
|
+
return (lower if has_lower else None, upper if has_upper else None)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def scalar_value_from_expr(expr: dict[str, Any] | None) -> tuple[Any, bool]:
|
|
187
|
+
if not isinstance(expr, dict):
|
|
188
|
+
return None, False
|
|
189
|
+
kind = expr.get("kind")
|
|
190
|
+
if kind == "string":
|
|
191
|
+
return expr.get("value"), True
|
|
192
|
+
if kind == "number":
|
|
193
|
+
return expr.get("value"), True
|
|
194
|
+
if kind == "bool":
|
|
195
|
+
return bool(expr.get("value")), True
|
|
196
|
+
if kind == "null":
|
|
197
|
+
return None, True
|
|
198
|
+
return None, False
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def normalize_name(text: str) -> str:
|
|
202
|
+
return re.sub(r"[^a-z0-9]+", "", text.lower())
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def type_names_from_expr(expr: dict[str, Any] | None) -> set[str]:
|
|
206
|
+
if is_list_type_expr(expr):
|
|
207
|
+
item_expr = list_item_type_expr(expr)
|
|
208
|
+
return type_names_from_expr(item_expr)
|
|
209
|
+
plain = get_plain_value(expr)
|
|
210
|
+
if isinstance(plain, str):
|
|
211
|
+
return {plain}
|
|
212
|
+
if isinstance(plain, list):
|
|
213
|
+
result = set()
|
|
214
|
+
for item in plain:
|
|
215
|
+
if isinstance(item, dict) and item.get("type"):
|
|
216
|
+
result.add(str(item["type"]))
|
|
217
|
+
return result
|
|
218
|
+
if isinstance(plain, dict) and plain.get("type"):
|
|
219
|
+
return {str(plain["type"])}
|
|
220
|
+
return set()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def is_list_type_expr(expr: dict[str, Any] | None) -> bool:
|
|
224
|
+
if not isinstance(expr, dict) or expr.get("kind") != "sequence":
|
|
225
|
+
return False
|
|
226
|
+
items = expr.get("items", [])
|
|
227
|
+
if len(items) != 2:
|
|
228
|
+
return False
|
|
229
|
+
if get_plain_value(items[0]) != "list":
|
|
230
|
+
return False
|
|
231
|
+
return isinstance(items[1], dict) and items[1].get("kind") == "array"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def list_item_type_expr(expr: dict[str, Any] | None) -> dict[str, Any] | None:
|
|
235
|
+
if not is_list_type_expr(expr):
|
|
236
|
+
return None
|
|
237
|
+
items = expr.get("items", [])
|
|
238
|
+
if len(items) != 2:
|
|
239
|
+
return None
|
|
240
|
+
inner = items[1]
|
|
241
|
+
if not isinstance(inner, dict):
|
|
242
|
+
return None
|
|
243
|
+
inner_items = inner.get("items", [])
|
|
244
|
+
if not inner_items:
|
|
245
|
+
return None
|
|
246
|
+
first = inner_items[0]
|
|
247
|
+
return first if isinstance(first, dict) else None
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def ref_input_name(expr: dict[str, Any] | None) -> str | None:
|
|
251
|
+
if not isinstance(expr, dict) or expr.get("kind") != "ref":
|
|
252
|
+
return None
|
|
253
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
254
|
+
if len(parts) >= 2 and parts[0] == "INPUT":
|
|
255
|
+
return parts[1]
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def is_abstraction_reference_expr(expr: dict[str, Any] | None) -> bool:
|
|
260
|
+
if not isinstance(expr, dict) or expr.get("kind") != "ref":
|
|
261
|
+
return False
|
|
262
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
263
|
+
return len(parts) >= 2 and parts[0] == "ABSTRACTIONS"
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def selectable_values_from_plain_values(values: Any) -> list[Any]:
|
|
267
|
+
if not isinstance(values, list):
|
|
268
|
+
return []
|
|
269
|
+
if all(not isinstance(item, dict) for item in values):
|
|
270
|
+
return list(values)
|
|
271
|
+
return [
|
|
272
|
+
item["value"]
|
|
273
|
+
for item in values
|
|
274
|
+
if isinstance(item, dict) and item.get("value") is not None
|
|
275
|
+
]
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import tomllib
|
|
9
|
+
|
|
10
|
+
from .generator_config import config_section
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate_generated_project(output_root: Path) -> None:
|
|
14
|
+
output_root = output_root.resolve()
|
|
15
|
+
package_name = read_generated_package_name(output_root)
|
|
16
|
+
validation_config = config_section("validation")
|
|
17
|
+
checks = validation_config.get("checks", [])
|
|
18
|
+
if not isinstance(checks, list) or not checks:
|
|
19
|
+
raise RuntimeError("msra_codegen/config.toml must define validation.checks.")
|
|
20
|
+
|
|
21
|
+
context = {
|
|
22
|
+
"output_root": str(output_root),
|
|
23
|
+
"package_name": package_name,
|
|
24
|
+
"python_executable": sys.executable,
|
|
25
|
+
}
|
|
26
|
+
for raw_check in checks:
|
|
27
|
+
check = normalize_check(raw_check)
|
|
28
|
+
run_validation_check(output_root, check, context)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def read_generated_package_name(output_root: Path) -> str:
|
|
32
|
+
pyproject_path = output_root / "pyproject.toml"
|
|
33
|
+
if not pyproject_path.exists():
|
|
34
|
+
raise FileNotFoundError(pyproject_path)
|
|
35
|
+
|
|
36
|
+
with pyproject_path.open("rb") as handle:
|
|
37
|
+
pyproject = tomllib.load(handle)
|
|
38
|
+
if not isinstance(pyproject, dict):
|
|
39
|
+
raise RuntimeError(f"Unexpected TOML structure in {pyproject_path}.")
|
|
40
|
+
|
|
41
|
+
project = pyproject.get("project")
|
|
42
|
+
if not isinstance(project, dict):
|
|
43
|
+
raise RuntimeError(f"Missing [project] table in {pyproject_path}.")
|
|
44
|
+
|
|
45
|
+
package_name = str(project.get("name", "")).strip()
|
|
46
|
+
if not package_name:
|
|
47
|
+
raise RuntimeError(f"Missing project.name in {pyproject_path}.")
|
|
48
|
+
return package_name
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def normalize_check(raw_check: Any) -> dict[str, list[str] | str]:
|
|
52
|
+
if not isinstance(raw_check, dict):
|
|
53
|
+
raise RuntimeError("validation.checks entries must be tables.")
|
|
54
|
+
|
|
55
|
+
name = str(raw_check.get("name", "")).strip()
|
|
56
|
+
if not name:
|
|
57
|
+
raise RuntimeError("validation.checks entries must define a non-empty name.")
|
|
58
|
+
|
|
59
|
+
argv = raw_check.get("argv", [])
|
|
60
|
+
if not isinstance(argv, list) or not argv:
|
|
61
|
+
raise RuntimeError(f"validation.checks.{name}.argv must be a non-empty list.")
|
|
62
|
+
|
|
63
|
+
targets = raw_check.get("targets", [])
|
|
64
|
+
if not isinstance(targets, list):
|
|
65
|
+
raise RuntimeError(f"validation.checks.{name}.targets must be a list.")
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
"name": name,
|
|
69
|
+
"argv": [str(part) for part in argv],
|
|
70
|
+
"targets": [str(target) for target in targets],
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def run_validation_check(
|
|
75
|
+
output_root: Path,
|
|
76
|
+
check: dict[str, list[str] | str],
|
|
77
|
+
context: dict[str, str],
|
|
78
|
+
) -> None:
|
|
79
|
+
name = str(check["name"])
|
|
80
|
+
argv = [expand_placeholder(part, context) for part in check["argv"]]
|
|
81
|
+
targets = [resolve_validation_target(target, output_root, context) for target in check["targets"]]
|
|
82
|
+
command = [*argv, *targets]
|
|
83
|
+
process = subprocess.run(
|
|
84
|
+
command,
|
|
85
|
+
cwd=output_root,
|
|
86
|
+
capture_output=True,
|
|
87
|
+
text=True,
|
|
88
|
+
encoding="utf-8",
|
|
89
|
+
errors="replace",
|
|
90
|
+
check=False,
|
|
91
|
+
)
|
|
92
|
+
if process.returncode == 0:
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
stderr = process.stderr.strip()
|
|
96
|
+
stdout = process.stdout.strip()
|
|
97
|
+
details = stderr or stdout or "validation command failed without output"
|
|
98
|
+
raise RuntimeError(
|
|
99
|
+
f"Validation check {name!r} failed with exit code {process.returncode}.\n"
|
|
100
|
+
f"Command: {' '.join(command)}\n"
|
|
101
|
+
f"{details}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def resolve_validation_target(target: str, output_root: Path, context: dict[str, str]) -> str:
|
|
106
|
+
rendered = expand_placeholder(target, context)
|
|
107
|
+
rendered_path = Path(rendered)
|
|
108
|
+
if rendered_path.is_absolute():
|
|
109
|
+
return str(rendered_path)
|
|
110
|
+
return rendered
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def expand_placeholder(value: str, context: dict[str, str]) -> str:
|
|
114
|
+
try:
|
|
115
|
+
return value.format(**context)
|
|
116
|
+
except KeyError as exc:
|
|
117
|
+
missing_key = exc.args[0]
|
|
118
|
+
raise RuntimeError(f"Unknown validation placeholder {{{missing_key}}} in {value!r}.") from exc
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: msra-codegen
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MSRA to async Python client generator
|
|
5
|
+
License-Expression: GPL-3.0-or-later
|
|
6
|
+
Project-URL: Homepage, https://github.com/Open-Inflation/engine-reverse-ide
|
|
7
|
+
Project-URL: Repository, https://github.com/Open-Inflation/engine-reverse-ide
|
|
8
|
+
Project-URL: Issues, https://github.com/Open-Inflation/engine-reverse-ide/issues
|
|
9
|
+
Keywords: msra,codegen,generator,python,api
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: jinja2>=3.1
|
|
19
|
+
Requires-Dist: packaging>=24.0
|
|
20
|
+
Requires-Dist: Pillow>=10.0
|
|
21
|
+
Requires-Dist: ruff>=0.6
|
|
22
|
+
Requires-Dist: mypy>=1.11
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# msra-codegen
|
|
26
|
+
|
|
27
|
+
MSRA to async Python client generator.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```powershell
|
|
32
|
+
pip install msra-codegen
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```powershell
|
|
38
|
+
msra-codegen generate .\examples\example\example.msra -o .\generated
|
|
39
|
+
msra-codegen validate .\generated
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The module entrypoint stays available as well:
|
|
43
|
+
|
|
44
|
+
```powershell
|
|
45
|
+
python -m msra_codegen generate .\examples\example\example.msra -o .\generated
|
|
46
|
+
python -m msra_codegen validate .\generated
|
|
47
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
msra_codegen/README.md,sha256=QZ7cgcMzZdmVH01Cor_1l_XaSmzMQ-zieLEL3ncuG8I,433
|
|
2
|
+
msra_codegen/__init__.py,sha256=TgrFRwIE1SARuKmA8-zwUPCrRPZRmVs0wDXDUaUl_2M,145
|
|
3
|
+
msra_codegen/__main__.py,sha256=PSQ4rpL0dG6f-qH4N7H-gD9igQkdHzH4yVZDcW8lfZo,80
|
|
4
|
+
msra_codegen/bridge.py,sha256=8tPvo1dFSBk4JvxTxO6jHNXTq7niEbHXOGarNoG7KMA,787
|
|
5
|
+
msra_codegen/cli.py,sha256=L2FbDrlf1n0FlnNN7OoQpCVVeMcyTkKn6sBa6sFO1FY,3414
|
|
6
|
+
msra_codegen/codegen_context.py,sha256=VpwV539GFoP17qxiPPyTxXpiCpCSAbbm9RqRndjdVpU,81135
|
|
7
|
+
msra_codegen/config.toml,sha256=_QifB5o0nUPqAykEQnM3TGyCd-UxaWbwx7WYISAit5k,4416
|
|
8
|
+
msra_codegen/core_naming.py,sha256=r3w4eMhrGibaeRZjXcuqXGdcF3a1S_yeXF4RywhgtMM,4880
|
|
9
|
+
msra_codegen/docs_generator.py,sha256=oA70yOXXjmaDGL6nJEccq_J9BE7te2pcCpsgjtDG4mA,12983
|
|
10
|
+
msra_codegen/file_utils.py,sha256=rOdyQodNfFZgv7wjyO-pxAp5lhHd1A3Tn86VmzTJp9M,211
|
|
11
|
+
msra_codegen/funcresult.py,sha256=cG9YZfja-o4PbT8B5KJTW9bjQgHxfZ6lxUvt8BujYko,5964
|
|
12
|
+
msra_codegen/generator.py,sha256=KOrDZKxG3SBs1_W0wTw-RUpWwFKnYSIMBnJQwQm8Y_4,171
|
|
13
|
+
msra_codegen/generator_config.py,sha256=1e4-VGn9ZlK4Fxd5hkKYRjHGBZ_eDczp27oNTW6pAWI,928
|
|
14
|
+
msra_codegen/github_workflows.py,sha256=PhoBiG8AlAMe9S2Ic4wDNMJDxZi7rYDxQwhUTRXpgL4,6860
|
|
15
|
+
msra_codegen/gitignore.py,sha256=ewGikQoiwHLYQZj-OcNfeASoHode55__YSAPcZhAh6w,1004
|
|
16
|
+
msra_codegen/issue_templates.py,sha256=Mk1lLciCRd6ZqvhnA2_hcrMHEKLNlPSjVu1rYeTbRHg,3458
|
|
17
|
+
msra_codegen/logo_assets.py,sha256=BtU5iuOU2FhdhLFH70QDuBxTwpbUBH1-y6e12sTfyRk,3642
|
|
18
|
+
msra_codegen/msra_serializer.py,sha256=eUSDFE2dw4QCtBqUSQkW0yLqEeZQoXDVn4ps76kFAmQ,7817
|
|
19
|
+
msra_codegen/node_export.js,sha256=WrKjmLU7N9QM-2BAHhnJztvKG66vAZirqyXqPdYMRaE,7519
|
|
20
|
+
msra_codegen/package_metadata.py,sha256=qU2ulFWUxXLJXLmk6uSksfcuCx9_BB3FrRDPMeBzk_s,11353
|
|
21
|
+
msra_codegen/package_writer.py,sha256=2dNfGA4GXLVrhnVOq8cpQVDP0AcaZZzqHBwdkRsaKhk,6950
|
|
22
|
+
msra_codegen/project_model.py,sha256=iJ1VtifcCloseMrr-0-ZprqD8t0ndQt6g6qiiLPiFP0,20088
|
|
23
|
+
msra_codegen/python_formatting.py,sha256=nz8_ek7Psx4Nrmf0pT8bXcsxMiaQhzhWcGqZ0HL9xJ0,2520
|
|
24
|
+
msra_codegen/python_render.py,sha256=QIRDyRidbp6XhpWuC7Yt9S-VyZzYDpmacwhwA7B6qEw,10031
|
|
25
|
+
msra_codegen/readme_pipeline.py,sha256=wstRsOoiiwr2DltlhWpsvDEvlIdy7jl0okaqgKWB3p8,20480
|
|
26
|
+
msra_codegen/requirements.txt,sha256=TGEOcSBPBq31lUJYl5s2mfYXDbUjWzIsZwjmpI3o-y8,62
|
|
27
|
+
msra_codegen/template_engine.py,sha256=E2saVQMAwfrRMGy6jA0u8PoyFLuijbOAJJvAbSGK_NI,749
|
|
28
|
+
msra_codegen/tests_generator.py,sha256=7FPHC_X9JnzNhiYF3R-C4kSAknWXpOBMhtv0n4cvGcE,38026
|
|
29
|
+
msra_codegen/typespec.py,sha256=YPnfNoaaNh9NKDy7G5vInhvEVxiYjbTizcLjKrSr2b0,9531
|
|
30
|
+
msra_codegen/validation.py,sha256=LPHWfpcAO8O7RPSZA69eGWRJHntGkD0SROSmSghU-8I,3897
|
|
31
|
+
msra_codegen/templates/Makefile.tpl,sha256=UaPHmXcX7iIU8gRZQdnZJb4YN42nog_xf42-CHFhsxk,1180
|
|
32
|
+
msra_codegen/templates/README.md.tpl,sha256=vERzjz7H57WRBIG2bz8c_FG9xg5rUlLzXLkmo_BNdJ0,1493
|
|
33
|
+
msra_codegen/templates/endpoints_init.py.tpl,sha256=2V8gqXGRS-HV3yG-Fo5ZGc6dUrLVAx-fkGn2r2u6gIA,349
|
|
34
|
+
msra_codegen/templates/example.py.tpl,sha256=_nfryVxniYskaAnzk8Dc20rzcuTS3D1hvejMyKf2ZE0,27
|
|
35
|
+
msra_codegen/templates/function.py.tpl,sha256=zh9DTbff7Kyg8GBLlWjoS35GC7cFJ7-YJSqkFUI6iD0,17040
|
|
36
|
+
msra_codegen/templates/gitignore.tpl,sha256=tbDU-LuUp6VTfKAZIxBECyw_-fqwLvtfKcldrAf0iXo,58
|
|
37
|
+
msra_codegen/templates/group.py.tpl,sha256=LOesecFGYzBf77STFejJ7Iunn6X1mOw4NexV338izWw,1307
|
|
38
|
+
msra_codegen/templates/group_init.py.tpl,sha256=zv3zYtvFlfWKZrT_X-TRO28QmWrnEJd3-xd343Xi0Ec,456
|
|
39
|
+
msra_codegen/templates/init.py.tpl,sha256=pQYEPc1dTWr0KPzxNX7e4CkTRBqxfrwdQfq-08fvfhs,114
|
|
40
|
+
msra_codegen/templates/manager.py.tpl,sha256=e2tfTqHLkC-zYWV0h7qW-hSBsBBB3VaCrb2XevWMxRE,8427
|
|
41
|
+
msra_codegen/templates/pyproject.toml.tpl,sha256=vntgHtJGycTlC-rVPA07Vhp0UEw1KH24a9tFq7D0KI0,979
|
|
42
|
+
msra_codegen/templates/variable.py.tpl,sha256=G9ZBoUl_9fCRhgaWJ_Vj9f5c6KnK-vHweCutpAxoEPY,2152
|
|
43
|
+
msra_codegen/templates/abstraction/__init__.py.tpl,sha256=2pjmpE5SS6yXTZ8qW90QHj02eLlqNhGmP8fDXC-DQm0,5327
|
|
44
|
+
msra_codegen/templates/abstraction/regexes.py.tpl,sha256=hEq-wdoaN4VbttxcDDlPEGwfO59yOCWBNB7074ssKy4,502
|
|
45
|
+
msra_codegen/templates/docs/requirements.txt.tpl,sha256=LisN4V35t7YXySkjwF3nFxwU9XiW29-mo2epckFASEE,33
|
|
46
|
+
msra_codegen/templates/docs/source/Makefile.tpl,sha256=i2WHuFlgfyAPEW4ssEP8NY4cOibDJrVjvzSEU8_Ggwc,634
|
|
47
|
+
msra_codegen/templates/docs/source/api.rst.tpl,sha256=aq671_12LJfwUdbzMc2VE2KVRKjscMhen6Dfu-XooDM,123
|
|
48
|
+
msra_codegen/templates/docs/source/conf.py.tpl,sha256=mHi4MuOViH-Qi8hQtOhbr3XwnutBbh7Fa0I6eMCGGxo,1992
|
|
49
|
+
msra_codegen/templates/docs/source/index.rst.tpl,sha256=l2QftkvlNdRquxK3g37YQpTtjnP72fTC_C_4xvKT6KQ,165
|
|
50
|
+
msra_codegen/templates/docs/source/module.rst.tpl,sha256=WXn8KvvywJnQl09qU8-mscXxJlccdz898TjqNxHf8Tw,517
|
|
51
|
+
msra_codegen/templates/docs/source/quick_start.rst.tpl,sha256=tJ1-yy7Kroqa2AUKgA08zPRvn023ZkJWhiP71V7RUHk,351
|
|
52
|
+
msra_codegen/templates/github/issue_templates/bug_report.yml.tpl,sha256=0rn2Qt6-VrYUwbyiWk2SE05kH9lumbeqLXxUFIQNcx4,1308
|
|
53
|
+
msra_codegen/templates/github/issue_templates/config.yml.tpl,sha256=kABVkxMluwWauCtF71BUcoF0YJ6xWzgxuUSipUbnYN0,203
|
|
54
|
+
msra_codegen/templates/github/issue_templates/documentation_issue.yml.tpl,sha256=XOqeptXf4sgx6HDCXhBeiPKfWM0Q7TzzSlgKwmDXfdE,766
|
|
55
|
+
msra_codegen/templates/github/issue_templates/feature_request.yml.tpl,sha256=34wXm6kC93T_E36S3V2hWnTX4bL-tz3SW9FMolM7Pfc,988
|
|
56
|
+
msra_codegen/templates/github/workflows/publish.yml.tpl,sha256=4BvhQ_q53xLn1HYEX-1DN5XM0JTyEk6i7SOof4fIoqA,2731
|
|
57
|
+
msra_codegen/templates/github/workflows/source-sync.yml.tpl,sha256=XrgqzfSSesCNU-8ELNMfc9iF2ooWd5FwQ9tOJg-fiv0,6548
|
|
58
|
+
msra_codegen/templates/github/workflows/tests.yml.tpl,sha256=zuJIZmmhY2EgK8e9XzlLg0Atgd6fSS4nu77FSMzpMts,1751
|
|
59
|
+
msra_codegen/templates/licenses/GPL-3.0-or-later.txt.tpl,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
60
|
+
msra_codegen/templates/licenses/MIT.txt.tpl,sha256=2tAP1iRu8_2JMv1tCn3BSV8mdUn7YJVxs1TeXZsLspw,1086
|
|
61
|
+
msra_codegen/templates/tests/api_test.py.tpl,sha256=2tLFajzKtDjRD_orAHRbEoph0mbKt_CpKY9hwIXSMrk,1077
|
|
62
|
+
msra_codegen/templates/tests/conftest.py.tpl,sha256=jsrA4Gl54CCVF9c1wUu5wFlROmM-nJvzsqoZbCVtZhg,451
|
|
63
|
+
msra_codegen-0.1.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
64
|
+
msra_codegen-0.1.0.dist-info/METADATA,sha256=r5KTJ1S_Sy0VOSdWYUqvVVktCUFNK3KOtYzxij2S7K0,1364
|
|
65
|
+
msra_codegen-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
66
|
+
msra_codegen-0.1.0.dist-info/entry_points.txt,sha256=qSDNB8315HFeLaYf9imJT-s5kyyoLWF2z1oDcvuz17w,55
|
|
67
|
+
msra_codegen-0.1.0.dist-info/top_level.txt,sha256=z8EcNhplJZTF_sruniZrAogi85X5zNywxKeeNSSW09U,13
|
|
68
|
+
msra_codegen-0.1.0.dist-info/RECORD,,
|