Remy Blank wrote:
> Christian Boos wrote:
>   
>> Rather, the comparison with the "with" approach made me think we should 
>> instead propagate the value returned by the implementation function, 
>> otherwise we would have no way to communicate something from there to 
>> the outside.
>>     
>
> Not quite true. The usual trick is to allocate a list in the surrounding
> scope, and to mutate the array in the transaction function.
>
>   retval = [0]
>
>   @with_transaction(env, db)
>   def renamne(db):
>       # ... transaction body ...
>       retval[0] = 1
>   

Ah yes ;-)
That's more explicit than the return trick, and when switching to 
"with", we'll just have to remove the list.

> (snip)
> Could we start with as little magic as possible, and only add the magic
> if/when we really need it?
>   

Ok, reading more about context managers show that swallowing exceptions 
should be ... the exception.
Updated sample code accordingly.

-- Christian

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Trac 
Development" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/trac-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

from __future__ import with_statement # this requires Python 2.5 or 2.6

class Connection(object):
    def __init__(self, fail):
        self._fail = fail
    def execute(self):
        if self._fail:
            print '>> arghh!'
            a = int('')
        else:
            print '>> execute something successfully...'
    def commit(self):
        print '>> commit transaction'
    def rollback(self):
        print '>> rollback transaction'

class Environment(object):
    def __init__(self, fail):
        self._fail = fail
    def get_db_cnx(self):
        print ">> Environment.get_db_cnx"
        return Connection(self._fail)


def with_transaction(env, db):
    """ ... """
    def wrap(fn):
        if db:
            fn(db)
        else:
            tmpdb = env.get_db_cnx()
            try:
                fn(tmpdb)
                tmpdb.commit()
            except Exception:
                tmpdb.rollback()
                raise
    return wrap


class transaction(object):
    def __init__(self, env, db):
        self._env = env
        self._db = db

    def __enter__(self):
        if not self._db:
            self._db = self._env.get_db_cnx()
            self._env = None
        return self._db

    def __exit__(self, et, ev, tb):
        if self._env is None:
            if et is None:
                self._db.commit()
            else:
                self._db.rollback()


# Trac 0.12
def do_rename_deco(env, db=None):
    new_name = [None]

    @with_transaction(env, db)
    def rename(db):
        db.execute()
        new_name[0] = 'renamed'
    
    if new_name[0]:
        print '> it worked:', new_name[0]
        return True
    else:
        print '> rename failed'


# Trac 0.13
def do_rename_with(env, db=None):
    new_name = None
    
    with transaction(env, db) as db:
        db.execute()
        new_name = 'renamed' # modifies `new_name` above
    
    if new_name:
        print '> it worked:', new_name
        return True
    else:
        print '> rename failed'



if __name__ == '__main__':
    for do_rename in (do_rename_deco, do_rename_with):
        for fail in (False, True):
            env = Environment(fail)
            print
            print '== (fail=%r, do_rename=%r)' % (fail, do_rename)
            print
            print '-- testing automatic transaction'
            try:
                do_rename(env)
            except:
                pass
            print
            print '-- testing explicit transaction'
            db = env.get_db_cnx()
            try:
                if do_rename(env, db) and do_rename(env, db):
                    db.commit()
                else:
                    db.rollback()
            except:
                db.rollback()

Reply via email to