"""generate_models_from_schema.py — schema model generation Mirrors schemas/generated/*.schema.json into src/quant_engine/models/generated as lightweight Python model descriptors. """ from __future__ import annotations import argparse import json from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parents[2] SCHEMA_DIR = ROOT / "schemas" / "generated" OUT_DIR = ROOT / "src" / "quant_engine" / "models" / "generated" def load_json(path: Path) -> dict[str, Any]: try: obj = json.loads(path.read_text(encoding="utf-8")) except Exception: return {} return obj if isinstance(obj, dict) else {} def to_module_name(path: Path) -> str: return path.stem.replace(".", "_").lower() def render_module(schema_path: Path, schema: dict[str, Any]) -> str: title = str(schema.get("title") or schema_path.stem) schema_id = str(schema.get("$id") or f"schema://{title}") schema_rel_path = str(schema_path.relative_to(ROOT)).replace("\\", "/") props = schema.get("properties") if isinstance(schema.get("properties"), dict) else {} required = schema.get("required") if isinstance(schema.get("required"), list) else [] prop_names = list(props.keys()) return ( '"""Auto-generated schema model descriptor."""\n' "from __future__ import annotations\n\n" "from dataclasses import dataclass\n" "import json\n" "from pathlib import Path\n" "from typing import Any\n\n" f"SCHEMA_TITLE = {title!r}\n" f"SCHEMA_ID = {schema_id!r}\n" f"SCHEMA_PATH = {schema_rel_path!r}\n" f"SCHEMA_PROPERTIES = {prop_names!r}\n" f"SCHEMA_REQUIRED = {required!r}\n\n" "@dataclass(frozen=True)\n" "class SchemaModel:\n" " title: str\n" " schema_id: str\n" " path: str\n" " properties: list[str]\n" " required: list[str]\n\n" "def load_schema() -> dict[str, Any]:\n" " return json.loads(Path(__file__).with_suffix('.schema.json').read_text(encoding='utf-8'))\n\n" "def describe() -> SchemaModel:\n" " return SchemaModel(\n" " title=SCHEMA_TITLE,\n" " schema_id=SCHEMA_ID,\n" " path=SCHEMA_PATH,\n" " properties=list(SCHEMA_PROPERTIES),\n" " required=list(SCHEMA_REQUIRED),\n" " )\n" ) def write_text(path: Path, text: str) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(text, encoding="utf-8") def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--schemas", default=str(SCHEMA_DIR)) parser.add_argument("--out", default=str(OUT_DIR)) parser.add_argument("--report", default=str(ROOT / "Temp" / "schema_model_generation_v1.json")) args = parser.parse_args() schema_dir = Path(args.schemas) out_dir = Path(args.out) schema_files = sorted(schema_dir.glob("*.schema.json")) modules = [] for schema_file in schema_files: schema = load_json(schema_file) module_name = to_module_name(schema_file) modules.append(module_name) write_text(out_dir / f"{module_name}.py", render_module(schema_file, schema)) write_text(out_dir / f"{module_name}.schema.json", json.dumps(schema, ensure_ascii=False, indent=2) + "\n") write_text(out_dir / "__init__.py", '"""Auto-generated schema model package."""\n') write_text( out_dir.parent / "__init__.py", '"""Auto-generated quant_engine.models package."""\n', ) write_text( out_dir.parent.parent / "__init__.py", '"""Canonical quant_engine package."""\n', ) write_text( out_dir.parent.parent.parent / "__init__.py", '"""Canonical src package."""\n', ) report = { "status": "OK", "schema_count": len(schema_files), "generated_module_count": len(modules), "package_root": "src/quant_engine/models/generated", "report_path": str(Path(args.report).relative_to(ROOT)), } write_text(Path(args.report), json.dumps(report, ensure_ascii=False, indent=2) + "\n") print(json.dumps(report, ensure_ascii=False, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())