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
msra_codegen/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # msra-codegen
2
+
3
+ MSRA to async Python client generator.
4
+
5
+ ## Install
6
+
7
+ ```powershell
8
+ pip install msra-codegen
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```powershell
14
+ msra-codegen generate .\examples\example\example.msra -o .\generated
15
+ msra-codegen validate .\generated
16
+ ```
17
+
18
+ The module entrypoint stays available as well:
19
+
20
+ ```powershell
21
+ python -m msra_codegen generate .\examples\example\example.msra -o .\generated
22
+ python -m msra_codegen validate .\generated
23
+ ```
@@ -0,0 +1,6 @@
1
+ """MSRA to async Python client generator."""
2
+
3
+ from .package_writer import generate_project
4
+
5
+ __all__ = ["generate_project"]
6
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ from .cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main())
msra_codegen/bridge.py ADDED
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+
9
+ PACKAGE_ROOT = Path(__file__).resolve().parent
10
+ NODE_EXPORT = PACKAGE_ROOT / "node_export.js"
11
+
12
+
13
+ def load_msra_document(msra_path: Path) -> dict[str, Any]:
14
+ msra_path = msra_path.resolve()
15
+ if not msra_path.exists():
16
+ raise FileNotFoundError(msra_path)
17
+
18
+ process = subprocess.run(
19
+ ["node", str(NODE_EXPORT), str(msra_path)],
20
+ capture_output=True,
21
+ text=True,
22
+ encoding="utf-8",
23
+ errors="replace",
24
+ check=False,
25
+ )
26
+ if process.returncode != 0:
27
+ message = process.stderr.strip() or process.stdout.strip() or "MSRA parser failed"
28
+ raise RuntimeError(message)
29
+ return json.loads(process.stdout)
msra_codegen/cli.py ADDED
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import shutil
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ from .bridge import load_msra_document
9
+ from .msra_serializer import write_merged_msra_document
10
+ from .package_writer import generate_project
11
+ from .project_model import build_project
12
+ from .validation import validate_generated_project
13
+
14
+
15
+ def build_parser() -> argparse.ArgumentParser:
16
+ parser = argparse.ArgumentParser(
17
+ prog="msra-codegen",
18
+ description="Generate or validate an async Python client and Sphinx docs from an MSRA document.",
19
+ )
20
+ subparsers = parser.add_subparsers(dest="command", required=True)
21
+
22
+ generate_parser = subparsers.add_parser(
23
+ "generate",
24
+ help="Generate an async Python client and Sphinx docs from an MSRA document.",
25
+ )
26
+ generate_parser.add_argument("msra_file", type=Path, help="Path to the source .msra file")
27
+ generate_parser.add_argument(
28
+ "-o",
29
+ "--output",
30
+ type=Path,
31
+ default=Path("generated"),
32
+ help="Output directory for the generated project (default: ./generated)",
33
+ )
34
+ generate_parser.add_argument(
35
+ "--no-cleanup",
36
+ action="store_true",
37
+ help="Keep the existing output directory contents and preserve merged.msra after generation",
38
+ )
39
+ generate_parser.set_defaults(handler=handle_generate)
40
+
41
+ validate_parser = subparsers.add_parser(
42
+ "validate",
43
+ help="Validate a generated project with python syntax, ruff, and mypy.",
44
+ )
45
+ validate_parser.add_argument(
46
+ "output_dir",
47
+ type=Path,
48
+ help="Path to a generated project directory",
49
+ )
50
+ validate_parser.set_defaults(handler=handle_validate)
51
+ return parser
52
+
53
+
54
+ def main(argv: list[str] | None = None) -> int:
55
+ if hasattr(sys.stdout, "reconfigure"):
56
+ sys.stdout.reconfigure(encoding="utf-8")
57
+ parser = build_parser()
58
+ args = parser.parse_args(argv)
59
+ args.handler(args)
60
+ return 0
61
+
62
+
63
+ def handle_generate(args: argparse.Namespace) -> None:
64
+ ast = load_msra_document(args.msra_file)
65
+ output_root = args.output.resolve()
66
+ if not args.no_cleanup:
67
+ remove_output_tree(output_root)
68
+ merged_source_path = output_root / "merged.msra"
69
+ write_merged_msra_document(ast, merged_source_path)
70
+ project = build_project(ast, args.msra_file)
71
+ try:
72
+ generate_project(
73
+ project,
74
+ output_dir=args.output,
75
+ source_root=args.msra_file.resolve().parent,
76
+ )
77
+ finally:
78
+ if not args.no_cleanup and merged_source_path.exists():
79
+ merged_source_path.unlink()
80
+
81
+ message = f"Generated {project['app'].get('package_name', '')} into {output_root}"
82
+ if args.no_cleanup:
83
+ message += f" (merged source in {merged_source_path}, docs in {output_root / 'docs'})"
84
+ else:
85
+ message += f" (docs in {output_root / 'docs'}, merged source cleaned)"
86
+ print(message)
87
+
88
+
89
+ def handle_validate(args: argparse.Namespace) -> None:
90
+ output_root = args.output_dir.resolve()
91
+ validate_generated_project(output_root)
92
+ print(f"Validated generated project in {output_root}")
93
+
94
+
95
+ def remove_output_tree(output_root: Path) -> None:
96
+ if not output_root.exists():
97
+ return
98
+ if output_root.is_dir() and not output_root.is_symlink():
99
+ shutil.rmtree(output_root)
100
+ return
101
+ output_root.unlink()
102
+
103
+
104
+ if __name__ == "__main__":
105
+ raise SystemExit(main())