From 02bad367d6af8c3c08b594575600e4dd996c9c69 Mon Sep 17 00:00:00 2001 From: Xia Jiang Date: Mon, 26 Oct 2026 09:54:39 +0800 Subject: [PATCH] gh-160683: Stop half-closing the remote debugging interrupt socket --- Lib/pdb.py | 1 - Lib/test/test_remote_pdb.py | 35 +++++++++++++++++++ ...-10-26-03-58-12.gh-issue-160683.Kz3q1P.rst | 5 +++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-26-03-58-12.gh-issue-160683.Kz3q1P.rst diff --git a/Lib/pdb.py b/Lib/pdb.py index 114634a4469c131..2b90bb3284e62d1 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -3006,7 +3006,6 @@ def listener(sock): # Check if the interpreter is finalizing every quarter of a second. # Clean up and exit if so. sock.settimeout(0.25) - sock.shutdown(socket.SHUT_WR) while not shut_down.is_set(): try: data = sock.recv(1024) diff --git a/Lib/test/test_remote_pdb.py b/Lib/test/test_remote_pdb.py index e5387526090536d..87dcbcdf29bc4b2 100644 --- a/Lib/test/test_remote_pdb.py +++ b/Lib/test/test_remote_pdb.py @@ -835,6 +835,41 @@ def test_reading_empty_json_during_completion(self): ) +@requires_subprocess() +class SignalListenerTestCase(unittest.TestCase): + def test_interrupt_socket_is_not_half_closed(self): + # gh-160683: half-closing leaves the connection in FIN_WAIT_2, which + # Windows tears down after 120 seconds, so a session that has been + # idle that long loses its interrupt channel. + # The listener thread is kept alive by an atexit callback and cannot be + # joined, so start it in a subprocess to leave this interpreter's + # thread bookkeeping untouched. + with closing(socket.socket()) as listener: + listener.bind(("127.0.0.1", 0)) + listener.listen(1) + listener.settimeout(SHORT_TIMEOUT) + code = textwrap.dedent(f""" + import sys + from pdb import _PdbServer + + _PdbServer._start_signal_listener({listener.getsockname()}) + sys.stdin.read() + """) + with subprocess.Popen( + [sys.executable, "-c", code], stdin=subprocess.PIPE + ) as process: + with closing(listener.accept()[0]) as conn: + conn.settimeout(1.0) + try: + data = conn.recv(1024) + except TimeoutError: + pass + else: + self.fail( + f"interrupt socket returned {data!r} instead of staying open" + ) + + class RemotePdbTestCase(unittest.TestCase): """Tests for the _PdbServer class.""" diff --git a/Misc/NEWS.d/next/Library/2026-10-26-03-58-12.gh-issue-160683.Kz3q1P.rst b/Misc/NEWS.d/next/Library/2026-10-26-03-58-12.gh-issue-160683.Kz3q1P.rst new file mode 100644 index 000000000000000..5903e0eedeb8828 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-26-03-58-12.gh-issue-160683.Kz3q1P.rst @@ -0,0 +1,5 @@ +Keep the interrupt channel of a remote debugging session open. + +Half-closing it left the connection in FIN_WAIT_2, which Windows resets +after 120 seconds, so an idle session silently lost the ability to +interrupt the debuggee.