transact.py 1.93 KB
Newer Older
Jeremy Hylton's avatar
Jeremy Hylton committed
1 2 3 4 5 6
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
Jim Fulton's avatar
Jim Fulton committed
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
Jeremy Hylton's avatar
Jeremy Hylton committed
8 9 10 11 12 13 14 15 16 17 18
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Tools to simplify transactions within applications."""

from ZODB.POSException import ReadConflictError, ConflictError

def _commit(note):
19
    t = transaction.get()
Jeremy Hylton's avatar
Jeremy Hylton committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
    if note:
        t.note(note)
    t.commit()

def transact(f, note=None, retries=5):
    """Returns transactional version of function argument f.

    Higher-order function that converts a regular function into
    a transactional function.  The transactional function will
    retry up to retries time before giving up.  If note, it will
    be added to the transaction metadata when it commits.

    The retries occur on ConflictErrors.  If some other
    TransactionError occurs, the transaction will not be retried.
    """

Tim Peters's avatar
Tim Peters committed
36
    # TODO:  deal with ZEO disconnected errors?
37

Jeremy Hylton's avatar
Jeremy Hylton committed
38
    def g(*args, **kwargs):
39 40 41
        n = retries
        while n:
            n -= 1
Jeremy Hylton's avatar
Jeremy Hylton committed
42 43 44
            try:
                r = f(*args, **kwargs)
            except ReadConflictError, msg:
45
                transaction.abort()
46
                if not n:
Jeremy Hylton's avatar
Jeremy Hylton committed
47 48 49 50 51
                    raise
                continue
            try:
                _commit(note)
            except ConflictError, msg:
52
                transaction.abort()
53
                if not n:
Jeremy Hylton's avatar
Jeremy Hylton committed
54 55 56 57 58
                    raise
                continue
            return r
        raise RuntimeError, "couldn't commit transaction"
    return g