https://github.com/python/cpython/commit/52bdfcc141079806b73e28d7901552c9b667bfc2
commit: 52bdfcc141079806b73e28d7901552c9b667bfc2
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-06-26T18:00:56+03:00
summary:

[3.15] gh-139145: Fix tkinter event loop in interactive mode (GH-152257) 
(GH-152291)

When a Tcl command running its own event loop (such as vwait or
wait_variable) was active and the user typed input on stdin, the event
loop kept spinning at 100% CPU.  The stdin file handler is now removed as
soon as input becomes available.

Also fix gh-139816: an exception raised in a callback no longer stops the
event loop to wait for Enter on a Python built without readline; pending
callbacks keep running until input is actually available on stdin.
(cherry picked from commit 3ffda34f5cd3809a8755c9596948e4e477661d5f)

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: mdehoon <[email protected]>
Co-authored-by: Christopher Chavez <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-06-25-14-32-42.gh-issue-139145.kT9rmP.rst
A Misc/NEWS.d/next/Library/2026-06-25-14-32-43.gh-issue-139816.Lq2vXa.rst
M Modules/_tkinter.c

diff --git 
a/Misc/NEWS.d/next/Library/2026-06-25-14-32-42.gh-issue-139145.kT9rmP.rst 
b/Misc/NEWS.d/next/Library/2026-06-25-14-32-42.gh-issue-139145.kT9rmP.rst
new file mode 100644
index 00000000000000..3fa639c5f727ba
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-25-14-32-42.gh-issue-139145.kT9rmP.rst
@@ -0,0 +1,5 @@
+Fix a busy loop in :mod:`tkinter` on interactive Python.  When a Tcl command
+running its own event loop (such as ``vwait`` or :meth:`!wait_variable`) was
+active and input arrived on stdin, the event loop kept spinning at 100% CPU.
+The stdin file handler is now removed as soon as input is available.  Based on
+a patch by Michiel de Hoon.
diff --git 
a/Misc/NEWS.d/next/Library/2026-06-25-14-32-43.gh-issue-139816.Lq2vXa.rst 
b/Misc/NEWS.d/next/Library/2026-06-25-14-32-43.gh-issue-139816.Lq2vXa.rst
new file mode 100644
index 00000000000000..b180dee686caf8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-25-14-32-43.gh-issue-139816.Lq2vXa.rst
@@ -0,0 +1,4 @@
+Fix a hang in :mod:`tkinter` on interactive Python built without
+:mod:`readline`.  An exception raised in a callback no longer causes the
+event loop to stop and wait for the user to press Enter; pending callbacks
+now keep running until input is actually available on stdin.
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index 466e275c5cecf0..86b1b2d9a534d8 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -3433,7 +3433,13 @@ static int stdin_ready = 0;
 static void
 MyFileProc(void *clientData, int mask)
 {
+    int tfile = (int)(Py_intptr_t)clientData;
     stdin_ready = 1;
+    /* Stop watching stdin now that input is available.  Doing it here rather
+       than after the loop below ensures that a nested event loop (e.g. the one
+       started by wait_variable) does not keep waking up on the same unread
+       input, spinning at 100% CPU. */
+    Tcl_DeleteFileHandler(tfile);
 }
 #endif
 
@@ -3450,9 +3456,10 @@ EventHook(void)
     errorInCmd = 0;
 #ifndef MS_WINDOWS
     tfile = fileno(stdin);
-    Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, NULL);
+    Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc,
+                          (void *)(Py_intptr_t)tfile);
 #endif
-    while (!errorInCmd && !stdin_ready) {
+    while (!stdin_ready) {
         int result;
 #ifdef MS_WINDOWS
         if (_kbhit()) {
@@ -3472,18 +3479,23 @@ EventHook(void)
             Sleep(Tkinter_busywaitinterval);
         Py_END_ALLOW_THREADS
 
+        /* Report an exception raised in a callback, but keep pumping events
+           instead of returning to the prompt: without readline there is no
+           input waiting on stdin yet, so returning here would block in fgets
+           until the user hits enter, freezing later callbacks. */
+        if (errorInCmd) {
+            errorInCmd = 0;
+            PyErr_SetRaisedException(excInCmd);
+            excInCmd = NULL;
+            PyErr_Print();
+        }
         if (result < 0)
             break;
     }
 #ifndef MS_WINDOWS
-    Tcl_DeleteFileHandler(tfile);
+    if (!stdin_ready)
+        Tcl_DeleteFileHandler(tfile);
 #endif
-    if (errorInCmd) {
-        errorInCmd = 0;
-        PyErr_SetRaisedException(excInCmd);
-        excInCmd = NULL;
-        PyErr_Print();
-    }
     PyEval_SaveThread();
     return 0;
 }

_______________________________________________
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