    """
    DBPool.py

    Implements a pool of cached connections to a database. This should result in
    a speedup for persistent apps. The pool of connections is threadsafe
    regardless of whether the DB API module question in general has a
    threadsafety of 1 or 2.

    For more information on the DB API, see:
        http://www.python.org/topics/database/DatabaseAPI-2.0.html

    The idea behind DBPool is that it's completely seamless, so once you have
    established your connection, use it just as you would any other DB-API
    compliant module. For example:

        dbPool = DBPool(MySQLdb, 5, host=xxx, user=xxx, ...)
        db = dbPool.getConnection()

    Now use "db" exactly as if it were a MySQLdb connection. It's really
    just a proxy class.

    db.close() will return the connection to the pool, not actually
    close it. This is so your existing code works nicely.

    FUTURE

    * If in the presence of WebKit, register ourselves as a Can.

    CREDIT

    * Contributed by Dan Green
    * thread safety bug found by Tom Schwaller
    * Fixes by Geoff Talvola (thread safety in _threadsafe_getConnection()).
    * Clean up by Chuck Esterbrook.
    * Fix unthreadsafe functions which were leaking, Jay Love
    * Eli Green's webware-discuss comments were lifted for additional docs.
    """

    import threading

    class DBPoolError(Exception): pass
    class UnsupportedError(DBPoolError): pass

    class PooledConnection:
        """ A wrapper for database connections to help with DBPool. You don't normally deal with this class directly, but use DBPool to get new connections. """

        def __init__(self, pool, con):
            self._con = con
            self._pool = pool

        def close(self):
            if self._con is not None:
                self._pool.returnConnection(self)
                self._con = None

        def __getattr__(self, name):
            return getattr(self._con, name)

        def __del__(self):
            self.close()

    class DBPool:

        def __init__(self, dbModule, maxConnections, *args, **kwargs):
            if dbModule.threadsafety==0:
                raise UnsupportedError, "Database module does not support any level of threading."
            elif dbModule.threadsafety==1:
                from Queue import Queue
                self._queue = Queue(maxConnections)
                self.addConnection = self._unthreadsafe_addConnection
                self.getConnection = self._unthreadsafe_getConnection
                self.returnConnection = self._unthreadsafe_returnConnection
            elif dbModule.threadsafety>=2:
                self._lock = threading.Lock()
                self._nextCon = 0
                self._connections = []
                self.addConnection = self._threadsafe_addConnection
                self.getConnection = self._threadsafe_getConnection
                self.returnConnection = self._threadsafe_returnConnection

            # @@ 2000-12-04 ce: Should we really make all the connections now?
            # Couldn't we do this on demand?
            for i in range(maxConnections):
                con = apply(dbModule.connect, args, kwargs)
                self.addConnection(con)

        # threadsafe/unthreadsafe refers to the database _module_, not THIS class..
        # this class is definitely threadsafe (um. that is, I hope so - Dan)

        def _threadsafe_addConnection(self, con):
            self._connections.append(con)

        def _threadsafe_getConnection(self):
            self._lock.acquire()
            try:
                con = PooledConnection(self, self._connections[self._nextCon])
                self._nextCon = self._nextCon + 1
                if self._nextCon >= len(self._connections):
                    self._nextCon = 0
                return con
            finally:
                self._lock.release()

        def _threadsafe_returnConnection(self, con):
            return

        # These functions are used with DB modules that have connection level threadsafety, like PostgreSQL.
        #
        def _unthreadsafe_addConnection(self, con):
            self._queue.put(con)

        def _unthreadsafe_getConnection(self):
            return PooledConnection(self, self._queue.get())

        def _unthreadsafe_returnConnection(self, conpool):
            """
            This should never be called explicitly outside of this module.
            """
            self.addConnection(conpool._con)