Added module gaphor.transaction
git-svn-id: file:///Users/arjan/backup/gaphor/gaphor/trunk@1204 a8418922-720d-0410-834f-a69b97ada669
This commit is contained in:
parent
e621eede0f
commit
65d8c5c389
63
gaphor/transaction.py
Normal file
63
gaphor/transaction.py
Normal file
@ -0,0 +1,63 @@
|
||||
"""
|
||||
Transation support for Gaphor
|
||||
"""
|
||||
|
||||
from zope import interface, component
|
||||
from gaphor.interfaces import ITransaction
|
||||
from gaphor.event import TransactionBegin, TransactionCommit, TransactionRollback
|
||||
|
||||
def transactional(func):
|
||||
def _transactional(*args, **kwargs):
|
||||
tx = Transaction()
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
except:
|
||||
tx.rollback()
|
||||
raise
|
||||
else:
|
||||
tx.commit()
|
||||
return _transactional
|
||||
|
||||
class TransactionError(Exception):
|
||||
"""
|
||||
Errors related to the transaction module.
|
||||
"""
|
||||
|
||||
|
||||
class Transaction(object):
|
||||
interface.implements(ITransaction)
|
||||
|
||||
_stack= []
|
||||
|
||||
def __init__(self):
|
||||
self._need_rollback = False
|
||||
if not self._stack:
|
||||
component.handle(TransactionBegin())
|
||||
self._stack.append(self)
|
||||
|
||||
def commit(self):
|
||||
self._close()
|
||||
# if self._need_rollback:
|
||||
# log.warning('Tried to commit a transaction already marked for rollback.')
|
||||
if not self._stack:
|
||||
if self._need_rollback:
|
||||
component.handle(TransactionRollback())
|
||||
else:
|
||||
component.handle(TransactionCommit())
|
||||
|
||||
def rollback(self):
|
||||
self._close()
|
||||
# Mark every tx on the stack for rollback
|
||||
for tx in self._stack:
|
||||
tx._need_rollback = True
|
||||
if not self._stack:
|
||||
component.handle(TransactionRollback())
|
||||
|
||||
def _close(self):
|
||||
try:
|
||||
last = self._stack.pop()
|
||||
except IndexError:
|
||||
raise TransactionError, 'No Transaction on stack.'
|
||||
if last is not self:
|
||||
self._stack.append(last)
|
||||
raise TransactionError, 'Transaction on stack is not the transaction being closed.'
|
108
gaphor/transaction.txt
Normal file
108
gaphor/transaction.txt
Normal file
@ -0,0 +1,108 @@
|
||||
Transaction support for Gaphor
|
||||
==============================
|
||||
|
||||
Transaction support is located in module gaphor.transaction:
|
||||
|
||||
>>> from gaphor import transaction
|
||||
|
||||
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 zope import component
|
||||
>>> @component.adapter(transaction.TransactionBegin)
|
||||
... def transaction_begin_handler(event):
|
||||
... print 'tx begin'
|
||||
>>> component.provideHandler(transaction_begin_handler)
|
||||
|
||||
Same goes for commit and rollback events:
|
||||
|
||||
>>> @component.adapter(transaction.TransactionCommit)
|
||||
... def transaction_commit_handler(event):
|
||||
... print 'tx commit'
|
||||
>>> component.provideHandler(transaction_commit_handler)
|
||||
>>> @component.adapter(transaction.TransactionRollback)
|
||||
... def transaction_rollback_handler(event):
|
||||
... print 'tx rollback'
|
||||
>>> component.provideHandler(transaction_rollback_handler)
|
||||
|
||||
|
||||
A Transaction is started by initiating a Transaction instance:
|
||||
|
||||
>>> tx = transaction.Transaction()
|
||||
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()
|
||||
... # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TransactionError: No Transaction on stack.
|
||||
|
||||
|
||||
Transactions may be nested:
|
||||
|
||||
>>> tx = transaction.Transaction()
|
||||
tx begin
|
||||
>>> tx2 = transaction.Transaction()
|
||||
>>> tx2.commit()
|
||||
>>> tx.commit()
|
||||
tx commit
|
||||
|
||||
Transactions should be closed in the right order (subtransactions first):
|
||||
|
||||
>>> tx = transaction.Transaction()
|
||||
tx begin
|
||||
>>> tx2 = transaction.Transaction()
|
||||
>>> tx.commit()
|
||||
... # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
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
|
||||
[]
|
||||
|
||||
All transactions are marked for rollback once an exception is raised:
|
||||
|
||||
>>> tx = transaction.Transaction()
|
||||
tx begin
|
||||
>>> a()
|
||||
... # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
IndexError: bla
|
||||
>>> tx._need_rollback
|
||||
True
|
||||
>>> tx.commit()
|
||||
tx rollback
|
||||
|
Loading…
x
Reference in New Issue
Block a user