It is difficult to see exactly how complex data structures change step-by-step when multiple updates happen at once. Tracking these changes manually becomes messy as the data gets deeper.
It looks at complex data and maps out the shortest path of changes by tracking specific events. It shows exactly how a piece of information evolves from one state to the next.
It provides a clear, step-by-step map of how data changes over time.
It was run in the sandbox and it failed. run output shows an error/traceback — the artifact does NOT run clean.
$ python3 diff_nested_trace.py
Traceback (most recent call last):
File "/work/diff_nested_trace.py", line 82, in <module>
main()
File "/work/diff_nested_trace.py", line 75, in main
minimal = d.minimal_path(trace)
^^^^^^^^^^^^^^^^^^^^^
File "/work/diff_nested_trace.py", line 47, in minimal_path
if not any(e['path'].startswith(p['path']) or p == event for p in path):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/work/diff_nested_trace.py", line 47, in <genexpr>
if not any(e['path'].startswith(p['path']) or p == event for p in path):
^
NameError: name 'e' is not definedNo screenshot — there is nothing working to show. This is recorded as an unfinished sketch so the attempt stays visible instead of being quietly dropped.
All of it — 89 lines, one file, standard library only.
# diff_nested_trace.py
import json
from typing import List, Dict, Any, Union
class DiffNestedTrace:
def __init__(self, old_data: Union[Dict, List], new_data: Union[Dict, List]) -> None:
self.old = old_data
self.new = new_data
self.diffs = []
def diff(self) -> List[Dict[str, Union[str, List[Union[str, int]]]]]:
self._diff(self.old, self.new, path=[]),
return self.diffs
def _diff(self, a, b, path: List[Union[str, int]]) -> None:
if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)):
for i in range(max(len(a), len(b))):
a_item = a[i] if i < len(a) else None
b_item = b[i] if i < len(b) else None
self._diff(a_item, b_item, path + [i])
elif isinstance(a, dict) and isinstance(b, dict):
all_keys = set(a.keys()) | set(b.keys())
for key in all_keys:
a_item = a.get(key)
b_item = b.get(key)
self._diff(a_item, b_item, path + [key])
else:
if a != b:
self.diffs.append({
'type': 'change' if a is not None and b is not None else 'add' if a is None else 'remove',
'path': path,
'old': a,
'new': b,
'path_string': self.path_to_string(path)
})
@staticmethod
def path_to_string(path: List[Union[str, int]]) -> str:
"""
Convert a list path into dot notation string
"""
return '.'.join(map(str, path))
def trace(self) -> List[Dict[str, Any]]:
"""
Convert diffs into a structured event trace
"""
return self.diffs
@staticmethod
def minimal_path(trace: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Calculate minimal path from event trace
"""
# Filter out redundant changes and sort by path length
path = []
for event in sorted(trace, key=lambda e: -len(e['path'])):
if not any(e['path'].startswith(p['path']) or p == event for p in path):
path.append(event)
return path
def main():
# Example usage
old_data = {
'user': {
'preferences': [
{'type': 'theme', 'value': 'light'},
{'type': 'notifications', 'value': True}
]
}
}
new_data = {
'user': {
'preferences': [
{'type': 'theme', 'value': 'dark'},
{'type': 'notifications', 'value': True},
{'type': 'layout', 'value': 'compact'}
]
}
}
d = DiffNestedTrace(old_data, new_data)
diff = d.diff()
trace = d.trace()
minimal = d.minimal_path(trace)
print("Diff:\n", json.dumps(diff, indent=2))
print("\nEvent Trace:\n", json.dumps(trace, indent=2))
print("\nMinimal Path:\n", json.dumps(minimal, indent=2))
if __name__ == '__main__':
main()