1############################################################################## 2# 3# Copyright (c) 2003 Zope Foundation and Contributors. 4# All Rights Reserved. 5# 6# This software is subject to the provisions of the Zope Public License, 7# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. 8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED 9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 11# FOR A PARTICULAR PURPOSE 12# 13############################################################################## 14"""Tools to simplify transactions within applications.""" 15 16from ZODB.POSException import ReadConflictError, ConflictError 17import transaction 18 19def _commit(note): 20 t = transaction.get() 21 if note: 22 t.note(note) 23 t.commit() 24 25def transact(f, note=None, retries=5): 26 """Returns transactional version of function argument f. 27 28 Higher-order function that converts a regular function into 29 a transactional function. The transactional function will 30 retry up to retries time before giving up. If note, it will 31 be added to the transaction metadata when it commits. 32 33 The retries occur on ConflictErrors. If some other 34 TransactionError occurs, the transaction will not be retried. 35 """ 36 37 # TODO: deal with ZEO disconnected errors? 38 39 def g(*args, **kwargs): 40 n = retries 41 while n: 42 n -= 1 43 try: 44 r = f(*args, **kwargs) 45 except ReadConflictError as msg: 46 transaction.abort() 47 if not n: 48 raise 49 continue 50 try: 51 _commit(note) 52 except ConflictError as msg: 53 transaction.abort() 54 if not n: 55 raise 56 continue 57 return r 58 raise RuntimeError("couldn't commit transaction") 59 return g 60