pgconn.notice_handler = partial(BaseConnection._notice_handler, wself)
pgconn.notify_handler = partial(BaseConnection._notify_handler, wself)
- self._pool: Optional["ConnectionPool"] = None
+ # Attribute is only set if the connection is from a pool so we can tell
+ # apart a connection in the pool too (when _pool = None)
+ self._pool: Optional["ConnectionPool"]
def __del__(self) -> None:
# If fails on connection we might not have this attribute yet
if not hasattr(self, "pgconn"):
return
- status = self.pgconn.transaction_status
- if status == TransactionStatus.UNKNOWN:
+ # Connection correctly closed
+ if self.closed:
+ return
+
+ # Connection in a pool so terminating with the program is normal
+ if hasattr(self, "_pool"):
return
- status = TransactionStatus(status)
warnings.warn(
f"connection {self} was deleted while still open."
f" Please use 'with' or '.close()' to close the connection",
self.commit()
# Close the connection only if it doesn't belong to a pool.
- if not self._pool:
+ if not getattr(self, "_pool", None):
self.close()
def close(self) -> None:
return conn
def putconn(self, conn: Connection) -> None:
- if conn._pool is not self:
- if conn._pool:
- msg = f"it comes from pool {conn._pool.name!r}"
+ pool = getattr(conn, "_pool", None)
+ if pool is not self:
+ if pool:
+ msg = f"it comes from pool {pool.name!r}"
else:
msg = "it doesn't come from any pool"
raise ValueError(
def _return_connection(self, conn: Connection) -> None:
# Remove the pool reference from the connection before returning it
# to the state, to avoid to create a reference loop.
+ # Also disable the warning for open connection in conn.__del__
conn._pool = None
self._reset_transaction_status(conn)
if isinstance(task, StopWorker):
return
+ # delete reference loops which may keep the pool alive
+ del task.pool
+ del task
+
def _connect(self) -> Connection:
"""Return a connection configured for the pool."""
conn = Connection.connect(self.conninfo, **self.kwargs)
logger.debug("task running: %s", self)
def __repr__(self) -> str:
- return f"{self.__class__.__name__}({self.pool.name})"
+ return (
+ f"<{self.__class__.__name__} {self.pool.name!r} at 0x{id(self):x}>"
+ )
class StopWorker(MaintenanceTask):
super().__call__()
conn = self.pool._connect()
- conn._pool = self.pool # make it acceptable
+ conn._pool = self.pool # make it accepted by the pool
self.pool.putconn(conn)
import logging
-from time import time
+import weakref
+from time import time, sleep
from threading import Thread
import pytest
conn = p1.getconn()
with pytest.raises(ValueError):
p2.putconn(conn)
+
+
+def test_del_no_warning(dsn, recwarn):
+ p = pool.ConnectionPool(minconn=2)
+ with p.connection() as conn:
+ conn.execute("select 1")
+
+ while len(p._pool) < p.minconn:
+ sleep(0.01)
+
+ ref = weakref.ref(p)
+ del p
+ assert not ref()
+ assert not recwarn