Undo Manager

Undo is a required feature in modern applications. Gaphor is no exception. Having an undo function in place means you can change the model and easily revert to an older state.

Overview of Transactions

The recording and playback of changes in Gaphor is handled by the the Undo Manager. The Undo Manager works transactionally. A transaction must succeed or fail as a complete unit. If the transaction fails in the middle, it is rolled back. In Gaphor this is achieved by the transaction module, which provides a context manager Transaction and a decorator called @transactional.

When transactions take place, they emit events when the top-most transaction begins and is finished. The event notifications are for the begin of the transaction, and the commit of the transaction if it is successful or the rollback of the transaction if it fails.

The Undo Manager only allows changes to be made in a transaction. If a change is made outside a transaction, an exception is raised.

Start of a Transaction

  1. A Transaction object is created.

  2. TransactionBegin event is emitted.

  3. The UndoManager instantiates a new ActionStack which is the transaction object, and adds the undo action to the stack.

Nested transactions are supported to allow a transaction to be added inside of another transaction that is already in progress.

Successful Transaction

  1. A TransactionCommit event is emitted

  2. The UndoManager closes and stores the transaction.

Failed Transaction

  1. A TransactionRollback event is emitted.

  2. The UndoManager plays back all the recorded actions, but does not store it.

Transaction API

Note

You only require transactions if the undo manager is active.

class gaphor.transaction.Transaction(event_manager, context=None)[source]

The transaction.

On start and end of a transaction an event is emitted. Transactions can be nested. Events are only emitted when the outermost transaction begins and finishes.

Note that transactions are a global construct.

>>> import gaphor.core.eventmanager
>>> event_manager = gaphor.core.eventmanager.EventManager()

Transactions can be nested. If the outermost transaction is committed or rolled back, an event is emitted.

It’s most convenient to use Transaction as a context manager:

>>> with Transaction(event_manager) as ctx:
...     ... # do actions
...     # in case the transaction should be rolled back:
...     ctx.rollback()

Events can be handled programmatically, although this is discouraged:

>>> tx = Transaction(event_manager)
>>> tx.commit()
commit()[source]

Commit the transaction.

The transaction is closed. A TransactionCommit event is emitted. If the transaction needs to be rolled back, a TransactionRollback event is emitted instead.

classmethod in_transaction() bool[source]

Are you running inside a transaction?

classmethod mark_rollback()[source]

Mark the transaction for rollback.

This operation itself will not close the transaction, instead it will allow you to elegantly revert changes.

rollback()[source]

Roll-back the transaction.

First, the transaction is closed. A TransactionRollback event is emitted.

gaphor.transaction.transactional(func)[source]

The transactional decorator makes a function transactional. Events are emitted through the (global) subscribers set.

>>> @transactional
... def foo():
...     pass

It is preferred to use the Transaction context manager. The context manager emits events in the context of the session in scope, whereas the @transactional decorator emits a global event which is sent to the active session.

class gaphor.event.TransactionBegin(context)[source]

This event denotes the beginning of a transaction.

Nested (sub-) transactions should not emit this signal.

class gaphor.event.TransactionCommit(context)[source]

This event is emitted when a transaction (toplevel) is successfully committed.

class gaphor.event.TransactionRollback(context)[source]

This event is emitted to tell the operation has failed.

If a set of operations fail (e.i. due to an exception) the transaction should be marked for rollback.

References