Skip to content

Keep snapshots

A live handle follows history navigation. A snapshot instead retains committed managed data from one frame, even after new edits discard that frame's branch. It does not capture ordinary fields, caches, resources or application methods.

from yidl_undoable import UndoRedoManager, managed, undoable

@undoable
class Document:
    text: str = managed(default="zero")

history = UndoRedoManager()
document = Document(transaction_manager=history)
history.initialize(document)
with history.begin():
    document.text = "one"
pinned = history.snapshot()
old_document = pinned.node(document.node_id)
assert history.undo()
with history.begin():
    document.text = "another branch"
assert old_document.get("text") == "one"
assert document.text == "another branch" and not history.can_redo()

FrameSnapshot.roots contains data-only SnapshotNode handles. Use .node(node_id) to locate a member and .get(field_name) to read a managed field. Nested references become snapshot nodes and aliases retain identity within the same snapshot. Unknown/nonmanaged fields raise AttributeError; missing nodes raise ObjectAbsentError. There are no setters or application methods.

Snapshot node identity is scoped to the snapshot and node ID; it is unaffected by optional facade logical comparisons. Taking a snapshot during editing captures the selected committed frame, not pending values. Snapshots retain their epoch in memory, so keeping many snapshots can retain abandoned history. They are not a disk serialization format.