https://github.com/python/cpython/commit/625b044ccac45dfff747145db42b0d61a1b398fe
commit: 625b044ccac45dfff747145db42b0d61a1b398fe
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: terryjreedy <[email protected]>
date: 2026-09-23T01:57:47Z
summary:

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

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]

Reply via email to