https://github.com/python/cpython/commit/fc53775ef5c35f3c926e258cef4adf82b913a649
commit: fc53775ef5c35f3c926e258cef4adf82b913a649
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: hugovk <[email protected]>
date: 2026-09-28T19:00:52+03:00
summary:

[3.15] gh-89544: Fix IDLE hang after Restart Shell while receiving output 
(GH-157644) (#157970)

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]

Reply via email to