]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
feat(pool): raise a warning if async pools are open in the constructor
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 8 Oct 2023 18:43:47 +0000 (20:43 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sat, 14 Oct 2023 07:39:18 +0000 (09:39 +0200)
docs/news_pool.rst
psycopg_pool/psycopg_pool/base.py
psycopg_pool/psycopg_pool/pool.py
psycopg_pool/psycopg_pool/pool_async.py
tests/pool/test_pool.py
tests/pool/test_pool_async.py
tests/pool/test_pool_async_noasyncio.py
tests/pool/test_pool_common.py
tests/pool/test_pool_common_async.py

index 7cc79a815471d5dd149e8884e5ba135bdd29c100..636dc9a8c03d7a6b3317f1c1b5db0764f07502aa 100644 (file)
@@ -16,9 +16,11 @@ psycopg_pool 3.2.0 (unreleased)
 - Add support for async `!reconnect_failed` callbacks in `AsyncConnectionPool`
   (:ticket:`#520`).
 - Make connection pool classes generic on the connection type (:ticket:`#559`).
-- Raise a warning if a pool is used relying on an implicit `!open=True` and the
+- Raise a warning if sync pools rely an implicit `!open=True` and the
   pool context is not used. In the future the default will become `!False`
   (:ticket:`#659`).
+- Raise a warning if async pools are opened in the constructor. In the future
+  it will become an error. (:ticket:`#659`).
 
 
 psycopg_pool 3.1.9 (unreleased)
index a14f7a2d65e68d530299caca3255187851ccaa25..1ed6ab1c92995b1cf780399a7f9cf9d26b5412f6 100644 (file)
@@ -7,7 +7,6 @@ psycopg connection pool base class and functionalities.
 from time import monotonic
 from random import random
 from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING
-import warnings
 
 from psycopg import errors as e
 
@@ -142,13 +141,6 @@ class BasePool:
                 raise PoolClosed(f"the pool {self.name!r} is already closed")
             else:
                 raise PoolClosed(f"the pool {self.name!r} is not open yet")
-        elif self._open_implicit:
-            warnings.warn(
-                f"the default for the {type(self).__name__} 'open' parameter will"
-                " become 'False' in a future release; please use open={True|False}"
-                " explicitly or use the pool as context manager",
-                DeprecationWarning,
-            )
 
     def _check_pool_putconn(self, conn: "BaseConnection[Any]") -> None:
         pool = getattr(conn, "_pool", None)
index 8abecd6ed7d4346d44c78ea1b60720d6136bd052..185cef08d881aa94a0f8401194d411c9d97eff5d 100644 (file)
@@ -10,6 +10,7 @@ Psycopg connection pool module (sync version).
 from __future__ import annotations
 
 import logging
+import warnings
 from abc import ABC, abstractmethod
 from time import monotonic
 from types import TracebackType
@@ -152,6 +153,20 @@ class ConnectionPool(Generic[CT], BasePool):
 
         self._stop_workers()
 
+    def _check_open_getconn(self) -> None:
+        super()._check_open_getconn()
+
+        if self._open_implicit:
+            self._open_implicit = False
+
+            warnings.warn(
+                f"the default for the {type(self).__name__} 'open' parameter"
+                + " will become 'False' in a future release. Please use"
+                + " open={True|False} explicitly, or use the pool as context"
+                + f" manager using: `with {type(self).__name__}(...) as pool: ...`",
+                DeprecationWarning,
+            )
+
     def wait(self, timeout: float = 30.0) -> None:
         """
         Wait for the pool to be full (with `min_size` connections) after creation.
index 2077b425b6ade55f6dafc4060919e15fbdf722cf..c1c371022d1a179f8db6f5575414e4fe593e2eaf 100644 (file)
@@ -7,6 +7,7 @@ Psycopg connection pool module (async version).
 from __future__ import annotations
 
 import logging
+import warnings
 from abc import ABC, abstractmethod
 from time import monotonic
 from types import TracebackType
@@ -137,6 +138,10 @@ class AsyncConnectionPool(Generic[ACT], BasePool):
             num_workers=num_workers,
         )
 
+        if True:  # ASYNC
+            if open:
+                self._warn_open_async()
+
         if open is None:
             open = self._open_implicit = True
 
@@ -153,6 +158,35 @@ class AsyncConnectionPool(Generic[ACT], BasePool):
 
             self._stop_workers()
 
+    def _check_open_getconn(self) -> None:
+        super()._check_open_getconn()
+
+        if self._open_implicit:
+            self._open_implicit = False
+
+            if True:  # ASYNC
+                # If open was explicit, we already warned it in __init__
+                self._warn_open_async()
+            else:
+                warnings.warn(
+                    f"the default for the {type(self).__name__} 'open' parameter"
+                    + " will become 'False' in a future release. Please use"
+                    + " open={True|False} explicitly, or use the pool as context"
+                    + f" manager using: `with {type(self).__name__}(...) as pool: ...`",
+                    DeprecationWarning,
+                )
+
+    if True:  # ASYNC
+
+        def _warn_open_async(self) -> None:
+            warnings.warn(
+                f"opening the async pool {type(self).__name__} in the constructor"
+                " is deprecated and will not be supported anymore in a future"
+                " release. Please use `await pool.open()`, or use the pool as context"
+                f" manager using: `async with {type(self).__name__}(...) as pool: `...",
+                DeprecationWarning,
+            )
+
     async def wait(self, timeout: float = 30.0) -> None:
         """
         Wait for the pool to be full (with `min_size` connections) after creation.
index f16d5480b008047dcf5a5f1462e281ce9cccf60c..fd3d656b3253470cd1606adc7ef3f90f522e5df0 100644 (file)
@@ -359,7 +359,8 @@ def test_fail_rollback_close(dsn, caplog, monkeypatch):
 
 
 def test_del_no_warning(dsn, recwarn):
-    p = pool.ConnectionPool(dsn, min_size=2, open=True)
+    p = pool.ConnectionPool(dsn, min_size=2, open=False)
+    p.open()
     with p.connection() as conn:
         conn.execute("select 1")
 
@@ -788,7 +789,7 @@ def test_debug_deadlock(dsn):
     handler.setLevel(logging.DEBUG)
     logger.addHandler(handler)
     try:
-        with pool.ConnectionPool(dsn, min_size=4, open=True) as p:
+        with pool.ConnectionPool(dsn, min_size=4) as p:
             p.wait(timeout=2)
     finally:
         logger.removeHandler(handler)
index a49622062e42b7d182d0551816b5293c3bac3b05..195fec8186d211ccda53c1cca4765fc12a18ad5d 100644 (file)
@@ -363,7 +363,8 @@ async def test_fail_rollback_close(dsn, caplog, monkeypatch):
 
 
 async def test_del_no_warning(dsn, recwarn):
-    p = pool.AsyncConnectionPool(dsn, min_size=2, open=True)
+    p = pool.AsyncConnectionPool(dsn, min_size=2, open=False)
+    await p.open()
     async with p.connection() as conn:
         await conn.execute("select 1")
 
@@ -794,7 +795,7 @@ async def test_debug_deadlock(dsn):
     handler.setLevel(logging.DEBUG)
     logger.addHandler(handler)
     try:
-        async with pool.AsyncConnectionPool(dsn, min_size=4, open=True) as p:
+        async with pool.AsyncConnectionPool(dsn, min_size=4) as p:
             await p.wait(timeout=2)
     finally:
         logger.removeHandler(handler)
index 982b9288e8a74dcb79ee91b09d75ac3e2acf8bb7..304aa4ceb54c883da69a9849240d8194f395544c 100644 (file)
@@ -56,8 +56,9 @@ def test_working_created_before_loop(dsn, asyncio_run):
 
 
 def test_cant_create_open_outside_loop(dsn):
-    with pytest.raises(RuntimeError):
-        pool.AsyncConnectionPool(dsn, open=True)
+    with pytest.warns(DeprecationWarning):
+        with pytest.raises(RuntimeError):
+            pool.AsyncConnectionPool(dsn, open=True)
 
 
 @pytest.fixture
index e12433786a257c9f04adba78743c1e75c5b5507b..df76266a5aa1b46fcab2151b9017721341af59a5 100644 (file)
@@ -55,7 +55,7 @@ def test_context(pool_cls, dsn):
 
 
 def test_create_warning(pool_cls, dsn):
-    # No warning on explicit open
+    # No warning on explicit open for sync pool
     p = pool_cls(dsn, open=True)
     try:
         with p.connection():
@@ -357,7 +357,8 @@ def test_del_stops_threads(pool_cls, dsn):
 
 
 def test_closed_getconn(pool_cls, dsn):
-    p = pool_cls(dsn, min_size=min_size(pool_cls), open=True)
+    p = pool_cls(dsn, min_size=min_size(pool_cls), open=False)
+    p.open()
     assert not p.closed
     with p.connection():
         pass
@@ -371,7 +372,8 @@ def test_closed_getconn(pool_cls, dsn):
 
 
 def test_close_connection_on_pool_close(pool_cls, dsn):
-    p = pool_cls(dsn, min_size=min_size(pool_cls), open=True)
+    p = pool_cls(dsn, min_size=min_size(pool_cls), open=False)
+    p.open()
     with p.connection() as conn:
         p.close()
     assert conn.closed
@@ -396,18 +398,17 @@ def test_closed_queue(pool_cls, dsn):
     e1 = Event()
     e2 = Event()
 
-    p = pool_cls(dsn, min_size=min_size(pool_cls), max_size=1, open=True)
-    p.wait()
-    success: List[str] = []
+    with pool_cls(dsn, min_size=min_size(pool_cls), max_size=1) as p:
+        p.wait()
+        success: List[str] = []
 
-    t1 = spawn(w1)
-    # Wait until w1 has received a connection
-    e1.wait()
+        t1 = spawn(w1)
+        # Wait until w1 has received a connection
+        e1.wait()
 
-    t2 = spawn(w2)
-    # Wait until w2 is in the queue
-    ensure_waiting(p)
-    p.close()
+        t2 = spawn(w2)
+        # Wait until w2 is in the queue
+        ensure_waiting(p)
 
     # Wait for the workers to finish
     e2.set()
@@ -454,7 +455,8 @@ def test_open_context(pool_cls, dsn):
 
 
 def test_open_no_op(pool_cls, dsn):
-    p = pool_cls(dsn, open=True)
+    p = pool_cls(dsn, open=False)
+    p.open()
     try:
         assert not p.closed
         p.open()
@@ -468,7 +470,8 @@ def test_open_no_op(pool_cls, dsn):
 
 
 def test_reopen(pool_cls, dsn):
-    p = pool_cls(dsn, open=True)
+    p = pool_cls(dsn, open=False)
+    p.open()
     with p.connection() as conn:
         conn.execute("select 1")
     p.close()
@@ -567,7 +570,7 @@ def test_debug_deadlock(pool_cls, dsn):
     handler.setLevel(logging.DEBUG)
     logger.addHandler(handler)
     try:
-        with pool_cls(dsn, min_size=min_size(pool_cls, 4), open=True) as p:
+        with pool_cls(dsn, min_size=min_size(pool_cls, 4)) as p:
             p.wait(timeout=2)
     finally:
         logger.removeHandler(handler)
index b368fab54bd0153b3179a7fc8f464eca48ec2157..f231652534604b1632b46d59426f62a4a17184b9 100644 (file)
@@ -57,13 +57,20 @@ async def test_context(pool_cls, dsn):
 
 
 async def test_create_warning(pool_cls, dsn):
-    # No warning on explicit open
-    p = pool_cls(dsn, open=True)
-    try:
-        async with p.connection():
-            pass
-    finally:
-        await p.close()
+    if True:  # ASYNC
+        # warning on explicit open too on async
+        with pytest.warns(DeprecationWarning):
+            p = pool_cls(dsn, open=True)
+            await p.close()
+
+    else:
+        # No warning on explicit open for sync pool
+        p = pool_cls(dsn, open=True)
+        try:
+            async with p.connection():
+                pass
+        finally:
+            await p.close()
 
     # No warning on explicit close
     p = pool_cls(dsn, open=False)
@@ -367,7 +374,8 @@ async def test_del_stops_threads(pool_cls, dsn):
 
 
 async def test_closed_getconn(pool_cls, dsn):
-    p = pool_cls(dsn, min_size=min_size(pool_cls), open=True)
+    p = pool_cls(dsn, min_size=min_size(pool_cls), open=False)
+    await p.open()
     assert not p.closed
     async with p.connection():
         pass
@@ -381,7 +389,8 @@ async def test_closed_getconn(pool_cls, dsn):
 
 
 async def test_close_connection_on_pool_close(pool_cls, dsn):
-    p = pool_cls(dsn, min_size=min_size(pool_cls), open=True)
+    p = pool_cls(dsn, min_size=min_size(pool_cls), open=False)
+    await p.open()
     async with p.connection() as conn:
         await p.close()
     assert conn.closed
@@ -406,18 +415,17 @@ async def test_closed_queue(pool_cls, dsn):
     e1 = AEvent()
     e2 = AEvent()
 
-    p = pool_cls(dsn, min_size=min_size(pool_cls), max_size=1, open=True)
-    await p.wait()
-    success: List[str] = []
+    async with pool_cls(dsn, min_size=min_size(pool_cls), max_size=1) as p:
+        await p.wait()
+        success: List[str] = []
 
-    t1 = spawn(w1)
-    # Wait until w1 has received a connection
-    await e1.wait()
+        t1 = spawn(w1)
+        # Wait until w1 has received a connection
+        await e1.wait()
 
-    t2 = spawn(w2)
-    # Wait until w2 is in the queue
-    await ensure_waiting(p)
-    await p.close()
+        t2 = spawn(w2)
+        # Wait until w2 is in the queue
+        await ensure_waiting(p)
 
     # Wait for the workers to finish
     e2.set()
@@ -465,7 +473,8 @@ async def test_open_context(pool_cls, dsn):
 
 
 async def test_open_no_op(pool_cls, dsn):
-    p = pool_cls(dsn, open=True)
+    p = pool_cls(dsn, open=False)
+    await p.open()
     try:
         assert not p.closed
         await p.open()
@@ -480,7 +489,8 @@ async def test_open_no_op(pool_cls, dsn):
 
 
 async def test_reopen(pool_cls, dsn):
-    p = pool_cls(dsn, open=True)
+    p = pool_cls(dsn, open=False)
+    await p.open()
     async with p.connection() as conn:
         await conn.execute("select 1")
     await p.close()
@@ -579,7 +589,7 @@ async def test_debug_deadlock(pool_cls, dsn):
     handler.setLevel(logging.DEBUG)
     logger.addHandler(handler)
     try:
-        async with pool_cls(dsn, min_size=min_size(pool_cls, 4), open=True) as p:
+        async with pool_cls(dsn, min_size=min_size(pool_cls, 4)) as p:
             await p.wait(timeout=2)
     finally:
         logger.removeHandler(handler)