Skip to content

Commit 1452410

Browse files
Pin do_ping cursor-close suppression branches
do_ping's finally narrowly suppresses cursor.close failures on OperationalError, InterfaceError, DqliteConnectionError, OSError, and TimeoutError so a dead-socket close does not crash the probe, while letting programming bugs (ValueError, TypeError, ...) propagate. Tests covered DqliteConnectionError and the ValueError-propagates inverse, but OperationalError, InterfaceError, OSError, BrokenPipeError, and TimeoutError had no direct pin. A refactor dropping any OS-level type would silently re-raise legitimate close-path transport errors and break pool pre-ping. Parametrize across the full suppression tuple; each case asserts do_ping returns True (execute succeeded) and the close failure produces the DEBUG log line carrying the exception type. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 88ae813 commit 1452410

1 file changed

Lines changed: 31 additions & 0 deletions

File tree

tests/test_do_ping_close_narrow.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,34 @@ def test_do_ping_happy_path_is_silent(caplog: pytest.LogCaptureFixture) -> None:
104104
assert dialect.do_ping(conn) is True
105105
messages = [r.getMessage() for r in caplog.records if r.name == "sqlalchemydqlite.base"]
106106
assert not messages, f"happy path must not emit DEBUG logs; got {messages!r}"
107+
108+
109+
@pytest.mark.parametrize(
110+
"close_exc",
111+
[
112+
_dbapi_exc.OperationalError("dead"),
113+
_dbapi_exc.InterfaceError("cursor is closed"),
114+
_client_exc.DqliteConnectionError("peer gone"),
115+
OSError(104, "connection reset"),
116+
BrokenPipeError(32, "broken pipe"),
117+
TimeoutError("read timed out"),
118+
],
119+
)
120+
def test_do_ping_suppresses_close_path_transport_errors(
121+
caplog: pytest.LogCaptureFixture, close_exc: BaseException
122+
) -> None:
123+
"""After ``execute`` succeeded, ``cursor.close`` failing on a
124+
transport error is recoverable — ping returns True and the
125+
failure is DEBUG-logged. Pin every type in the narrow
126+
suppression tuple so a future narrowing (e.g. "OSError covers
127+
it, drop TimeoutError") would fail the test.
128+
"""
129+
caplog.set_level(logging.DEBUG, logger="sqlalchemydqlite.base")
130+
cursor = _CursorExecuteOK(on_close=close_exc) # type: ignore[arg-type]
131+
conn = _FakeConnection(cursor)
132+
dialect = DqliteDialect()
133+
assert dialect.do_ping(conn) is True
134+
messages = [r.getMessage() for r in caplog.records if r.name == "sqlalchemydqlite.base"]
135+
assert any("cursor.close failed" in m for m in messages), messages
136+
# Log line carries the exception type for triage.
137+
assert any(type(close_exc).__name__ in m for m in messages), messages

0 commit comments

Comments
 (0)