https://github.com/python/cpython/commit/0b3e615714a510827fc497e6620879979da41668 commit: 0b3e615714a510827fc497e6620879979da41668 branch: 3.14 author: Miss Islington (bot) <[email protected]> committer: terryjreedy <[email protected]> date: 2026-09-23T01:58:21Z summary:
[3.14] gh-89544: Fix IDLE hang after Restart Shell while receiving output (GH-157644) (#157971) gh-89544: Fix IDLE hang after Restart Shell while receiving output (GH-157644) RPCClient.accept() reinitializes the SocketIO for the new connection, but the receive buffer kept the partially received packet from the old connection, so no message from the new user process was ever complete. (cherry picked from commit b0dda158aad00f0064e220f882792a5bd16ba0a0) Co-authored-by: Serhiy Storchaka <[email protected]> files: A Misc/NEWS.d/next/IDLE/2026-09-17-01-00-00.gh-issue-89544.rstbuf.rst M Lib/idlelib/idle_test/test_rpc.py M Lib/idlelib/rpc.py diff --git a/Lib/idlelib/idle_test/test_rpc.py b/Lib/idlelib/idle_test/test_rpc.py index 81eff398c72f45..15c3ed14b8f6f3 100644 --- a/Lib/idlelib/idle_test/test_rpc.py +++ b/Lib/idlelib/idle_test/test_rpc.py @@ -1,9 +1,28 @@ "Test rpc, coverage 20%." from idlelib import rpc +import socket +import struct import unittest +class SocketIOTest(unittest.TestCase): + + def test_reconnect_discards_partial_packet(self): + # gh-89544: RPCClient.accept() reinitializes the SocketIO after + # a restart; a partially received packet must not be kept. + old_sock, old_peer = socket.socketpair() + new_sock, new_peer = socket.socketpair() + with old_sock, old_peer, new_sock, new_peer: + sockio = rpc.SocketIO(old_sock) + old_peer.sendall(struct.pack('<i', 100) + b'x' * 10) + self.assertIsNone(sockio.pollpacket(1)) + sockio.close() + rpc.SocketIO.__init__(sockio, new_sock) + new_peer.sendall(struct.pack('<i', 3) + b'abc') + self.assertEqual(sockio.pollpacket(1), b'abc') + + class CodePicklerTest(unittest.TestCase): diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py index 3f0b2230dd185d..9af9863d74e0b6 100644 --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -139,6 +139,11 @@ def __init__(self, sock, objtable=None, debugging=None): self.objtable = objtable self.responses = {} self.cvars = {} + # Receive buffer state. A new connection must not inherit a + # partially received packet from the old one (gh-89544). + self.buff = b'' + self.bufneed = 4 + self.bufstate = 0 # meaning: 0 => reading count; 1 => reading data def close(self): sock = self.sock @@ -345,10 +350,6 @@ def putmessage(self, message): raise OSError("socket no longer exists") s = s[n:] - buff = b'' - bufneed = 4 - bufstate = 0 # meaning: 0 => reading count; 1 => reading data - def pollpacket(self, wait): self._stage0() if len(self.buff) < self.bufneed: diff --git a/Misc/NEWS.d/next/IDLE/2026-09-17-01-00-00.gh-issue-89544.rstbuf.rst b/Misc/NEWS.d/next/IDLE/2026-09-17-01-00-00.gh-issue-89544.rstbuf.rst new file mode 100644 index 00000000000000..4a4c885eff9681 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2026-09-17-01-00-00.gh-issue-89544.rstbuf.rst @@ -0,0 +1,2 @@ +Fix IDLE hanging after Restart Shell while receiving a large output from the +user process. _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
