Standalone CLI that diffs two engine-surface snapshots across all four axes,
the foundation the FastAPI/DB layer will sit on.
- ams.snapshot : typed access to a snapshot.json
- ams.diff : keyed set-diff per axis (added/removed/changed) + cross-owner
method-move detection; types keyed by (script_name,
via_module_iface) so the dual MULTIARRAY stays stable;
filter_by_owner for per-class focus
- ams.render : human-readable report (+/-/~), owner-grouped
- ams.cli : python -m ams OLD NEW [--owner C] [--only ...] [--json]
6 tests pass, incl. an integration test over the committed golden pair
(asserts BlooMoo adds GRBUFFER/INTERNET, MOUSE grows 104->128, Animo gains
GETFPS, Animo script fields unchanged).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
59 lines
1.5 KiB
Python
59 lines
1.5 KiB
Python
"""Loading and light typed access to an engine-surface snapshot.json."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
|
|
@dataclass
|
|
class Snapshot:
|
|
"""Thin wrapper over a parsed snapshot.json. Axes are returned as plain lists of dicts so
|
|
they round-trip cleanly to JSON; the diff engine works directly on those records."""
|
|
|
|
raw: dict[str, Any]
|
|
|
|
@classmethod
|
|
def load(cls, path: str) -> "Snapshot":
|
|
with open(path, "r", encoding="utf-8") as fh:
|
|
return cls(json.load(fh))
|
|
|
|
@property
|
|
def binary(self) -> dict[str, Any]:
|
|
return self.raw.get("binary", {})
|
|
|
|
@property
|
|
def types(self) -> list[dict]:
|
|
return self.raw.get("types", [])
|
|
|
|
@property
|
|
def methods(self) -> list[dict]:
|
|
return self.raw.get("methods", [])
|
|
|
|
@property
|
|
def events(self) -> list[dict]:
|
|
return self.raw.get("events", [])
|
|
|
|
@property
|
|
def fields(self) -> list[dict]:
|
|
return self.raw.get("fields", [])
|
|
|
|
@property
|
|
def struct_layout(self) -> list[dict]:
|
|
return self.raw.get("struct_layout", [])
|
|
|
|
@property
|
|
def method_inheritance(self) -> list[dict]:
|
|
return self.raw.get("method_inheritance", [])
|
|
|
|
@property
|
|
def field_inheritance(self) -> list[dict]:
|
|
return self.raw.get("field_inheritance", [])
|
|
|
|
@property
|
|
def label(self) -> str:
|
|
b = self.binary
|
|
return "{0} [{1}/{2}]".format(
|
|
b.get("name", "?"), b.get("engine", "?"), b.get("compiler", "?"))
|