Passing connections to functions using a decorator

Posted by Daniele Varrazzo on 2010-10-22
Tagged as recipe

In many script I write, there are functions requiring database operations. Every time I need them, I try to write such code in functions like:

@with_connection
def do_some_job(cnn, arg1, arg2=None):
    cur = cnn.cursor()
    cur.execute(SQL, (arg1, arg2)) # or something else

do_some_job(42, arg2='hi')

There are many benefits for this pattern:

  • the client code doesn't depend on the details of connection creation and disposal
  • the client code doesn't either need to deal with the session management: no more pending transactions...
  • connection management is consistent throughout all the functions.

Implementation of such a decorator is straightforward, but because of the many possible variation it never made its way into a proper library. It goes something like:

def with_connection(f):
    def with_connection_(*args, **kwargs):
        # or use a pool, or a factory function...
        cnn = psycopg.connect(DSN)
        try:
            rv = f(cnn, *args, **kwargs)
        except Exception, e:
            cnn.rollback()
            raise
        else:
            cnn.commit() # or maybe not
        finally:
            cnn.close()

        return rv

    return with_connection_

There may be many variations: you may use use a connection pooler, you may want a read only or an autocommit connection... Best strategy I've found is just to keep one or a few decorator like this in the project I'm working at and use it for a consistent management of the database communications in the desired scope (the project, a package, a module...)