https://github.com/python/cpython/commit/6d209cbb93d0871ad4a5883637a8f0aebc053f76
commit: 6d209cbb93d0871ad4a5883637a8f0aebc053f76
branch: main
author: mdehoon <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-06-29T08:42:56Z
summary:

gh-140146: Fix for stdin redirection to a pipe with interactive tkinter on 
Windows (GH-148819)

files:
A Lib/test/test_tkinter/test_tkinter_pipe.py
A Misc/NEWS.d/next/Windows/2026-04-21-16-07-11.gh-issue-140146.TAcUHA.rst
M Modules/_tkinter.c

diff --git a/Lib/test/test_tkinter/test_tkinter_pipe.py 
b/Lib/test/test_tkinter/test_tkinter_pipe.py
new file mode 100644
index 000000000000000..775d0ff5e19b0ce
--- /dev/null
+++ b/Lib/test/test_tkinter/test_tkinter_pipe.py
@@ -0,0 +1,55 @@
+# test_tkinter_pipe.py
+import unittest
+import subprocess
+import sys
+from test import support
+
+
[email protected](support.has_subprocess_support, "test requires 
subprocess")
+class TkinterPipeTest(unittest.TestCase):
+
+    def test_tkinter_pipe_buffered(self):
+        args = [sys.executable, "-i"]
+        proc = subprocess.Popen(args,
+                                 stdin=subprocess.PIPE,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+        proc.stdin.write(b"import tkinter\n")
+        proc.stdin.write(b"interpreter = tkinter.Tcl()\n")
+        proc.stdin.write(b"print('hello')\n")
+        proc.stdin.write(b"print('goodbye')\n")
+        proc.stdin.write(b"quit()\n")
+        stdout, stderr = proc.communicate(timeout=support.SHORT_TIMEOUT)
+        stdout = stdout.decode()
+        self.assertEqual(stdout.split(), ['hello', 'goodbye'])
+
+    def test_tkinter_pipe_unbuffered(self):
+        args = [sys.executable, "-i", "-u"]
+        proc = subprocess.Popen(args,
+                                 stdin=subprocess.PIPE,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+        proc.stdin.write(b"import tkinter\n")
+        proc.stdin.write(b"interpreter = tkinter.Tcl()\n")
+
+        proc.stdin.write(b"print('hello')\n")
+        proc.stdin.flush()
+        stdout = proc.stdout.readline()
+        stdout = stdout.decode()
+        self.assertEqual(stdout.strip(), 'hello')
+
+        proc.stdin.write(b"print('hello again')\n")
+        proc.stdin.flush()
+        stdout = proc.stdout.readline()
+        stdout = stdout.decode()
+        self.assertEqual(stdout.strip(), 'hello again')
+
+        proc.stdin.write(b"print('goodbye')\n")
+        proc.stdin.write(b"quit()\n")
+        stdout, stderr = proc.communicate(timeout=support.SHORT_TIMEOUT)
+        stdout = stdout.decode()
+        self.assertEqual(stdout.strip(), 'goodbye')
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git 
a/Misc/NEWS.d/next/Windows/2026-04-21-16-07-11.gh-issue-140146.TAcUHA.rst 
b/Misc/NEWS.d/next/Windows/2026-04-21-16-07-11.gh-issue-140146.TAcUHA.rst
new file mode 100644
index 000000000000000..2d3e42c86fd521f
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2026-04-21-16-07-11.gh-issue-140146.TAcUHA.rst
@@ -0,0 +1,3 @@
+Prevent :mod:`tkinter` from hanging on Windows if stdin is redirected to a 
pipe in an
+interactive session. This is helpful for testing interactive usage of
+tkinter from a script, for example as part of the cpython test suite.
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index 1f1fe4a91addf4c..62a79128d9726ca 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -3443,10 +3443,10 @@ static PyMethodDef moduleMethods[] =
 };
 
 #ifdef WAIT_FOR_STDIN
+#ifndef MS_WINDOWS
 
 static int stdin_ready = 0;
 
-#ifndef MS_WINDOWS
 static void
 MyFileProc(void *clientData, int mask)
 {
@@ -3465,22 +3465,40 @@ static PyThreadState *event_tstate = NULL;
 static int
 EventHook(void)
 {
-#ifndef MS_WINDOWS
+#ifdef MS_WINDOWS
+    HANDLE hStdin;
+    DWORD type;
+#else
     int tfile;
+    stdin_ready = 0;
 #endif
     PyEval_RestoreThread(event_tstate);
-    stdin_ready = 0;
     errorInCmd = 0;
-#ifndef MS_WINDOWS
+#ifdef MS_WINDOWS
+    hStdin = GetStdHandle(STD_INPUT_HANDLE);
+    type = GetFileType(hStdin);
+    while (1) {
+#else
     tfile = fileno(stdin);
     Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc,
                           (void *)(Py_intptr_t)tfile);
-#endif
     while (!stdin_ready) {
+#endif
         int result;
 #ifdef MS_WINDOWS
-        if (_kbhit()) {
-            stdin_ready = 1;
+        if (type == FILE_TYPE_CHAR) {
+            if (_kbhit()) break;
+        }
+        else if (type == FILE_TYPE_PIPE) {
+            DWORD available;
+            if (PeekNamedPipe(hStdin, NULL, 0, NULL, &available, NULL)) {
+                if (available > 0) break;
+            }
+            else {
+                if (GetLastError() == ERROR_BROKEN_PIPE) break;
+            }
+        }
+        else if (type == FILE_TYPE_DISK) {
             break;
         }
 #endif

_______________________________________________
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