Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-psycopg-pool for 
openSUSE:Factory checked in at 2024-01-07 21:41:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-psycopg-pool (Old)
 and      /work/SRC/openSUSE:Factory/.python-psycopg-pool.new.28375 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-psycopg-pool"

Sun Jan  7 21:41:02 2024 rev:3 rq:1137427 version:3.2.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-psycopg-pool/python-psycopg-pool.changes  
2023-12-15 21:48:24.744639866 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-psycopg-pool.new.28375/python-psycopg-pool.changes
       2024-01-07 21:41:20.298280204 +0100
@@ -1,0 +2,10 @@
+Sun Jan  7 19:25:21 UTC 2024 - Dirk Müller <dmuel...@suse.com>
+
+- update to 3.2.1:
+  * Respect the timeout parameter on connection() when check fails.
+    Also avoid a busy-loop of checking; separate check attempts
+    using an exponential backoff (ticket #709).
+  * Use typing.Self as a more correct return value annotation of
+    context managers and other self-returning methods (see ticket
+
+-------------------------------------------------------------------

Old:
----
  psycopg-pool-3.2.0.tar.gz

New:
----
  psycopg-pool-3.2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-psycopg-pool.spec ++++++
--- /var/tmp/diff_new_pack.G3R6Xm/_old  2024-01-07 21:41:20.818299120 +0100
+++ /var/tmp/diff_new_pack.G3R6Xm/_new  2024-01-07 21:41:20.818299120 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-psycopg-pool
 #
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           python-psycopg-pool
-Version:        3.2.0
+Version:        3.2.1
 Release:        0
 Summary:        Connection Pool for Psycopg
 License:        LGPL-3.0-only
@@ -28,7 +28,7 @@
 BuildRequires:  %{python_module wheel >= 0.37}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
-Requires:       python-typing-extensions >= 3.10
+Requires:       python-typing-extensions >= 4.4
 BuildArch:      noarch
 %python_subpackages
 

++++++ psycopg-pool-3.2.0.tar.gz -> psycopg-pool-3.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/PKG-INFO 
new/psycopg-pool-3.2.1/PKG-INFO
--- old/psycopg-pool-3.2.0/PKG-INFO     2023-11-11 16:14:36.800819200 +0100
+++ new/psycopg-pool-3.2.1/PKG-INFO     2024-01-07 13:24:41.375857400 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: psycopg-pool
-Version: 3.2.0
+Version: 3.2.1
 Summary: Connection Pool for Psycopg
 Home-page: https://psycopg.org/psycopg3/
 Author: Daniele Varrazzo
@@ -24,6 +24,8 @@
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Database
 Classifier: Topic :: Database :: Front-Ends
 Classifier: Topic :: Software Development
@@ -31,7 +33,7 @@
 Requires-Python: >=3.8
 Description-Content-Type: text/x-rst
 License-File: LICENSE.txt
-Requires-Dist: typing-extensions>=3.10
+Requires-Dist: typing-extensions>=4.4
 
 Psycopg 3: PostgreSQL database adapter for Python - Connection Pool
 ===================================================================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool/_acompat.py 
new/psycopg-pool-3.2.1/psycopg_pool/_acompat.py
--- old/psycopg-pool-3.2.0/psycopg_pool/_acompat.py     2023-11-11 
16:14:26.000000000 +0100
+++ new/psycopg-pool-3.2.1/psycopg_pool/_acompat.py     2024-01-07 
13:24:30.000000000 +0100
@@ -10,14 +10,17 @@
 
 from __future__ import annotations
 
+import time
 import queue
 import asyncio
 import logging
 import threading
-from typing import Any, Callable, Coroutine, TypeVar, TYPE_CHECKING
+from typing import Any, Callable, Coroutine, TYPE_CHECKING
 
 from typing_extensions import TypeAlias
 
+from ._compat import TypeVar
+
 logger = logging.getLogger("psycopg.pool")
 T = TypeVar("T")
 
@@ -26,6 +29,7 @@
 Condition = threading.Condition
 Lock = threading.RLock
 ALock = asyncio.Lock
+sleep = time.sleep
 
 Worker: TypeAlias = threading.Thread
 AWorker: TypeAlias = "asyncio.Task[None]"
@@ -160,3 +164,10 @@
         if not t.is_alive():
             continue
         logger.warning("couldn't stop thread %r within %s seconds", t.name, 
timeout)
+
+
+def asleep(seconds: float) -> Coroutine[Any, Any, None]:
+    """
+    Equivalent to asyncio.sleep(), converted to time.sleep() by async_to_sync.
+    """
+    return asyncio.sleep(seconds)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool/_compat.py 
new/psycopg-pool-3.2.1/psycopg_pool/_compat.py
--- old/psycopg-pool-3.2.0/psycopg_pool/_compat.py      2023-11-11 
16:14:26.000000000 +0100
+++ new/psycopg-pool-3.2.1/psycopg_pool/_compat.py      2024-01-07 
13:24:30.000000000 +0100
@@ -14,9 +14,21 @@
 else:
     from typing import Counter, Deque
 
+if sys.version_info >= (3, 11):
+    from typing import Self
+else:
+    from typing_extensions import Self
+
+if sys.version_info >= (3, 13):
+    from typing import TypeVar
+else:
+    from typing_extensions import TypeVar
+
 __all__ = [
     "Counter",
     "Deque",
+    "Self",
+    "TypeVar",
 ]
 
 # Workaround for psycopg < 3.0.8.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool/abc.py 
new/psycopg-pool-3.2.1/psycopg_pool/abc.py
--- old/psycopg-pool-3.2.0/psycopg_pool/abc.py  2023-11-11 16:14:26.000000000 
+0100
+++ new/psycopg-pool-3.2.1/psycopg_pool/abc.py  2024-01-07 13:24:30.000000000 
+0100
@@ -6,18 +6,21 @@
 
 from __future__ import annotations
 
-from typing import Any, Awaitable, Callable, TypeVar, Union, TYPE_CHECKING
+from typing import Any, Awaitable, Callable, Union, TYPE_CHECKING
 
 from typing_extensions import TypeAlias
 
+from ._compat import TypeVar
+
 if TYPE_CHECKING:
     from .pool import ConnectionPool
     from .pool_async import AsyncConnectionPool
-    from psycopg import Connection, AsyncConnection
+    from psycopg import Connection, AsyncConnection  # noqa: F401
+    from psycopg.rows import TupleRow  # noqa: F401
 
 # Connection types to make the pool generic
-CT = TypeVar("CT", bound="Connection[Any]")
-ACT = TypeVar("ACT", bound="AsyncConnection[Any]")
+CT = TypeVar("CT", bound="Connection[Any]", default="Connection[TupleRow]")
+ACT = TypeVar("ACT", bound="AsyncConnection[Any]", 
default="AsyncConnection[TupleRow]")
 
 # Callbacks taking a connection from the pool
 ConnectionCB: TypeAlias = Callable[[CT], None]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool/base.py 
new/psycopg-pool-3.2.1/psycopg_pool/base.py
--- old/psycopg-pool-3.2.0/psycopg_pool/base.py 2023-11-11 16:14:26.000000000 
+0100
+++ new/psycopg-pool-3.2.1/psycopg_pool/base.py 2024-01-07 13:24:30.000000000 
+0100
@@ -200,22 +200,24 @@
         conn._expire_at = monotonic() + self._jitter(self.max_lifetime, -0.05, 
0.0)
 
 
-class ConnectionAttempt:
-    """Keep the state of a connection attempt."""
+class AttemptWithBackoff:
+    """
+    Keep the state of a repeated operation attempt with exponential backoff.
+    """
 
     INITIAL_DELAY = 1.0
     DELAY_JITTER = 0.1
     DELAY_BACKOFF = 2.0
 
-    def __init__(self, *, reconnect_timeout: float):
-        self.reconnect_timeout = reconnect_timeout
+    def __init__(self, *, timeout: float):
+        self.timeout = timeout
         self.delay = 0.0
         self.give_up_at = 0.0
 
     def update_delay(self, now: float) -> None:
         """Calculate how long to wait for a new connection attempt"""
         if self.delay == 0.0:
-            self.give_up_at = now + self.reconnect_timeout
+            self.give_up_at = now + self.timeout
             self.delay = BasePool._jitter(
                 self.INITIAL_DELAY, -self.DELAY_JITTER, self.DELAY_JITTER
             )
@@ -226,5 +228,5 @@
             self.delay = max(0.0, self.give_up_at - now)
 
     def time_to_give_up(self, now: float) -> bool:
-        """Return True if we are tired of trying to connect. Meh."""
+        """Return True if we are tired of trying this attempt. Meh."""
         return self.give_up_at > 0.0 and now >= self.give_up_at
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool/null_pool.py 
new/psycopg-pool-3.2.1/psycopg_pool/null_pool.py
--- old/psycopg-pool-3.2.0/psycopg_pool/null_pool.py    2023-11-11 
16:14:26.000000000 +0100
+++ new/psycopg-pool-3.2.1/psycopg_pool/null_pool.py    2024-01-07 
13:24:30.000000000 +0100
@@ -10,11 +10,10 @@
 from __future__ import annotations
 
 import logging
-from typing import Any, cast, Dict, Optional, overload, Type
+from typing import Any, cast, Dict, Optional, Type
 
 from psycopg import Connection
 from psycopg.pq import TransactionStatus
-from psycopg.rows import TupleRow
 
 from .abc import CT, ConnectionCB, ConnectFailedCB
 from .errors import PoolTimeout, TooManyRequests
@@ -27,53 +26,6 @@
 
 
 class NullConnectionPool(_BaseNullConnectionPool, ConnectionPool[CT]):
-    @overload
-    def __init__(
-        self: NullConnectionPool[Connection[TupleRow]],
-        conninfo: str = "",
-        *,
-        kwargs: Optional[Dict[str, Any]] = ...,
-        min_size: int = ...,
-        max_size: Optional[int] = ...,
-        open: bool | None = ...,
-        configure: Optional[ConnectionCB[CT]] = ...,
-        check: Optional[ConnectionCB[CT]] = ...,
-        reset: Optional[ConnectionCB[CT]] = ...,
-        name: Optional[str] = ...,
-        timeout: float = ...,
-        max_waiting: int = ...,
-        max_lifetime: float = ...,
-        max_idle: float = ...,
-        reconnect_timeout: float = ...,
-        reconnect_failed: Optional[ConnectFailedCB] = ...,
-        num_workers: int = ...,
-    ):
-        ...
-
-    @overload
-    def __init__(
-        self: NullConnectionPool[CT],
-        conninfo: str = "",
-        *,
-        connection_class: Type[CT] = ...,
-        kwargs: Optional[Dict[str, Any]] = ...,
-        min_size: int = ...,
-        max_size: Optional[int] = ...,
-        open: bool | None = ...,
-        configure: Optional[ConnectionCB[CT]] = ...,
-        check: Optional[ConnectionCB[CT]] = ...,
-        reset: Optional[ConnectionCB[CT]] = ...,
-        name: Optional[str] = ...,
-        timeout: float = ...,
-        max_waiting: int = ...,
-        max_lifetime: float = ...,
-        max_idle: float = ...,
-        reconnect_timeout: float = ...,
-        reconnect_failed: Optional[ConnectFailedCB] = ...,
-        num_workers: int = ...,
-    ):
-        ...
-
     def __init__(
         self,
         conninfo: str = "",
@@ -144,6 +96,9 @@
         logger.info("pool %r is ready to use", self.name)
 
     def _get_ready_connection(self, timeout: Optional[float]) -> Optional[CT]:
+        if timeout is not None and timeout <= 0.0:
+            raise PoolTimeout()
+
         conn: Optional[CT] = None
         if self.max_size == 0 or self._nconns < self.max_size:
             # Create a new connection for the client
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool/null_pool_async.py 
new/psycopg-pool-3.2.1/psycopg_pool/null_pool_async.py
--- old/psycopg-pool-3.2.0/psycopg_pool/null_pool_async.py      2023-11-11 
16:14:26.000000000 +0100
+++ new/psycopg-pool-3.2.1/psycopg_pool/null_pool_async.py      2024-01-07 
13:24:30.000000000 +0100
@@ -7,11 +7,10 @@
 from __future__ import annotations
 
 import logging
-from typing import Any, cast, Dict, Optional, overload, Type
+from typing import Any, cast, Dict, Optional, Type
 
 from psycopg import AsyncConnection
 from psycopg.pq import TransactionStatus
-from psycopg.rows import TupleRow
 
 from .abc import ACT, AsyncConnectionCB, AsyncConnectFailedCB
 from .errors import PoolTimeout, TooManyRequests
@@ -24,53 +23,6 @@
 
 
 class AsyncNullConnectionPool(_BaseNullConnectionPool, 
AsyncConnectionPool[ACT]):
-    @overload
-    def __init__(
-        self: AsyncNullConnectionPool[AsyncConnection[TupleRow]],
-        conninfo: str = "",
-        *,
-        kwargs: Optional[Dict[str, Any]] = ...,
-        min_size: int = ...,
-        max_size: Optional[int] = ...,
-        open: bool | None = ...,
-        configure: Optional[AsyncConnectionCB[ACT]] = ...,
-        check: Optional[AsyncConnectionCB[ACT]] = ...,
-        reset: Optional[AsyncConnectionCB[ACT]] = ...,
-        name: Optional[str] = ...,
-        timeout: float = ...,
-        max_waiting: int = ...,
-        max_lifetime: float = ...,
-        max_idle: float = ...,
-        reconnect_timeout: float = ...,
-        reconnect_failed: Optional[AsyncConnectFailedCB] = ...,
-        num_workers: int = ...,
-    ):
-        ...
-
-    @overload
-    def __init__(
-        self: AsyncNullConnectionPool[ACT],
-        conninfo: str = "",
-        *,
-        connection_class: Type[ACT] = ...,
-        kwargs: Optional[Dict[str, Any]] = ...,
-        min_size: int = ...,
-        max_size: Optional[int] = ...,
-        open: bool | None = ...,
-        configure: Optional[AsyncConnectionCB[ACT]] = ...,
-        check: Optional[AsyncConnectionCB[ACT]] = ...,
-        reset: Optional[AsyncConnectionCB[ACT]] = ...,
-        name: Optional[str] = ...,
-        timeout: float = ...,
-        max_waiting: int = ...,
-        max_lifetime: float = ...,
-        max_idle: float = ...,
-        reconnect_timeout: float = ...,
-        reconnect_failed: Optional[AsyncConnectFailedCB] = ...,
-        num_workers: int = ...,
-    ):
-        ...
-
     def __init__(
         self,
         conninfo: str = "",
@@ -141,6 +93,9 @@
         logger.info("pool %r is ready to use", self.name)
 
     async def _get_ready_connection(self, timeout: Optional[float]) -> 
Optional[ACT]:
+        if timeout is not None and timeout <= 0.0:
+            raise PoolTimeout()
+
         conn: Optional[ACT] = None
         if self.max_size == 0 or self._nconns < self.max_size:
             # Create a new connection for the client
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool/pool.py 
new/psycopg-pool-3.2.1/psycopg_pool/pool.py
--- old/psycopg-pool-3.2.0/psycopg_pool/pool.py 2023-11-11 16:14:26.000000000 
+0100
+++ new/psycopg-pool-3.2.1/psycopg_pool/pool.py 2024-01-07 13:24:30.000000000 
+0100
@@ -15,21 +15,20 @@
 from time import monotonic
 from types import TracebackType
 from typing import Any, Iterator, cast, Dict, Generic, List
-from typing import Optional, overload, Sequence, Type, TypeVar
+from typing import Optional, Sequence, Type
 from weakref import ref
 from contextlib import contextmanager
 
 from psycopg import errors as e
 from psycopg import Connection
 from psycopg.pq import TransactionStatus
-from psycopg.rows import TupleRow
 
 from .abc import CT, ConnectionCB, ConnectFailedCB
-from .base import ConnectionAttempt, BasePool
+from .base import AttemptWithBackoff, BasePool
 from .errors import PoolClosed, PoolTimeout, TooManyRequests
-from ._compat import Deque
+from ._compat import Deque, Self
 from ._acompat import Condition, Event, Lock, Queue, Worker, spawn, gather
-from ._acompat import current_thread_name
+from ._acompat import sleep, current_thread_name
 from .sched import Scheduler
 
 
@@ -37,56 +36,8 @@
 
 
 class ConnectionPool(Generic[CT], BasePool):
-    _Self = TypeVar("_Self", bound="ConnectionPool[CT]")
     _pool: Deque[CT]
 
-    @overload
-    def __init__(
-        self: ConnectionPool[Connection[TupleRow]],
-        conninfo: str = "",
-        *,
-        kwargs: Optional[Dict[str, Any]] = ...,
-        min_size: int = ...,
-        max_size: Optional[int] = ...,
-        open: bool | None = ...,
-        configure: Optional[ConnectionCB[CT]] = ...,
-        check: Optional[ConnectionCB[CT]] = ...,
-        reset: Optional[ConnectionCB[CT]] = ...,
-        name: Optional[str] = ...,
-        timeout: float = ...,
-        max_waiting: int = ...,
-        max_lifetime: float = ...,
-        max_idle: float = ...,
-        reconnect_timeout: float = ...,
-        reconnect_failed: Optional[ConnectFailedCB] = ...,
-        num_workers: int = ...,
-    ):
-        ...
-
-    @overload
-    def __init__(
-        self: ConnectionPool[CT],
-        conninfo: str = "",
-        *,
-        connection_class: Type[CT] = ...,
-        kwargs: Optional[Dict[str, Any]] = ...,
-        min_size: int = ...,
-        max_size: Optional[int] = ...,
-        open: bool | None = ...,
-        configure: Optional[ConnectionCB[CT]] = ...,
-        check: Optional[ConnectionCB[CT]] = ...,
-        reset: Optional[ConnectionCB[CT]] = ...,
-        name: Optional[str] = ...,
-        timeout: float = ...,
-        max_waiting: int = ...,
-        max_lifetime: float = ...,
-        max_idle: float = ...,
-        reconnect_timeout: float = ...,
-        reconnect_failed: Optional[ConnectFailedCB] = ...,
-        num_workers: int = ...,
-    ):
-        ...
-
     def __init__(
         self,
         conninfo: str = "",
@@ -236,10 +187,9 @@
         failing to do so will deplete the pool. A depleted pool is a sad pool:
         you don't want a depleted pool.
         """
-        t0 = monotonic()
         if timeout is None:
             timeout = self.timeout
-        deadline = t0 + timeout
+        deadline = monotonic() + timeout
 
         logger.info("connection requested from %r", self.name)
         self._stats[self._REQUESTS_NUM] += 1
@@ -247,15 +197,7 @@
         self._check_open_getconn()
 
         try:
-            while True:
-                conn = self._getconn_unchecked(deadline - monotonic())
-                try:
-                    self._check_connection(conn)
-                except Exception:
-                    self._putconn(conn, from_getconn=True)
-                else:
-                    logger.info("connection given by %r", self.name)
-                    return conn
+            return self._getconn_with_check_loop(deadline)
         # Re-raise the timeout exception presenting the user the global
         # timeout, not the per-attempt one.
         except PoolTimeout:
@@ -263,6 +205,32 @@
                 f"couldn't get a connection after {timeout:.2f} sec"
             ) from None
 
+    def _getconn_with_check_loop(self, deadline: float) -> CT:
+        attempt: AttemptWithBackoff | None = None
+
+        while True:
+            conn = self._getconn_unchecked(deadline - monotonic())
+            try:
+                self._check_connection(conn)
+            except Exception:
+                self._putconn(conn, from_getconn=True)
+            else:
+                logger.info("connection given by %r", self.name)
+                return conn
+
+            # Delay further checks to avoid a busy loop, using the same
+            # backoff policy used in reconnection attempts.
+            now = monotonic()
+            if not attempt:
+                attempt = AttemptWithBackoff(timeout=deadline - now)
+            else:
+                attempt.update_delay(now)
+
+            if attempt.time_to_give_up(now):
+                raise PoolTimeout()
+            else:
+                sleep(attempt.delay)
+
     def _getconn_unchecked(self, timeout: float) -> CT:
         # Critical section: decide here if there's a connection ready
         # or if the client needs to wait.
@@ -298,6 +266,9 @@
 
     def _get_ready_connection(self, timeout: Optional[float]) -> Optional[CT]:
         """Return a connection, if the client deserves one."""
+        if timeout is not None and timeout <= 0.0:
+            raise PoolTimeout()
+
         conn: Optional[CT] = None
         if self._pool:
             # Take a connection ready out of the pool
@@ -491,7 +462,7 @@
         sched_runner, self._sched_runner = (self._sched_runner, None)
         gather(sched_runner, *workers, timeout=timeout)
 
-    def __enter__(self: _Self) -> _Self:
+    def __enter__(self) -> Self:
         self._open_implicit = False
         self.open()
         return self
@@ -626,7 +597,7 @@
             kwargs["connect_timeout"] = max(round(timeout), 1)
         t0 = monotonic()
         try:
-            conn: CT = cast(CT, self.connection_class.connect(self.conninfo, 
**kwargs))
+            conn = self.connection_class.connect(self.conninfo, **kwargs)
         except Exception:
             self._stats[self._CONNECTIONS_ERRORS] += 1
             raise
@@ -651,7 +622,7 @@
         return conn
 
     def _add_connection(
-        self, attempt: Optional[ConnectionAttempt], growing: bool = False
+        self, attempt: Optional[AttemptWithBackoff], growing: bool = False
     ) -> None:
         """Try to connect and add the connection to the pool.
 
@@ -662,7 +633,7 @@
         """
         now = monotonic()
         if not attempt:
-            attempt = 
ConnectionAttempt(reconnect_timeout=self.reconnect_timeout)
+            attempt = AttemptWithBackoff(timeout=self.reconnect_timeout)
 
         try:
             conn = self._connect()
@@ -957,7 +928,7 @@
     def __init__(
         self,
         pool: ConnectionPool[Any],
-        attempt: Optional[ConnectionAttempt] = None,
+        attempt: Optional[AttemptWithBackoff] = None,
         growing: bool = False,
     ):
         super().__init__(pool)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool/pool_async.py 
new/psycopg-pool-3.2.1/psycopg_pool/pool_async.py
--- old/psycopg-pool-3.2.0/psycopg_pool/pool_async.py   2023-11-11 
16:14:26.000000000 +0100
+++ new/psycopg-pool-3.2.1/psycopg_pool/pool_async.py   2024-01-07 
13:24:30.000000000 +0100
@@ -12,21 +12,20 @@
 from time import monotonic
 from types import TracebackType
 from typing import Any, AsyncIterator, cast, Dict, Generic, List
-from typing import Optional, overload, Sequence, Type, TypeVar
+from typing import Optional, Sequence, Type
 from weakref import ref
 from contextlib import asynccontextmanager
 
 from psycopg import errors as e
 from psycopg import AsyncConnection
 from psycopg.pq import TransactionStatus
-from psycopg.rows import TupleRow
 
 from .abc import ACT, AsyncConnectionCB, AsyncConnectFailedCB
-from .base import ConnectionAttempt, BasePool
+from .base import AttemptWithBackoff, BasePool
 from .errors import PoolClosed, PoolTimeout, TooManyRequests
-from ._compat import Deque
+from ._compat import Deque, Self
 from ._acompat import ACondition, AEvent, ALock, AQueue, AWorker, aspawn, 
agather
-from ._acompat import current_task_name
+from ._acompat import asleep, current_task_name
 from .sched_async import AsyncScheduler
 
 if True:  # ASYNC
@@ -36,56 +35,8 @@
 
 
 class AsyncConnectionPool(Generic[ACT], BasePool):
-    _Self = TypeVar("_Self", bound="AsyncConnectionPool[ACT]")
     _pool: Deque[ACT]
 
-    @overload
-    def __init__(
-        self: AsyncConnectionPool[AsyncConnection[TupleRow]],
-        conninfo: str = "",
-        *,
-        kwargs: Optional[Dict[str, Any]] = ...,
-        min_size: int = ...,
-        max_size: Optional[int] = ...,
-        open: bool | None = ...,
-        configure: Optional[AsyncConnectionCB[ACT]] = ...,
-        check: Optional[AsyncConnectionCB[ACT]] = ...,
-        reset: Optional[AsyncConnectionCB[ACT]] = ...,
-        name: Optional[str] = ...,
-        timeout: float = ...,
-        max_waiting: int = ...,
-        max_lifetime: float = ...,
-        max_idle: float = ...,
-        reconnect_timeout: float = ...,
-        reconnect_failed: Optional[AsyncConnectFailedCB] = ...,
-        num_workers: int = ...,
-    ):
-        ...
-
-    @overload
-    def __init__(
-        self: AsyncConnectionPool[ACT],
-        conninfo: str = "",
-        *,
-        connection_class: Type[ACT] = ...,
-        kwargs: Optional[Dict[str, Any]] = ...,
-        min_size: int = ...,
-        max_size: Optional[int] = ...,
-        open: bool | None = ...,
-        configure: Optional[AsyncConnectionCB[ACT]] = ...,
-        check: Optional[AsyncConnectionCB[ACT]] = ...,
-        reset: Optional[AsyncConnectionCB[ACT]] = ...,
-        name: Optional[str] = ...,
-        timeout: float = ...,
-        max_waiting: int = ...,
-        max_lifetime: float = ...,
-        max_idle: float = ...,
-        reconnect_timeout: float = ...,
-        reconnect_failed: Optional[AsyncConnectFailedCB] = ...,
-        num_workers: int = ...,
-    ):
-        ...
-
     def __init__(
         self,
         conninfo: str = "",
@@ -256,10 +207,9 @@
         failing to do so will deplete the pool. A depleted pool is a sad pool:
         you don't want a depleted pool.
         """
-        t0 = monotonic()
         if timeout is None:
             timeout = self.timeout
-        deadline = t0 + timeout
+        deadline = monotonic() + timeout
 
         logger.info("connection requested from %r", self.name)
         self._stats[self._REQUESTS_NUM] += 1
@@ -267,15 +217,7 @@
         self._check_open_getconn()
 
         try:
-            while True:
-                conn = await self._getconn_unchecked(deadline - monotonic())
-                try:
-                    await self._check_connection(conn)
-                except Exception:
-                    await self._putconn(conn, from_getconn=True)
-                else:
-                    logger.info("connection given by %r", self.name)
-                    return conn
+            return await self._getconn_with_check_loop(deadline)
 
         # Re-raise the timeout exception presenting the user the global
         # timeout, not the per-attempt one.
@@ -284,6 +226,32 @@
                 f"couldn't get a connection after {timeout:.2f} sec"
             ) from None
 
+    async def _getconn_with_check_loop(self, deadline: float) -> ACT:
+        attempt: AttemptWithBackoff | None = None
+
+        while True:
+            conn = await self._getconn_unchecked(deadline - monotonic())
+            try:
+                await self._check_connection(conn)
+            except Exception:
+                await self._putconn(conn, from_getconn=True)
+            else:
+                logger.info("connection given by %r", self.name)
+                return conn
+
+            # Delay further checks to avoid a busy loop, using the same
+            # backoff policy used in reconnection attempts.
+            now = monotonic()
+            if not attempt:
+                attempt = AttemptWithBackoff(timeout=deadline - now)
+            else:
+                attempt.update_delay(now)
+
+            if attempt.time_to_give_up(now):
+                raise PoolTimeout()
+            else:
+                await asleep(attempt.delay)
+
     async def _getconn_unchecked(self, timeout: float) -> ACT:
         # Critical section: decide here if there's a connection ready
         # or if the client needs to wait.
@@ -319,6 +287,9 @@
 
     async def _get_ready_connection(self, timeout: Optional[float]) -> 
Optional[ACT]:
         """Return a connection, if the client deserves one."""
+        if timeout is not None and timeout <= 0.0:
+            raise PoolTimeout()
+
         conn: Optional[ACT] = None
         if self._pool:
             # Take a connection ready out of the pool
@@ -519,7 +490,7 @@
         sched_runner, self._sched_runner = self._sched_runner, None
         await agather(sched_runner, *workers, timeout=timeout)
 
-    async def __aenter__(self: _Self) -> _Self:
+    async def __aenter__(self) -> Self:
         self._open_implicit = False
         await self.open()
         return self
@@ -670,9 +641,7 @@
             kwargs["connect_timeout"] = max(round(timeout), 1)
         t0 = monotonic()
         try:
-            conn: ACT = cast(
-                ACT, await self.connection_class.connect(self.conninfo, 
**kwargs)
-            )
+            conn = await self.connection_class.connect(self.conninfo, **kwargs)
         except Exception:
             self._stats[self._CONNECTIONS_ERRORS] += 1
             raise
@@ -697,7 +666,7 @@
         return conn
 
     async def _add_connection(
-        self, attempt: Optional[ConnectionAttempt], growing: bool = False
+        self, attempt: Optional[AttemptWithBackoff], growing: bool = False
     ) -> None:
         """Try to connect and add the connection to the pool.
 
@@ -708,7 +677,7 @@
         """
         now = monotonic()
         if not attempt:
-            attempt = 
ConnectionAttempt(reconnect_timeout=self.reconnect_timeout)
+            attempt = AttemptWithBackoff(timeout=self.reconnect_timeout)
 
         try:
             conn = await self._connect()
@@ -1008,7 +977,7 @@
     def __init__(
         self,
         pool: AsyncConnectionPool[Any],
-        attempt: Optional[ConnectionAttempt] = None,
+        attempt: Optional[AttemptWithBackoff] = None,
         growing: bool = False,
     ):
         super().__init__(pool)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/psycopg_pool.egg-info/PKG-INFO 
new/psycopg-pool-3.2.1/psycopg_pool.egg-info/PKG-INFO
--- old/psycopg-pool-3.2.0/psycopg_pool.egg-info/PKG-INFO       2023-11-11 
16:14:36.000000000 +0100
+++ new/psycopg-pool-3.2.1/psycopg_pool.egg-info/PKG-INFO       2024-01-07 
13:24:41.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: psycopg-pool
-Version: 3.2.0
+Version: 3.2.1
 Summary: Connection Pool for Psycopg
 Home-page: https://psycopg.org/psycopg3/
 Author: Daniele Varrazzo
@@ -24,6 +24,8 @@
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Database
 Classifier: Topic :: Database :: Front-Ends
 Classifier: Topic :: Software Development
@@ -31,7 +33,7 @@
 Requires-Python: >=3.8
 Description-Content-Type: text/x-rst
 License-File: LICENSE.txt
-Requires-Dist: typing-extensions>=3.10
+Requires-Dist: typing-extensions>=4.4
 
 Psycopg 3: PostgreSQL database adapter for Python - Connection Pool
 ===================================================================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/psycopg-pool-3.2.0/psycopg_pool.egg-info/requires.txt 
new/psycopg-pool-3.2.1/psycopg_pool.egg-info/requires.txt
--- old/psycopg-pool-3.2.0/psycopg_pool.egg-info/requires.txt   2023-11-11 
16:14:36.000000000 +0100
+++ new/psycopg-pool-3.2.1/psycopg_pool.egg-info/requires.txt   2024-01-07 
13:24:41.000000000 +0100
@@ -1 +1 @@
-typing-extensions>=3.10
+typing-extensions>=4.4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/psycopg-pool-3.2.0/setup.cfg 
new/psycopg-pool-3.2.1/setup.cfg
--- old/psycopg-pool-3.2.0/setup.cfg    2023-11-11 16:14:36.804819000 +0100
+++ new/psycopg-pool-3.2.1/setup.cfg    2024-01-07 13:24:41.375857400 +0100
@@ -5,7 +5,7 @@
 author = Daniele Varrazzo
 author_email = daniele.varra...@gmail.com
 license = GNU Lesser General Public License v3 (LGPLv3)
-version = 3.2.0
+version = 3.2.1
 project_urls = 
        Homepage = https://psycopg.org/
        Documentation = https://www.psycopg.org/psycopg3/docs/advanced/pool.html
@@ -26,6 +26,8 @@
        Programming Language :: Python :: 3.10
        Programming Language :: Python :: 3.11
        Programming Language :: Python :: 3.12
+       Programming Language :: Python :: Implementation :: CPython
+       Programming Language :: Python :: Implementation :: PyPy
        Topic :: Database
        Topic :: Database :: Front-Ends
        Topic :: Software Development
@@ -39,7 +41,7 @@
 packages = find:
 zip_safe = False
 install_requires = 
-       typing-extensions >= 3.10
+       typing-extensions >= 4.4
 
 [options.package_data]
 psycopg_pool = py.typed

Reply via email to