@dstufft I've been doing a bit of reading on how sqlalchemy handles transactions, and I think the current programming paradigm makes doing this very difficult (in a standard way).

The example you showed only works in the threadlocal execution strategy: http://docs.sqlalchemy.org/en/rel_0_9/core/connections.html#using-the-threadlocal-execution-strategy , mainly because _insert_journal_entry utilizes engine.execute directly, instead of being passed a connection object. 

This has to be configured from the get go, and it's considered a legacy pattern. It also seems like it could be very confusing, as it's not clear when you're acting in a transaction or not.

I've been thinking of a way to remove this restriction. It seems like the pattern of having a class instance that is aware of it's engine object is restrictive: rather, it would be better to have each db method be static and take in a connection object:

    def upsert_project(connection, name, username, user_ip, **additional_columns):
        # NOTE: pypi behaviour is to assign the first submitter of a
        # project the "owner" role. this code does not
        # perform that behaviour (implement in the view instead)
        with connection.begin() as trans:
            db.validate_argument_column_mapping(additional_columns, packages)

            existing_project = self.get_project(name)

            if existing_project:
                message = "updating project {0}".format(existing_project['name'])
                query = (packages.update()
                     .where(packages.c.name == existing_project['name']))
            else:
                message = "create"
                query = packages.insert()

            trans.execute(query.values(
                name=name,
                normalized_name=utils.normalize_project_name(name),
                **additional_columns
            ))

            # potentially change the name here to something like PackageDatabase?
            Database._insert_journal_entry(trans, name, None, message, username, user_ip)

This make it very easy to compose transactions. Also, sqlalchemy is good at handling nested transaction blocks, so we don't have to worry there:

http://docs.sqlalchemy.org/en/rel_0_9/core/connections.html#nesting-of-transaction-blocks

This is a pretty large change, however. I'd be happy to take it on, but it would require a pretty large fundamental change to the layout of the Database Wrappers.