Transaction support

Transaction support is located in module gaphor.transaction:

>>> from gaphor import transaction

>>> import sys, logging
>>> transaction.log.addHandler(logging.StreamHandler(sys.stdout))

Do some basic initialization, so event emission will work. Since the transaction decorator does not know about the active user session (window), it emits it’s events via a global list of subscribers:

>>> from gaphor.core.eventmanager import EventManager
>>> event_manager = EventManager()
>>> transaction.subscribers.add(event_manager.handle)

The Transaction class is used mainly to signal the begin and end of a transaction. This is done by the TransactionBegin, TransactionCommit and TransactionRollback events:

>>> from gaphor.core import event_handler
>>> @event_handler(transaction.TransactionBegin)
... def transaction_begin_handler(event):
...     print('tx begin')
>>> event_manager.subscribe(transaction_begin_handler)

Same goes for commit and rollback events:

>>> @event_handler(transaction.TransactionCommit)
... def transaction_commit_handler(event):
...     print('tx commit')
>>> event_manager.subscribe(transaction_commit_handler)
>>> @event_handler(transaction.TransactionRollback)
... def transaction_rollback_handler(event):
...     print('tx rollback')
>>> event_manager.subscribe(transaction_rollback_handler)

A Transaction is started by initiating a Transaction instance:

>>> tx = transaction.Transaction(event_manager)
tx begin

On success, a transaction can be committed:

>>> tx.commit()
tx commit

After a commit, a rollback is no longer allowed (the transaction is closed):

>>> tx.rollback()
... 
Traceback (most recent call last):
...
gaphor.transaction.TransactionError: No Transaction on stack.

Transactions may be nested:

>>> tx = transaction.Transaction(event_manager)
tx begin
>>> tx2 = transaction.Transaction(event_manager)
>>> tx2.commit()
>>> tx.commit()
tx commit

Transactions should be closed in the right order (subtransactions first):

>>> tx = transaction.Transaction(event_manager)
tx begin
>>> tx2 = transaction.Transaction(event_manager)
>>> tx.commit()
... 
Traceback (most recent call last):
...
gaphor.transaction.TransactionError: Transaction on stack is not the transaction being closed.
>>> tx2.commit()
>>> tx.commit()
tx commit

The transactional decorator can be used to mark functions as transactional:

>>> @transaction.transactional
... def a():
...     print('do something')
>>> a()
tx begin
do something
tx commit

If an exception is raised from within the decorated function a rollback is performed:

>>> @transaction.transactional
... def a():
...     raise IndexError('bla')
>>> a() # doctest; +ELLIPSIS
Traceback (most recent call last):
...
IndexError: bla

>>> transaction.Transaction._stack
[]

Cleanup:

>>> transaction.subscribers.discard(event_manager.handle)