Honored twistedeers,


Consider the following (blocking) decorator, which runs a function in a transaction:

def _with_transaction(f):
    def decorated(self, *args, **kwargs):
        conn = self.engine.connect()
        txn = conn.begin()

        try:
            result = f(self, conn, *args, **kwargs)
        except:
            txn.rollback()
            raise
        else:
            txn.commit()
            return

    return decorated

Where I to translate this logic verbatim to @inlineCallbacks, I get:

def _with_transaction(f):
    @inlineCallbacks
    def decorated(self, *args, **kwargs):
        conn = yield self.engine.connect()
        txn = yield conn.begin()

        try:
            result = yield f(self, conn, *args, **kwargs)
        except:
            yield txn.rollback()
            raise
        else:
            yield txn.commit()
            returnValue(result)

    return decorated

However, there’s a bug here! In the except clause: there’s an (implicit) current exception, to be re-raised by the bare raise statement. Unfortunately, when doing yield txn.rollback(), that conveniently eats said exception.

Of course, there’s a fairly simple workaround involving catching BaseException and capturing the exception instance explicitly.

I’m wondering if this is just a leaky abstraction, or if I should report it as a bug in @inlineCallbacks?


cheers
lvh