Atomic operations
ObjectStateRegistry.atomic groups related state mutations into one history
snapshot. Structural registration alone is not a user-edit boundary: registering
or unregistering an ObjectState does not independently create a snapshot.
The atomic block records only when its body requests or causes a material state
change.
Basic use
Create and register states before editing them, then perform the logical change inside an atomic block:
from dataclasses import dataclass
from objectstate import ObjectState, ObjectStateRegistry
@dataclass
class Settings:
threshold: float = 0.5
enabled: bool = True
state = ObjectState(Settings(), scope_id="settings")
ObjectStateRegistry.register(state)
with ObjectStateRegistry.atomic("update settings", scope_id="settings"):
state.update_parameter("threshold", 0.8)
state.update_parameter("enabled", False)
The optional scope_id identifies the semantic owner of the mutation. When it
is omitted, the first scoped snapshot request inside the outermost block becomes
the owner.
Nesting
Nested atomic blocks coalesce into the outermost block. The outer label and scope remain authoritative:
with ObjectStateRegistry.atomic("configure import", scope_id="settings"):
state.update_parameter("enabled", True)
with ObjectStateRegistry.atomic("inner detail"):
state.update_parameter("threshold", 0.65)
Use atomic_success when an operation must produce no history entry if its
body raises:
with ObjectStateRegistry.atomic_success("validated update", scope_id="settings"):
state.update_parameter("threshold", 0.7)
validate_settings(state.to_object())
If validate_settings raises, the success-only block restores its history
bookkeeping and does not record a partial operation snapshot. Application code
is still responsible for rolling back external side effects.
Guidelines
Use one descriptive label for one user-visible operation.
Keep I/O and network activity outside the atomic block when possible.
Pass the owning scope when it is already known.
Never manipulate the registry’s atomic depth or snapshot fields directly.
Serialize mutations onto one application thread; the registry is process-wide state, not a per-thread transaction manager.
See Undo / Redo for history navigation and serialization.