Skip to content

Compare models

By default, handles compare by shared node-state identity, including across views. Optional logical comparison reads fields instead. Choose it per class and choose participation on each field declaration.

from yidl_undoable import UndoRedoManager, field, managed, undoable

@undoable(eq=True, order=True)
class Score:
    points: int = managed(default=0)
    label: str = field(default="", compare=False)

history = UndoRedoManager()
a = Score(points=1, label="A", transaction_manager=history)
b = Score(points=2, label="B", transaction_manager=history)
history.initialize(a, b)
assert a < b
with history.begin():
    a.points = 2
assert a == b
assert a != a.current  # Different generated facade classes.

eq=False, order=False are Undoable's defaults. order=True requires eq=True. Logical comparisons require the exact same facade class: ordinary/current/working are distinct, as are different model classes. Enabled facades are unhashable. Comparison can trigger the normal lazy, working-thaw or absence behavior; it is not an atomic snapshot of multiple field reads.

Eligible ordinary, const, lazy/static, binding and managed fields default to compare=True. Initvar/classvar/transient/local fields are excluded. compare=False belongs beside the field, not in a decorator mapping.

managed(write_compare="value") is a separate choice: it controls whether a managed setter can skip an equal write; "identity" skips only the same reference. During value-write detection, facade equality temporarily uses historical node-state identity, including equality invoked inside callbacks, to preserve the existing history contract. Ordering and hashing never change. For full edge cases and legacy compare="value"|"identity", see the reference.