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
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def pascal_case(text: str) -> str:
|
|
8
|
+
parts = re.split(r"[^A-Za-z0-9]+|_", text)
|
|
9
|
+
cleaned = []
|
|
10
|
+
for part in parts:
|
|
11
|
+
if not part:
|
|
12
|
+
continue
|
|
13
|
+
cleaned.append(part[:1].upper() + part[1:].lower())
|
|
14
|
+
return "".join(cleaned) or "Generated"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def regex_class_name(name: str) -> str:
|
|
18
|
+
return f"Regex{pascal_case(name)}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def render_simple_value(value: Any) -> str:
|
|
22
|
+
if isinstance(value, str):
|
|
23
|
+
return repr(value)
|
|
24
|
+
if value is None:
|
|
25
|
+
return "None"
|
|
26
|
+
return repr(value)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def escape_regex_literal(text: str) -> str:
|
|
30
|
+
return text.replace("\\", "\\\\").replace('"', '\\"')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def render_request_referrer(headers_spec: dict[str, Any] | None) -> str | None:
|
|
34
|
+
if not headers_spec or headers_spec.get("referrer") is None:
|
|
35
|
+
return None
|
|
36
|
+
return render_ref_value(headers_spec.get("referrer"), self_ref="self")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def render_request_cors_mode(headers_spec: dict[str, Any] | None, *, default_if_missing: bool) -> str | None:
|
|
40
|
+
if not headers_spec or headers_spec.get("cors_mode") is None:
|
|
41
|
+
return render_simple_value("cors") if default_if_missing else None
|
|
42
|
+
return render_simple_value(get_plain_value(headers_spec.get("cors_mode")))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def render_request_credentials(headers_spec: dict[str, Any] | None, *, default_if_missing: bool) -> str | None:
|
|
46
|
+
if not headers_spec or headers_spec.get("credentials") is None:
|
|
47
|
+
return render_simple_value("include") if default_if_missing else None
|
|
48
|
+
return render_simple_value(get_plain_value(headers_spec.get("credentials")))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def render_request_headers(headers_spec: dict[str, Any] | None, *, default_if_missing: bool) -> str | None:
|
|
52
|
+
base = '{"Accept": "application/json, text/plain, */*"}'
|
|
53
|
+
if not headers_spec or headers_spec.get("headers") is None:
|
|
54
|
+
return base if default_if_missing else None
|
|
55
|
+
return render_headers_expr(headers_spec.get("headers"))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def wait_source_expr(source: Any) -> str:
|
|
59
|
+
source_name = str(get_plain_value(source) or "request").strip().lower()
|
|
60
|
+
if source_name == "response":
|
|
61
|
+
return "WaitSource.RESPONSE"
|
|
62
|
+
return "WaitSource.REQUEST"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def header_names_from_wait_sniffer(step: dict[str, Any]) -> list[str]:
|
|
66
|
+
what = step.get("what")
|
|
67
|
+
if isinstance(what, dict) and what.get("kind") == "ref":
|
|
68
|
+
parts = [part["value"] for part in what.get("parts", []) if part.get("kind") == "name"]
|
|
69
|
+
if len(parts) >= 3:
|
|
70
|
+
return [parts[-1]]
|
|
71
|
+
return ["X-Key"]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def render_ref_value(expr: dict[str, Any] | None, self_ref: str = "self._parent") -> str:
|
|
75
|
+
if expr is None:
|
|
76
|
+
return "None"
|
|
77
|
+
if expr.get("kind") == "ref":
|
|
78
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
79
|
+
if not parts:
|
|
80
|
+
return "None"
|
|
81
|
+
root = parts[0]
|
|
82
|
+
if root == "DOCUMENT" and len(parts) >= 3 and parts[1] == "PREFIXES":
|
|
83
|
+
return f"{self_ref}._{parts[2]}"
|
|
84
|
+
if root == "DOCUMENT" and len(parts) >= 3 and parts[1] == "REGEXES":
|
|
85
|
+
return f"abstraction.{regex_class_name(parts[2])}.REGEX"
|
|
86
|
+
if root == "VARIABLES" and len(parts) >= 2:
|
|
87
|
+
return f"{self_ref}.{parts[1]}"
|
|
88
|
+
if root == "INPUT" and len(parts) >= 2:
|
|
89
|
+
return ".".join(parts[1:])
|
|
90
|
+
if root == "ABSTRACTIONS":
|
|
91
|
+
tail = ".".join(parts[1:])
|
|
92
|
+
return "abstraction" if not tail else f"abstraction.{tail}"
|
|
93
|
+
if root == "UNSTANDARD_HEADERS":
|
|
94
|
+
if len(parts) == 1:
|
|
95
|
+
return f"{self_ref}.unstandard_headers"
|
|
96
|
+
if len(parts) >= 3 and parts[1] == "REQUEST":
|
|
97
|
+
return f"{self_ref}.unstandard_headers.get({render_simple_value(parts[2])})"
|
|
98
|
+
return f"{self_ref}.unstandard_headers"
|
|
99
|
+
return render_expr(expr, self_ref=self_ref)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def render_expr(expr: dict[str, Any] | None, self_ref: str = "self._parent") -> str:
|
|
103
|
+
if expr is None:
|
|
104
|
+
return "None"
|
|
105
|
+
if not isinstance(expr, dict):
|
|
106
|
+
return render_simple_value(expr)
|
|
107
|
+
kind = expr.get("kind")
|
|
108
|
+
if kind == "string":
|
|
109
|
+
return render_simple_value(expr.get("value"))
|
|
110
|
+
if kind == "number":
|
|
111
|
+
return repr(expr.get("value"))
|
|
112
|
+
if kind == "bool":
|
|
113
|
+
return "True" if expr.get("value") else "False"
|
|
114
|
+
if kind == "null":
|
|
115
|
+
return "None"
|
|
116
|
+
if kind == "ref":
|
|
117
|
+
return render_ref_value(expr, self_ref=self_ref)
|
|
118
|
+
if kind == "array":
|
|
119
|
+
return "[" + ", ".join(render_expr(item, self_ref=self_ref) for item in expr.get("items", [])) + "]"
|
|
120
|
+
if kind == "inline_table":
|
|
121
|
+
items = ", ".join(
|
|
122
|
+
f"{render_simple_value(item['key'])}: {render_expr(item['value'], self_ref=self_ref)}"
|
|
123
|
+
for item in expr.get("items", [])
|
|
124
|
+
)
|
|
125
|
+
return "{" + items + "}"
|
|
126
|
+
if kind == "sequence":
|
|
127
|
+
return " + ".join(render_text_expr(item, self_ref=self_ref) for item in expr.get("items", []))
|
|
128
|
+
if kind == "merge":
|
|
129
|
+
parts = expr.get("parts", [])
|
|
130
|
+
inline = next((part for part in parts if part.get("kind") == "inline_table"), None)
|
|
131
|
+
if inline is not None:
|
|
132
|
+
other_parts = [part for part in parts if part is not inline]
|
|
133
|
+
rendered = [render_expr(inline, self_ref=self_ref)] + [render_text_expr(part, self_ref=self_ref) for part in other_parts]
|
|
134
|
+
return " | ".join(rendered)
|
|
135
|
+
return " + ".join(render_text_expr(item, self_ref=self_ref) for item in parts)
|
|
136
|
+
if kind == "call":
|
|
137
|
+
callee = render_expr(expr.get("callee"), self_ref=self_ref)
|
|
138
|
+
args = ", ".join(f"{arg['name']}={render_expr(arg['value'], self_ref=self_ref)}" for arg in expr.get("args", []))
|
|
139
|
+
return f"{callee}({args})"
|
|
140
|
+
if kind == "index":
|
|
141
|
+
return f"{render_expr(expr.get('value'), self_ref=self_ref)}[{render_expr(expr.get('index'), self_ref=self_ref)}]"
|
|
142
|
+
return "None"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def render_text_expr(expr: dict[str, Any] | None, self_ref: str = "self._parent") -> str:
|
|
146
|
+
if expr is None:
|
|
147
|
+
return "None"
|
|
148
|
+
if not isinstance(expr, dict):
|
|
149
|
+
return render_simple_value(expr)
|
|
150
|
+
kind = expr.get("kind")
|
|
151
|
+
if kind == "ref":
|
|
152
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
153
|
+
if not parts:
|
|
154
|
+
return "None"
|
|
155
|
+
root = parts[0]
|
|
156
|
+
if root == "DOCUMENT" and len(parts) >= 3 and parts[1] == "PREFIXES":
|
|
157
|
+
return f"str({self_ref}._{parts[2]})"
|
|
158
|
+
if root == "DOCUMENT" and len(parts) >= 3 and parts[1] == "REGEXES":
|
|
159
|
+
return f"abstraction.{regex_class_name(parts[2])}.REGEX"
|
|
160
|
+
if root == "VARIABLES" and len(parts) >= 2:
|
|
161
|
+
return f"str({self_ref}.{parts[1]})"
|
|
162
|
+
if root == "INPUT" and len(parts) >= 2:
|
|
163
|
+
return f"str({'.'.join(parts[1:])})"
|
|
164
|
+
if root == "ABSTRACTIONS":
|
|
165
|
+
tail = ".".join(parts[1:])
|
|
166
|
+
return f"str(abstraction{('.' + tail) if tail else ''})"
|
|
167
|
+
if root == "UNSTANDARD_HEADERS":
|
|
168
|
+
if len(parts) == 1:
|
|
169
|
+
return f"str({self_ref}.unstandard_headers)"
|
|
170
|
+
if len(parts) >= 3 and parts[1] == "REQUEST":
|
|
171
|
+
return f"str({self_ref}.unstandard_headers.get({render_simple_value(parts[2])}))"
|
|
172
|
+
return f"str({self_ref}.unstandard_headers)"
|
|
173
|
+
if kind in {"string", "number", "bool", "null"}:
|
|
174
|
+
return render_expr(expr, self_ref=self_ref)
|
|
175
|
+
return render_expr(expr, self_ref=self_ref)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def render_headers_expr(expr: dict[str, Any] | None) -> str:
|
|
179
|
+
if expr is None:
|
|
180
|
+
return "None"
|
|
181
|
+
if not isinstance(expr, dict):
|
|
182
|
+
return render_simple_value(expr)
|
|
183
|
+
kind = expr.get("kind")
|
|
184
|
+
if kind == "merge":
|
|
185
|
+
return " | ".join(render_headers_expr(part) for part in expr.get("parts", []))
|
|
186
|
+
if kind == "inline_table":
|
|
187
|
+
return "{" + ", ".join(
|
|
188
|
+
f"{render_simple_value(item['key'])}: {render_headers_value(item['value'])}"
|
|
189
|
+
for item in expr.get("items", [])
|
|
190
|
+
) + "}"
|
|
191
|
+
if kind == "ref":
|
|
192
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
193
|
+
if parts and parts[0] == "UNSTANDARD_HEADERS":
|
|
194
|
+
if len(parts) >= 3 and parts[1] == "REQUEST":
|
|
195
|
+
return f"self.unstandard_headers.get({render_simple_value(parts[2])})"
|
|
196
|
+
return "self.unstandard_headers"
|
|
197
|
+
return render_headers_value(expr)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def render_headers_value(expr: dict[str, Any] | None) -> str:
|
|
201
|
+
if expr is None:
|
|
202
|
+
return "None"
|
|
203
|
+
if not isinstance(expr, dict):
|
|
204
|
+
return render_simple_value(expr)
|
|
205
|
+
kind = expr.get("kind")
|
|
206
|
+
if kind == "ref":
|
|
207
|
+
parts = [part["value"] for part in expr.get("parts", []) if part.get("kind") == "name"]
|
|
208
|
+
if parts and parts[0] == "UNSTANDARD_HEADERS":
|
|
209
|
+
if len(parts) >= 3 and parts[1] == "REQUEST":
|
|
210
|
+
return f"str(self.unstandard_headers.get({render_simple_value(parts[2])}))"
|
|
211
|
+
return "str(self.unstandard_headers)"
|
|
212
|
+
return render_text_expr(expr, self_ref="self")
|
|
213
|
+
if kind in {"string", "number", "bool", "null"}:
|
|
214
|
+
return render_text_expr(expr, self_ref="self")
|
|
215
|
+
return render_text_expr(expr, self_ref="self")
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def get_plain_value(expr: dict[str, Any] | None) -> Any:
|
|
219
|
+
if expr is None:
|
|
220
|
+
return None
|
|
221
|
+
if not isinstance(expr, dict):
|
|
222
|
+
return expr
|
|
223
|
+
kind = expr.get("kind")
|
|
224
|
+
if kind == "string":
|
|
225
|
+
return expr.get("value")
|
|
226
|
+
if kind == "number":
|
|
227
|
+
return expr.get("value")
|
|
228
|
+
if kind == "bool":
|
|
229
|
+
return expr.get("value")
|
|
230
|
+
if kind == "null":
|
|
231
|
+
return None
|
|
232
|
+
if kind == "ref":
|
|
233
|
+
return expr
|
|
234
|
+
if kind == "array":
|
|
235
|
+
return [get_plain_value(item) for item in expr.get("items", [])]
|
|
236
|
+
if kind == "inline_table":
|
|
237
|
+
return {item["key"]: get_plain_value(item["value"]) for item in expr.get("items", [])}
|
|
238
|
+
if kind == "sequence":
|
|
239
|
+
return "".join(str(get_plain_value(item)) for item in expr.get("items", []))
|
|
240
|
+
if kind == "merge":
|
|
241
|
+
return "".join(str(get_plain_value(item)) for item in expr.get("parts", []))
|
|
242
|
+
return expr
|