# YAML/JSON 差异对比工具 import yaml def compute_diff(doc_a: dict | str, doc_b: dict | str) -> dict: """Compute diff between two dicts or YAML strings.""" if isinstance(doc_a, str): try: doc_a = yaml.safe_load(doc_a) or {} except yaml.YAMLError: doc_a = {} if isinstance(doc_b, str): try: doc_b = yaml.safe_load(doc_b) or {} except yaml.YAMLError: doc_b = {} added, removed, modified = _recursive_diff(doc_a, doc_b, "") return { "added": added, "removed": removed, "modified": modified, } def _recursive_diff(a: dict | list, b: dict | list, path: str) -> tuple[list, list, list]: added, removed, modified = [], [], [] if isinstance(a, dict) and isinstance(b, dict): all_keys = set(a.keys()) | set(b.keys()) for key in sorted(all_keys): new_path = f"{path}.{key}" if path else key if key not in a: added.append({"path": new_path, "value": b[key]}) elif key not in b: removed.append({"path": new_path, "value": a[key]}) elif a[key] != b[key]: if isinstance(a[key], (dict, list)) and isinstance(b[key], (dict, list)): a_sub, r_sub, m_sub = _recursive_diff(a[key], b[key], new_path) added.extend(a_sub) removed.extend(r_sub) modified.extend(m_sub) else: modified.append({ "path": new_path, "old_value": a[key], "new_value": b[key], }) elif isinstance(a, list) and isinstance(b, list): for i in range(max(len(a), len(b))): new_path = f"{path}[{i}]" if i >= len(a): added.append({"path": new_path, "value": b[i]}) elif i >= len(b): removed.append({"path": new_path, "value": a[i]}) elif a[i] != b[i]: if isinstance(a[i], (dict, list)) and isinstance(b[i], (dict, list)): a_sub, r_sub, m_sub = _recursive_diff(a[i], b[i], new_path) added.extend(a_sub) removed.extend(r_sub) modified.extend(m_sub) else: modified.append({ "path": new_path, "old_value": a[i], "new_value": b[i], }) return added, removed, modified