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 <[email protected]>
+
+- 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 = [email protected]
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