""" Note: this is untested. This includes two pieces of middleware to implement a transaction manager. The first (`TransactionManagerMiddleware`) adds a key ``'paste.transaction_manager'`` to the environment, which contains a `Manager` instance. This manager has the interface: ``.abort()``: Abort the transaction. An uncaught exception also aborts the transaction. ``.transactions``: A list of `transaction` objects that participate in this request. At the end of the request ``.commit()`` or ``.rollback()`` will be called. You should append transaction objects directly to this list. The second is `ConflictRetryMiddleware`, which retries a request up to 3 times when a conflict exception is raise. No exception types are caught by default, use ``add_conflict_exception(MyConflictError)`` to register exception types. """ __all__ = ['add_conflict_exception', 'TransactionManagerMiddleware', 'ConflictRetryMiddleware'] from paste import wsgilib class TransactionManagerMiddleware(object): def __init__(self, application): self.application = application def __call__(self, environ, start_response): environ['paste.transaction_manager'] = manager = Manager() # This makes sure nothing else traps unexpected exceptions: environ['paste.throw_errors'] = True return wsgilib.catch_errors(application, environ, start_response, error_callback=manager.error, ok_callback=manager.finish) class Manager(object): def __init__(self): self.aborted = False self.transactions = [] def abort(self): self.aborted = True def error(self, exc_info): self.aborted = True self.finish() def finish(self): for trans in self.transactions: if self.aborted: trans.rollback() else: trans.commit() # Different sources produce different exceptions... _conflict_exceptions = () def add_confict_exception(exc_class): global _confict_exceptions _conflict_exceptions = _conflict_exceptions + (exc_class,) class ConflictRetryMiddleware(object): def __init__(self, application, retries=3): self.application = application self.retries = retries def __call__(self, environ, start_response): assert 'paste.transaction_manager' not in environ, ( "The ConflictRetryMiddleware must be installed above TransactionManagerMiddleware " "in the middleware stack") retries_left = self.retries while 1: try: return self.application(environ, start_response) except _conflict_exceptions: if not retries_left: raise retries_left -= 1