You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
67 lines
1.9 KiB
67 lines
1.9 KiB
"""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}
|
|
|