- 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
+ pool context is not used. In the future the default will become `!False`
+ (:ticket:`#659`).
psycopg_pool 3.1.9 (unreleased)
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
kwargs: Optional[Dict[str, Any]],
min_size: int,
max_size: Optional[int],
- open: bool,
name: Optional[str],
timeout: float,
max_waiting: int,
self._opened = False
self._closed = True
+ self._open_implicit = False
def __repr__(self) -> str:
return (
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)
self: NullConnectionPool[Connection[TupleRow]],
conninfo: str = "",
*,
- open: bool = ...,
+ open: bool | None = ...,
configure: Optional[ConnectionCB[CT]] = ...,
reset: Optional[ConnectionCB[CT]] = ...,
kwargs: Optional[Dict[str, Any]] = ...,
self: NullConnectionPool[CT],
conninfo: str = "",
*,
- open: bool = ...,
+ open: bool | None = ...,
connection_class: Type[CT],
configure: Optional[ConnectionCB[CT]] = ...,
reset: Optional[ConnectionCB[CT]] = ...,
self,
conninfo: str = "",
*,
- open: bool = True,
+ open: bool | None = None,
connection_class: Type[CT] = cast(Type[CT], Connection),
configure: Optional[ConnectionCB[CT]] = None,
reset: Optional[ConnectionCB[CT]] = None,
self: AsyncNullConnectionPool[AsyncConnection[TupleRow]],
conninfo: str = "",
*,
- open: bool = ...,
+ open: bool | None = ...,
configure: Optional[AsyncConnectionCB[ACT]] = ...,
reset: Optional[AsyncConnectionCB[ACT]] = ...,
kwargs: Optional[Dict[str, Any]] = ...,
self: AsyncNullConnectionPool[ACT],
conninfo: str = "",
*,
- open: bool = ...,
+ open: bool | None = ...,
connection_class: Type[ACT],
configure: Optional[AsyncConnectionCB[ACT]] = ...,
reset: Optional[AsyncConnectionCB[ACT]] = ...,
self,
conninfo: str = "",
*,
- open: bool = True,
+ open: bool | None = None,
connection_class: Type[ACT] = cast(Type[ACT], AsyncConnection),
configure: Optional[AsyncConnectionCB[ACT]] = None,
reset: Optional[AsyncConnectionCB[ACT]] = None,
self: ConnectionPool[Connection[TupleRow]],
conninfo: str = "",
*,
- open: bool = ...,
+ open: bool | None = ...,
configure: Optional[ConnectionCB[CT]] = ...,
reset: Optional[ConnectionCB[CT]] = ...,
kwargs: Optional[Dict[str, Any]] = ...,
self: ConnectionPool[CT],
conninfo: str = "",
*,
- open: bool = ...,
+ open: bool | None = ...,
connection_class: Type[CT],
configure: Optional[ConnectionCB[CT]] = ...,
reset: Optional[ConnectionCB[CT]] = ...,
self,
conninfo: str = "",
*,
- open: bool = True,
+ open: bool | None = None,
connection_class: Type[CT] = cast(Type[CT], Connection),
configure: Optional[ConnectionCB[CT]] = None,
reset: Optional[ConnectionCB[CT]] = None,
kwargs=kwargs,
min_size=min_size,
max_size=max_size,
- open=open,
name=name,
timeout=timeout,
max_waiting=max_waiting,
num_workers=num_workers,
)
+ if open is None:
+ open = self._open_implicit = True
+
if open:
self._open()
gather(sched_runner, *workers, timeout=timeout)
def __enter__(self: _Self) -> _Self:
+ self._open_implicit = False
self.open()
return self
self: AsyncConnectionPool[AsyncConnection[TupleRow]],
conninfo: str = "",
*,
- open: bool = ...,
+ open: bool | None = ...,
configure: Optional[AsyncConnectionCB[ACT]] = ...,
reset: Optional[AsyncConnectionCB[ACT]] = ...,
kwargs: Optional[Dict[str, Any]] = ...,
self: AsyncConnectionPool[ACT],
conninfo: str = "",
*,
- open: bool = ...,
+ open: bool | None = ...,
connection_class: Type[ACT],
configure: Optional[AsyncConnectionCB[ACT]] = ...,
reset: Optional[AsyncConnectionCB[ACT]] = ...,
self,
conninfo: str = "",
*,
- open: bool = True,
+ open: bool | None = None,
connection_class: Type[ACT] = cast(Type[ACT], AsyncConnection),
configure: Optional[AsyncConnectionCB[ACT]] = None,
reset: Optional[AsyncConnectionCB[ACT]] = None,
kwargs=kwargs,
min_size=min_size,
max_size=max_size,
- open=open,
name=name,
timeout=timeout,
max_waiting=max_waiting,
num_workers=num_workers,
)
+ if open is None:
+ open = self._open_implicit = True
+
if open:
self._open()
await agather(sched_runner, *workers, timeout=timeout)
async def __aenter__(self: _Self) -> _Self:
+ self._open_implicit = False
await self.open()
return self
def test_del_no_warning(dsn, recwarn):
- p = pool.ConnectionPool(dsn, min_size=2)
+ p = pool.ConnectionPool(dsn, min_size=2, open=True)
with p.connection() as conn:
conn.execute("select 1")
async def test_del_no_warning(dsn, recwarn):
- p = pool.AsyncConnectionPool(dsn, min_size=2)
+ p = pool.AsyncConnectionPool(dsn, min_size=2, open=True)
async with p.connection() as conn:
await conn.execute("select 1")
def test_defaults(pool_cls, dsn):
with pool_cls(dsn) as p:
+ assert p.open
+ assert not p.closed
assert p.timeout == 30
assert p.max_idle == 10 * 60
assert p.max_lifetime == 60 * 60
assert p.closed
+def test_create_warning(pool_cls, dsn):
+ # No warning on explicit open
+ p = pool_cls(dsn, open=True)
+ try:
+ with p.connection():
+ pass
+ finally:
+ p.close()
+
+ # No warning on explicit close
+ p = pool_cls(dsn, open=False)
+ p.open()
+ try:
+ with p.connection():
+ pass
+ finally:
+ p.close()
+
+ # No warning on context manager
+ with pool_cls(dsn) as p:
+ with p.connection():
+ pass
+
+ # Warning on open not specified
+ with pytest.warns(DeprecationWarning):
+ p = pool_cls(dsn)
+ try:
+ with p.connection():
+ pass
+ finally:
+ p.close()
+
+ # Warning also if open is called explicitly on already implicitly open
+ with pytest.warns(DeprecationWarning):
+ p = pool_cls(dsn)
+ p.open()
+ try:
+ with p.connection():
+ pass
+ finally:
+ p.close()
+
+
def test_wait_closed(pool_cls, dsn):
with pool_cls(dsn) as p:
pass
def test_closed_getconn(pool_cls, dsn):
- p = pool_cls(dsn, min_size=min_size(pool_cls))
+ p = pool_cls(dsn, min_size=min_size(pool_cls), open=True)
assert not p.closed
with p.connection():
pass
def test_close_connection_on_pool_close(pool_cls, dsn):
- p = pool_cls(dsn, min_size=min_size(pool_cls))
+ p = pool_cls(dsn, min_size=min_size(pool_cls), open=True)
with p.connection() as conn:
p.close()
assert conn.closed
e1 = Event()
e2 = Event()
- p = pool_cls(dsn, min_size=min_size(pool_cls), max_size=1)
+ p = pool_cls(dsn, min_size=min_size(pool_cls), max_size=1, open=True)
p.wait()
success: List[str] = []
def test_open_no_op(pool_cls, dsn):
- p = pool_cls(dsn)
+ p = pool_cls(dsn, open=True)
try:
assert not p.closed
p.open()
def test_reopen(pool_cls, dsn):
- p = pool_cls(dsn)
+ p = pool_cls(dsn, open=True)
with p.connection() as conn:
conn.execute("select 1")
p.close()
async def test_defaults(pool_cls, dsn):
async with pool_cls(dsn) as p:
+ assert p.open
+ assert not p.closed
assert p.timeout == 30
assert p.max_idle == 10 * 60
assert p.max_lifetime == 60 * 60
assert p.closed
+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()
+
+ # No warning on explicit close
+ p = pool_cls(dsn, open=False)
+ await p.open()
+ try:
+ async with p.connection():
+ pass
+ finally:
+ await p.close()
+
+ # No warning on context manager
+ async with pool_cls(dsn) as p:
+ async with p.connection():
+ pass
+
+ # Warning on open not specified
+ with pytest.warns(DeprecationWarning):
+ p = pool_cls(dsn)
+ try:
+ async with p.connection():
+ pass
+ finally:
+ await p.close()
+
+ # Warning also if open is called explicitly on already implicitly open
+ with pytest.warns(DeprecationWarning):
+ p = pool_cls(dsn)
+ await p.open()
+ try:
+ async with p.connection():
+ pass
+ finally:
+ await p.close()
+
+
async def test_wait_closed(pool_cls, dsn):
async with pool_cls(dsn) as p:
pass
async def test_closed_getconn(pool_cls, dsn):
- p = pool_cls(dsn, min_size=min_size(pool_cls))
+ p = pool_cls(dsn, min_size=min_size(pool_cls), open=True)
assert not p.closed
async with p.connection():
pass
async def test_close_connection_on_pool_close(pool_cls, dsn):
- p = pool_cls(dsn, min_size=min_size(pool_cls))
+ p = pool_cls(dsn, min_size=min_size(pool_cls), open=True)
async with p.connection() as conn:
await p.close()
assert conn.closed
e1 = AEvent()
e2 = AEvent()
- p = pool_cls(dsn, min_size=min_size(pool_cls), max_size=1)
+ p = pool_cls(dsn, min_size=min_size(pool_cls), max_size=1, open=True)
await p.wait()
success: List[str] = []
async def test_open_no_op(pool_cls, dsn):
- p = pool_cls(dsn)
+ p = pool_cls(dsn, open=True)
try:
assert not p.closed
await p.open()
async def test_reopen(pool_cls, dsn):
- p = pool_cls(dsn)
+ p = pool_cls(dsn, open=True)
async with p.connection() as conn:
await conn.execute("select 1")
await p.close()