]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
docs(pool): add documentation for close_returns
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 4 May 2025 21:26:26 +0000 (23:26 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Mon, 12 May 2025 00:51:18 +0000 (02:51 +0200)
docs/advanced/pool.rst
docs/api/pool.rst
docs/news_pool.rst
psycopg_pool/psycopg_pool/pool.py
psycopg_pool/psycopg_pool/pool_async.py

index 6b41a7c05155dddf87206e9895d252daf27c2770..98f5563917f0f4b6e862335ddf5ae8a5e5e5de7c 100644 (file)
@@ -429,3 +429,70 @@ Metric                  Meaning
  ``connections_lost``   Number of connections lost identified by
                         `~ConnectionPool.check()` or by the `!check` callback
 ======================= =====================================================
+
+
+.. _pool-sqlalchemy:
+
+Integration with SQLAlchemy
+---------------------------
+
+.. versionadded:: 3.3
+
+If you want to use SQLAlchemy__ with psycopg's connection pool you can use the
+SQLAlchemy's NullPool__ feature, using the pool's `~ConnectionPool.getconn`
+as `!creator` function. However, SQLAlchemy expects that calling
+`~psycopg.Connection.close()` on the connection will return the connection to
+the pool, which is not Psycopg's default behaviour.
+
+.. __: https://sqlalchemy.org
+.. __: https://docs.sqlalchemy.org/en/20/core/pooling.html#sqlalchemy.pool.NullPool
+
+Since `!psycopg_pool` version 3.3, the `!close_returns` parameter of the pool
+allows to change this behaviour, telling its connections to return to the
+pool, instead of closing, when `!close()` is called.
+
+For example:
+
+.. code:: python
+
+    import sqlalchemy.pool
+    import psycopg_pool
+
+    db_url = "postgresql://postgres:postgres@hostname:port/database"
+
+    pgpool = psycopg_pool.ConnectionPool(conninfo=db_url, close_returns=True)
+
+    engine = sqlalchemy.create_engine(
+        url=db_url.replace("postgresql://", "postgresql+psycopg://"),
+        poolclass=sqlalchemy.pool.NullPool,
+        creator=pgpool.getconn,
+    )
+
+If you want to integrate the Psycopg pool with SQLAlchemy using a `!psycopg_pool`
+version older than 3.3, you will need to create a subclass of the
+`~psycopg.Connection` or `~psycopg.AsyncConnection` class, overriding the
+`!close()` method, with an implementation similar to the following:
+
+.. code:: python
+
+    class MyConnection(psycopg.AsyncConnection):
+        # If you need to subclass a sync connection, just drop the
+        # 'async' and 'await' keywords from this example.
+        async def close(self) -> None:
+            if pool := getattr(self, "_pool", None):
+                # Connection currently checked out from the pool.
+                # Instead of closing it, return it to the pool.
+                await pool.putconn(self)
+            else:
+                # Connection not part of any pool, or currently into the pool.
+                # Close the connection for real.
+                await super().close()
+
+
+Using a connection implementing this method you will not need to specify
+`!close_returns` in the pool constructor, but just `!connection_class`; the
+example above would be modified as:
+
+.. code:: python
+
+    pgpool = psycopg_pool.ConnectionPool(conninfo=db_url, connection_class=MyConnection)
index 76aed2b7db7c5e59c3e1a9c80c1d2e652b46f3e4..c1d0f5457b5af33c761786196557384d5d3eb104 100644 (file)
@@ -98,6 +98,15 @@ The `!ConnectionPool` class
                  want to perform a simple check.
    :type check: `Callable[[Connection], None]`
 
+   :param close_returns: If `!True`, calling `~psycopg.Connection.close()` on
+                         the connection will not actually close it, but it
+                         will return the connection to the pool, like in
+                         `~ConnectionPool.putconn()`. Use it if you want to
+                         use Psycopg pool with SQLAlchemy. See
+                         :ref:`pool-sqlalchemy`.
+
+   :type close_returns: `!bool`, default: `!False`
+
    :param reset: A callback to reset a function after it has been returned to
                  the pool. The connection is guaranteed to be passed to the
                  `!reset()` function in "idle" state (no transaction). When
@@ -166,9 +175,12 @@ The `!ConnectionPool` class
         added `!check` parameter to the constructor.
 
    .. versionchanged:: 3.2
-        The class is generic and `!connection_class` provides types type
+        the class is generic and `!connection_class` provides types type
         variable. See :ref:`pool-generic`.
 
+   .. versionchanged:: 3.3
+        added `!close_returns` parameter to the constructor.
+
    .. warning::
 
         At the moment, the default value for the `!open` parameter is `!True`;
@@ -298,6 +310,9 @@ Only the functions and parameters with different signature from
    .. versionchanged:: 3.2
         The `!reconnect_failed` parameter can be `!async`.
 
+   .. versionchanged:: 3.3
+        added `!close_returns` parameter to the constructor.
+
    .. warning::
 
         Opening an async pool in the constructor (using `!open=True` on init)
index 1e49e52a5cf4201b7c900d936fdc4de3e7ad8995..a2a463e0efc7591e12181449ea3718e68e9f170b 100644 (file)
@@ -7,6 +7,16 @@
 ``psycopg_pool`` release notes
 ==============================
 
+Future releases
+---------------
+
+psycopg_pool 3.3.0 (unreleased)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Add `!close_returns` for :ref:`integration with SQLAlchemy <pool-sqlalchemy>`
+  (:ticket:`#1046`).
+
+
 Current release
 ---------------
 
index d5fd6eb0f55f5cb3f24fa6983438a60b312a0dae..e67a32f249b2a01b75cbdf34bad60392ef6f58bb 100644 (file)
@@ -65,7 +65,7 @@ class ConnectionPool(Generic[CT], BasePool):
                 connection_class = cast(type[CT], PoolConnection)
             else:
                 raise TypeError(
-                    "Using 'close_returns=True' and a non-standard 'connection_class' requires psycopg 3.3 or newer."
+                    "Using 'close_returns=True' and a non-standard 'connection_class' requires psycopg 3.3 or newer. Please check the docs at https://www.psycopg.org/psycopg3/docs/advanced/pool.html#pool-sqlalchemy for a workaround."
                 )
 
         self.connection_class = connection_class
index ac4dfe83c7801bece5f77f67906e8bfc44840495..5731de9af1cd17a998881b1f63d8e3befbf3bc68 100644 (file)
@@ -66,7 +66,9 @@ class AsyncConnectionPool(Generic[ACT], BasePool):
             else:
                 raise TypeError(
                     "Using 'close_returns=True' and a non-standard 'connection_class'"
-                    " requires psycopg 3.3 or newer."
+                    " requires psycopg 3.3 or newer. Please check the docs at"
+                    " https://www.psycopg.org/psycopg3/docs/advanced/pool.html"
+                    "#pool-sqlalchemy for a workaround."
                 )
 
         self.connection_class = connection_class