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,188 @@
|
|
|
1
|
+
from human_requests.abstraction import Output as _Output
|
|
2
|
+
|
|
3
|
+
{% if has_regexes %}
|
|
4
|
+
{% for regex in regexes %}
|
|
5
|
+
from .regexes import {{ regex.class_name }} as _{{ regex.class_name }}
|
|
6
|
+
{% endfor %}
|
|
7
|
+
{% endif %}
|
|
8
|
+
{% if external_modules %}
|
|
9
|
+
from collections.abc import Mapping
|
|
10
|
+
from dataclasses import fields, is_dataclass
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel
|
|
14
|
+
|
|
15
|
+
{% for import_line in external_import_lines %}
|
|
16
|
+
{{ import_line }}
|
|
17
|
+
{% endfor %}
|
|
18
|
+
{% endif %}
|
|
19
|
+
|
|
20
|
+
Output = _Output
|
|
21
|
+
{% if has_regexes %}
|
|
22
|
+
{% for regex in regexes %}
|
|
23
|
+
{{ regex.class_name }} = _{{ regex.class_name }}
|
|
24
|
+
{% endfor %}
|
|
25
|
+
{% endif %}
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"Output",
|
|
29
|
+
{% for regex in regexes %}
|
|
30
|
+
"{{ regex.class_name }}",
|
|
31
|
+
{% endfor %}
|
|
32
|
+
{% if external_modules %}
|
|
33
|
+
{% for module in external_modules %}
|
|
34
|
+
{% for class_name in module.public_class_names %}
|
|
35
|
+
"{{ class_name }}",
|
|
36
|
+
{% endfor %}
|
|
37
|
+
{% endfor %}
|
|
38
|
+
{% endif %}
|
|
39
|
+
]
|
|
40
|
+
{% if external_modules %}
|
|
41
|
+
|
|
42
|
+
SCALAR_TYPES = (str, int, float, bool, bytes, type(None))
|
|
43
|
+
CONTAINER_TYPES = (Mapping, list, tuple, set)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def normalize(value: Any) -> Any:
|
|
47
|
+
if isinstance(value, BaseModel):
|
|
48
|
+
return value.model_dump()
|
|
49
|
+
return value
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def is_leaf(value: Any) -> bool:
|
|
53
|
+
value = normalize(value)
|
|
54
|
+
return isinstance(value, SCALAR_TYPES + CONTAINER_TYPES)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def public_children_with_names(obj: Any) -> list[tuple[str, Any]]:
|
|
58
|
+
result: list[tuple[str, Any]] = []
|
|
59
|
+
|
|
60
|
+
if isinstance(obj, type):
|
|
61
|
+
for name, value in vars(obj).items():
|
|
62
|
+
if name.startswith("_"):
|
|
63
|
+
continue
|
|
64
|
+
if isinstance(value, (staticmethod, classmethod)):
|
|
65
|
+
continue
|
|
66
|
+
if callable(value) and not isinstance(value, type):
|
|
67
|
+
continue
|
|
68
|
+
result.append((name, value))
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
if isinstance(obj, BaseModel):
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
if is_dataclass(obj) and not isinstance(obj, type):
|
|
75
|
+
for field in fields(obj):
|
|
76
|
+
if field.name.startswith("_"):
|
|
77
|
+
continue
|
|
78
|
+
result.append((field.name, getattr(obj, field.name)))
|
|
79
|
+
|
|
80
|
+
if hasattr(obj, "__dict__"):
|
|
81
|
+
for name, value in vars(obj).items():
|
|
82
|
+
if name.startswith("_"):
|
|
83
|
+
continue
|
|
84
|
+
if callable(value):
|
|
85
|
+
continue
|
|
86
|
+
result.append((name, value))
|
|
87
|
+
|
|
88
|
+
for name, descriptor in vars(type(obj)).items():
|
|
89
|
+
if name.startswith("_"):
|
|
90
|
+
continue
|
|
91
|
+
if isinstance(descriptor, property):
|
|
92
|
+
result.append((name, getattr(obj, name)))
|
|
93
|
+
|
|
94
|
+
return result
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def registry_display_name(value: Any) -> str:
|
|
98
|
+
if isinstance(value, type):
|
|
99
|
+
return value.__qualname__.replace("<locals>.", "")
|
|
100
|
+
return type(value).__qualname__.replace("<locals>.", "")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def collect_allowed_value_paths(parent: Any) -> list[str]:
|
|
104
|
+
result: list[str] = []
|
|
105
|
+
seen: set[int] = set()
|
|
106
|
+
|
|
107
|
+
def walk(value: Any, path: list[str]) -> None:
|
|
108
|
+
value = normalize(value)
|
|
109
|
+
if is_leaf(value):
|
|
110
|
+
result.append(".".join(path))
|
|
111
|
+
return
|
|
112
|
+
value_id = id(value)
|
|
113
|
+
if value_id in seen:
|
|
114
|
+
return
|
|
115
|
+
seen.add(value_id)
|
|
116
|
+
children = public_children_with_names(value)
|
|
117
|
+
if not children:
|
|
118
|
+
result.append(".".join(path))
|
|
119
|
+
return
|
|
120
|
+
for child_name, child_value in children:
|
|
121
|
+
walk(child_value, [*path, child_name])
|
|
122
|
+
|
|
123
|
+
walk(parent, [registry_display_name(parent)])
|
|
124
|
+
return result
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def collect_allowed_values(parent: Any) -> list[Any]:
|
|
128
|
+
result: list[Any] = []
|
|
129
|
+
seen: set[int] = set()
|
|
130
|
+
|
|
131
|
+
def walk(value: Any) -> None:
|
|
132
|
+
value = normalize(value)
|
|
133
|
+
if is_leaf(value):
|
|
134
|
+
result.append(value)
|
|
135
|
+
return
|
|
136
|
+
value_id = id(value)
|
|
137
|
+
if value_id in seen:
|
|
138
|
+
return
|
|
139
|
+
seen.add(value_id)
|
|
140
|
+
for _, child_value in public_children_with_names(value):
|
|
141
|
+
walk(child_value)
|
|
142
|
+
|
|
143
|
+
walk(parent)
|
|
144
|
+
return result
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def is_allowed_value(value: Any, parent: Any) -> bool:
|
|
148
|
+
value = normalize(value)
|
|
149
|
+
return value in collect_allowed_values(parent)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def find_registry_path(parent: Any, target: Any) -> str | None:
|
|
153
|
+
seen: set[int] = set()
|
|
154
|
+
|
|
155
|
+
def walk(value: Any, path: list[str]) -> str | None:
|
|
156
|
+
if value is target:
|
|
157
|
+
return ".".join(path)
|
|
158
|
+
value = normalize(value)
|
|
159
|
+
if is_leaf(value):
|
|
160
|
+
return None
|
|
161
|
+
value_id = id(value)
|
|
162
|
+
if value_id in seen:
|
|
163
|
+
return None
|
|
164
|
+
seen.add(value_id)
|
|
165
|
+
for child_name, child_value in public_children_with_names(value):
|
|
166
|
+
found = walk(child_value, [*path, child_name])
|
|
167
|
+
if found is not None:
|
|
168
|
+
return found
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
return walk(parent, [registry_display_name(parent)])
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def validate_allowed_value(value: Any, parent: Any) -> Any:
|
|
175
|
+
value = normalize(value)
|
|
176
|
+
allowed = collect_allowed_values(parent)
|
|
177
|
+
if value in allowed:
|
|
178
|
+
return value
|
|
179
|
+
if not is_leaf(value):
|
|
180
|
+
value_path = find_registry_path(parent, value) or registry_display_name(value)
|
|
181
|
+
allowed_paths = collect_allowed_value_paths(parent)
|
|
182
|
+
raise ValueError(
|
|
183
|
+
f"{value_path} is a value registry, not a value.\n"
|
|
184
|
+
"Use one of:\n"
|
|
185
|
+
+ "\n".join(f"- {path}" for path in allowed_paths)
|
|
186
|
+
)
|
|
187
|
+
raise ValueError(f"Invalid value {value!r}. Allowed values: {allowed!r}")
|
|
188
|
+
{% endif %}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RegexBase:
|
|
6
|
+
REGEX = r""
|
|
7
|
+
ERROR: str | None = None
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def match(cls, value: Any) -> bool:
|
|
11
|
+
return re.fullmatch(cls.REGEX, str(value)) is not None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
{% for regex in regexes %}
|
|
15
|
+
class {{ regex.class_name }}(RegexBase):
|
|
16
|
+
{% if regex.description %}
|
|
17
|
+
"""{{ regex.description }}"""
|
|
18
|
+
{% endif %}
|
|
19
|
+
REGEX = r"{{ regex.pattern }}"
|
|
20
|
+
{% if regex.raise_message is not none %}
|
|
21
|
+
ERROR = {{ regex.raise_message }}
|
|
22
|
+
{% endif %}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
{% endfor %}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Minimal makefile for Sphinx documentation
|
|
2
|
+
#
|
|
3
|
+
|
|
4
|
+
# You can set these variables from the command line, and also
|
|
5
|
+
# from the environment for the first two.
|
|
6
|
+
SPHINXOPTS ?=
|
|
7
|
+
SPHINXBUILD ?= sphinx-build
|
|
8
|
+
SOURCEDIR = .
|
|
9
|
+
BUILDDIR = _build
|
|
10
|
+
|
|
11
|
+
# Put it first so that "make" without argument is like "make help".
|
|
12
|
+
help:
|
|
13
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
14
|
+
|
|
15
|
+
.PHONY: help Makefile
|
|
16
|
+
|
|
17
|
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
18
|
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
19
|
+
%: Makefile
|
|
20
|
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
HERE = Path(__file__).resolve().parent
|
|
8
|
+
REPO_ROOT = HERE.parents[1]
|
|
9
|
+
sys.path.insert(0, str(REPO_ROOT))
|
|
10
|
+
|
|
11
|
+
project = "{{ project_name }}"
|
|
12
|
+
author = "Miskler"
|
|
13
|
+
copyright = "{{ current_year }}, Miskler"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _get_version() -> str:
|
|
17
|
+
for key in ("PROJECT_VERSION", "READTHEDOCS_VERSION", "VERSION"):
|
|
18
|
+
if os.getenv(key):
|
|
19
|
+
return os.environ[key]
|
|
20
|
+
try:
|
|
21
|
+
from importlib.metadata import version
|
|
22
|
+
|
|
23
|
+
return version("{{ package_name }}")
|
|
24
|
+
except Exception:
|
|
25
|
+
return "{{ project_version }}"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
release = _get_version()
|
|
29
|
+
version = ".".join(release.split(".")[:3])
|
|
30
|
+
|
|
31
|
+
extensions = [
|
|
32
|
+
"sphinx.ext.autodoc",
|
|
33
|
+
"sphinx.ext.autosummary",
|
|
34
|
+
"sphinx.ext.napoleon",
|
|
35
|
+
"sphinx.ext.intersphinx",
|
|
36
|
+
"sphinx.ext.viewcode",
|
|
37
|
+
"sphinx.ext.doctest",
|
|
38
|
+
"sphinx.ext.duration",
|
|
39
|
+
"jsoncrack_for_sphinx",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
autodoc_mock_imports = [
|
|
43
|
+
"aiohttp",
|
|
44
|
+
"aiohttp_retry",
|
|
45
|
+
"camoufox",
|
|
46
|
+
"playwright",
|
|
47
|
+
"playwright.async_api",
|
|
48
|
+
"PIL",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
source_suffix = {
|
|
52
|
+
".rst": "restructuredtext",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
autosummary_generate = False
|
|
56
|
+
autosummary_imported_members = True
|
|
57
|
+
autosummary_ignore_module_all = False
|
|
58
|
+
|
|
59
|
+
autodoc_default_options = {}
|
|
60
|
+
autodoc_typehints = "signature"
|
|
61
|
+
autodoc_preserve_defaults = True
|
|
62
|
+
|
|
63
|
+
add_module_names = False
|
|
64
|
+
python_use_unqualified_type_names = True
|
|
65
|
+
multi_line_parameter_list = True
|
|
66
|
+
python_maximum_signature_line_length = 80
|
|
67
|
+
default_role = "any"
|
|
68
|
+
|
|
69
|
+
intersphinx_mapping = {
|
|
70
|
+
"python": ("https://docs.python.org/3", None),
|
|
71
|
+
"human_requests": ("https://miskler.github.io/human-requests/", None),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
json_schema_dir = str(HERE.parents[2] / "tests" / "__snapshots__")
|
|
75
|
+
|
|
76
|
+
html_theme = "furo"
|
|
77
|
+
html_static_path = ["_static"]
|
|
78
|
+
templates_path = ["_templates"]
|
|
79
|
+
|
|
80
|
+
html_theme_options = {
|
|
81
|
+
"sidebar_hide_name": True,
|
|
82
|
+
{% if logo %}
|
|
83
|
+
"light_logo": "{{ logo.light_name }}",
|
|
84
|
+
"dark_logo": "{{ logo.dark_name }}",
|
|
85
|
+
{% endif %}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{{ title }}
|
|
2
|
+
{{ title_underline }}
|
|
3
|
+
|
|
4
|
+
{% if description %}
|
|
5
|
+
{{ description }}
|
|
6
|
+
|
|
7
|
+
{% endif %}
|
|
8
|
+
.. currentmodule:: {{ import_path }}
|
|
9
|
+
|
|
10
|
+
.. automodule:: {{ import_path }}
|
|
11
|
+
|
|
12
|
+
{% if class_names %}
|
|
13
|
+
.. rubric:: Classes
|
|
14
|
+
|
|
15
|
+
{% for class_name in class_names %}
|
|
16
|
+
.. autoclass:: {{ class_name }}
|
|
17
|
+
:members:
|
|
18
|
+
:undoc-members:
|
|
19
|
+
:show-inheritance:
|
|
20
|
+
|
|
21
|
+
{% endfor %}
|
|
22
|
+
|
|
23
|
+
{% endif %}
|
|
24
|
+
{% if child_pages %}
|
|
25
|
+
.. rubric:: Submodules
|
|
26
|
+
|
|
27
|
+
.. toctree::
|
|
28
|
+
:maxdepth: 1
|
|
29
|
+
|
|
30
|
+
{% for child_docname in child_docnames %}
|
|
31
|
+
{{ child_docname }}
|
|
32
|
+
{% endfor %}
|
|
33
|
+
|
|
34
|
+
{% endif %}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{{ quick_start.title }}
|
|
2
|
+
{{ quick_start.title_underline }}
|
|
3
|
+
|
|
4
|
+
Install the generated client:
|
|
5
|
+
|
|
6
|
+
.. code-block:: console
|
|
7
|
+
|
|
8
|
+
pip install {{ package_name }}
|
|
9
|
+
{% if quick_start.requires_camoufox %}
|
|
10
|
+
python -m camoufox fetch
|
|
11
|
+
{% endif %}
|
|
12
|
+
|
|
13
|
+
Import the client:
|
|
14
|
+
|
|
15
|
+
.. code-block:: python
|
|
16
|
+
|
|
17
|
+
{{ pipeline_script_code_rst }}
|
|
18
|
+
|
|
19
|
+
The public API is documented in :doc:`api`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{% if top_groups %}
|
|
2
|
+
{% for group in top_groups %}
|
|
3
|
+
from . import {{ group.package_name }} as _{{ group.package_name }}
|
|
4
|
+
{% endfor %}
|
|
5
|
+
|
|
6
|
+
{% for group in top_groups %}
|
|
7
|
+
{{ group.class_name }} = _{{ group.package_name }}.{{ group.class_name }}
|
|
8
|
+
{% endfor %}
|
|
9
|
+
{% endif %}
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
{% for group in top_groups %}
|
|
13
|
+
"{{ group.class_name }}",
|
|
14
|
+
{% endfor %}
|
|
15
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{ pipeline_script_code }}
|