Skip to content

Edit collections

Keep stable committed values and expose an editable working form only during a transaction. thaw prepares that form; freeze converts it back for publication.

from yidl_undoable import UndoRedoManager, managed, undoable

@undoable
class ShoppingList:
    items: tuple[str, ...] = managed(default=(), thaw=list, freeze=tuple)

history = UndoRedoManager()
shopping = ShoppingList(transaction_manager=history)
history.initialize(shopping)
with history.begin():
    working_items = shopping.working.items
    working_items.append("bread")
    working_items.append("milk")
    assert shopping.current.items == ()
assert shopping.items == ("bread", "milk")
assert history.undo() and shopping.items == ()
assert history.redo() and shopping.items == ("bread", "milk")

A working read thaws once per edit; repeated reads return the installed working container. Reading it can mark a field touched even if you do not mutate it. Ordinary reads do not implicitly thaw. Replacing a whole managed value through an ordinary setter is also valid during a transaction.

Escaped working containers

working_items.append("too late")
assert shopping.items == ("bread", "milk")

Completion detaches the working container. It remains a Python object, but later changes to it do not edit committed history. Do not keep it as the application's current editable model.

Managed stable values include exact built-in scalars, recursive tuples and frozensets, supported immutable mappings, and Undoable node references. Arbitrary mutable/custom objects are not made stable merely by annotation. Supply a conversion into the supported stable surface; otherwise freezing raises FrozenValueError. Converters must be pure and separate working data from stable data. See initialization callbacks and nested objects.