From: Daniele Varrazzo Date: Sun, 27 Dec 2020 04:18:27 +0000 (+0100) Subject: Raise a resource warning if a connection is deleted while open X-Git-Tag: 3.0.dev0~243 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=85cf9b9064b981b41ccc624b9d5ff397c167ba58;p=thirdparty%2Fpsycopg.git Raise a resource warning if a connection is deleted while open --- diff --git a/psycopg3/psycopg3/connection.py b/psycopg3/psycopg3/connection.py index 319c3179a..a5c9aa529 100644 --- a/psycopg3/psycopg3/connection.py +++ b/psycopg3/psycopg3/connection.py @@ -7,6 +7,7 @@ psycopg3 connection objects import sys import asyncio import logging +import warnings import threading from types import TracebackType from typing import Any, AsyncIterator, Callable, Iterator, List, NamedTuple @@ -122,6 +123,22 @@ class BaseConnection(AdaptContext): pgconn.notice_handler = partial(BaseConnection._notice_handler, wself) pgconn.notify_handler = partial(BaseConnection._notify_handler, wself) + def __del__(self) -> None: + status = self.pgconn.transaction_status + if status == TransactionStatus.UNKNOWN: + return + + elif status == TransactionStatus.INTRANS: + msg = ( + f"connection {self} was deleted with an open transaction," + " changes discarded by the server" + ) + else: + status = TransactionStatus(status) # in case we got an int + msg = f"connection {self} was deleted open in status {status.name}" + + warnings.warn(msg, ResourceWarning) + @property def closed(self) -> bool: """`True` if the connection is closed.""" diff --git a/tests/test_connection.py b/tests/test_connection.py index 0be39e0e9..62366fbe8 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -72,6 +72,35 @@ def test_close(conn): cur.execute("select 1") +def test_connection_warn_close(dsn, recwarn): + conn = Connection.connect(dsn) + conn.close() + del conn + assert not recwarn + + conn = Connection.connect(dsn) + del conn + assert "IDLE" in str(recwarn.pop(ResourceWarning).message) + + conn = Connection.connect(dsn) + conn.execute("select 1") + del conn + assert "discarded" in str(recwarn.pop(ResourceWarning).message) + + conn = Connection.connect(dsn) + try: + conn.execute("select wat") + except Exception: + pass + del conn + assert "INERROR" in str(recwarn.pop(ResourceWarning).message) + + with Connection.connect(dsn) as conn: + pass + del conn + assert not recwarn + + def test_context_commit(conn, dsn): with conn: with conn.cursor() as cur: diff --git a/tests/test_connection_async.py b/tests/test_connection_async.py index 2d57d9c0f..206fda2f6 100644 --- a/tests/test_connection_async.py +++ b/tests/test_connection_async.py @@ -77,6 +77,35 @@ async def test_close(aconn): await cur.execute("select 1") +async def test_connection_warn_close(dsn, recwarn): + conn = await AsyncConnection.connect(dsn) + await conn.close() + del conn + assert not recwarn + + conn = await AsyncConnection.connect(dsn) + del conn + assert "IDLE" in str(recwarn.pop(ResourceWarning).message) + + conn = await AsyncConnection.connect(dsn) + await conn.execute("select 1") + del conn + assert "discarded" in str(recwarn.pop(ResourceWarning).message) + + conn = await AsyncConnection.connect(dsn) + try: + await conn.execute("select wat") + except Exception: + pass + del conn + assert "INERROR" in str(recwarn.pop(ResourceWarning).message) + + async with await AsyncConnection.connect(dsn) as conn: + pass + del conn + assert not recwarn + + async def test_context_commit(aconn, dsn): async with aconn: async with await aconn.cursor() as cur: