feat(mindmodel): add manifest loader and tests
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""Simple manifest loader for mindmodel manifests.
|
||||
|
||||
Provides `load_manifest(path: str) -> dict` and `ManifestLoadError`.
|
||||
|
||||
Behavior:
|
||||
- If PyYAML is installed, uses yaml.safe_load to parse the file.
|
||||
- Otherwise falls back to the stdlib json parser.
|
||||
- If the top-level document is a list it will be normalized to {"constraints": <list>}.
|
||||
- Raises ManifestLoadError for missing file or parse errors.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class ManifestLoadError(Exception):
|
||||
"""Raised when a manifest cannot be loaded or parsed."""
|
||||
|
||||
|
||||
try:
|
||||
import yaml # type: ignore
|
||||
except Exception: # YAML not available
|
||||
yaml = None # type: ignore
|
||||
|
||||
|
||||
def _parse_with_yaml(text: str) -> Any:
|
||||
# yamlsafe_load may return any Python structure
|
||||
try:
|
||||
return yaml.safe_load(text)
|
||||
except Exception as exc: # pragma: no cover - defensive
|
||||
raise ManifestLoadError(f"YAML parse error: {exc}") from exc
|
||||
|
||||
|
||||
def _parse_with_json(text: str) -> Any:
|
||||
try:
|
||||
return json.loads(text)
|
||||
except Exception as exc:
|
||||
raise ManifestLoadError(f"JSON parse error: {exc}") from exc
|
||||
|
||||
|
||||
def load_manifest(path: str) -> Dict[str, Any]:
|
||||
"""Load a manifest from the given file path and normalize it to a dict.
|
||||
|
||||
If the top-level document is a list, it will be returned as {"constraints": list}.
|
||||
Raises ManifestLoadError if the file does not exist or if parsing fails.
|
||||
"""
|
||||
p = Path(path)
|
||||
if not p.exists():
|
||||
raise ManifestLoadError(f"Manifest file not found: {path}")
|
||||
|
||||
text = p.read_text(encoding="utf-8")
|
||||
|
||||
if yaml is not None:
|
||||
data = _parse_with_yaml(text)
|
||||
else:
|
||||
data = _parse_with_json(text)
|
||||
|
||||
# Normalize
|
||||
if isinstance(data, list):
|
||||
return {"constraints": data}
|
||||
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
|
||||
# Unexpected top-level type, wrap it
|
||||
return {"manifest": data}
|
||||
Reference in New Issue
Block a user