Skip to content

Commit 6d209cb

Browse files
authored
gh-140146: Fix for stdin redirection to a pipe with interactive tkinter on Windows (GH-148819)
1 parent f6e904e commit 6d209cb

3 files changed

Lines changed: 83 additions & 7 deletions

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# test_tkinter_pipe.py
2+
import unittest
3+
import subprocess
4+
import sys
5+
from test import support
6+
7+
8+
@unittest.skipUnless(support.has_subprocess_support, "test requires subprocess")
9+
class TkinterPipeTest(unittest.TestCase):
10+
11+
def test_tkinter_pipe_buffered(self):
12+
args = [sys.executable, "-i"]
13+
proc = subprocess.Popen(args,
14+
stdin=subprocess.PIPE,
15+
stdout=subprocess.PIPE,
16+
stderr=subprocess.PIPE)
17+
proc.stdin.write(b"import tkinter\n")
18+
proc.stdin.write(b"interpreter = tkinter.Tcl()\n")
19+
proc.stdin.write(b"print('hello')\n")
20+
proc.stdin.write(b"print('goodbye')\n")
21+
proc.stdin.write(b"quit()\n")
22+
stdout, stderr = proc.communicate(timeout=support.SHORT_TIMEOUT)
23+
stdout = stdout.decode()
24+
self.assertEqual(stdout.split(), ['hello', 'goodbye'])
25+
26+
def test_tkinter_pipe_unbuffered(self):
27+
args = [sys.executable, "-i", "-u"]
28+
proc = subprocess.Popen(args,
29+
stdin=subprocess.PIPE,
30+
stdout=subprocess.PIPE,
31+
stderr=subprocess.PIPE)
32+
proc.stdin.write(b"import tkinter\n")
33+
proc.stdin.write(b"interpreter = tkinter.Tcl()\n")
34+
35+
proc.stdin.write(b"print('hello')\n")
36+
proc.stdin.flush()
37+
stdout = proc.stdout.readline()
38+
stdout = stdout.decode()
39+
self.assertEqual(stdout.strip(), 'hello')
40+
41+
proc.stdin.write(b"print('hello again')\n")
42+
proc.stdin.flush()
43+
stdout = proc.stdout.readline()
44+
stdout = stdout.decode()
45+
self.assertEqual(stdout.strip(), 'hello again')
46+
47+
proc.stdin.write(b"print('goodbye')\n")
48+
proc.stdin.write(b"quit()\n")
49+
stdout, stderr = proc.communicate(timeout=support.SHORT_TIMEOUT)
50+
stdout = stdout.decode()
51+
self.assertEqual(stdout.strip(), 'goodbye')
52+
53+
54+
if __name__ == "__main__":
55+
unittest.main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Prevent :mod:`tkinter` from hanging on Windows if stdin is redirected to a pipe in an
2+
interactive session. This is helpful for testing interactive usage of
3+
tkinter from a script, for example as part of the cpython test suite.

Modules/_tkinter.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3443,10 +3443,10 @@ static PyMethodDef moduleMethods[] =
34433443
};
34443444

34453445
#ifdef WAIT_FOR_STDIN
3446+
#ifndef MS_WINDOWS
34463447

34473448
static int stdin_ready = 0;
34483449

3449-
#ifndef MS_WINDOWS
34503450
static void
34513451
MyFileProc(void *clientData, int mask)
34523452
{
@@ -3465,22 +3465,40 @@ static PyThreadState *event_tstate = NULL;
34653465
static int
34663466
EventHook(void)
34673467
{
3468-
#ifndef MS_WINDOWS
3468+
#ifdef MS_WINDOWS
3469+
HANDLE hStdin;
3470+
DWORD type;
3471+
#else
34693472
int tfile;
3473+
stdin_ready = 0;
34703474
#endif
34713475
PyEval_RestoreThread(event_tstate);
3472-
stdin_ready = 0;
34733476
errorInCmd = 0;
3474-
#ifndef MS_WINDOWS
3477+
#ifdef MS_WINDOWS
3478+
hStdin = GetStdHandle(STD_INPUT_HANDLE);
3479+
type = GetFileType(hStdin);
3480+
while (1) {
3481+
#else
34753482
tfile = fileno(stdin);
34763483
Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc,
34773484
(void *)(Py_intptr_t)tfile);
3478-
#endif
34793485
while (!stdin_ready) {
3486+
#endif
34803487
int result;
34813488
#ifdef MS_WINDOWS
3482-
if (_kbhit()) {
3483-
stdin_ready = 1;
3489+
if (type == FILE_TYPE_CHAR) {
3490+
if (_kbhit()) break;
3491+
}
3492+
else if (type == FILE_TYPE_PIPE) {
3493+
DWORD available;
3494+
if (PeekNamedPipe(hStdin, NULL, 0, NULL, &available, NULL)) {
3495+
if (available > 0) break;
3496+
}
3497+
else {
3498+
if (GetLastError() == ERROR_BROKEN_PIPE) break;
3499+
}
3500+
}
3501+
else if (type == FILE_TYPE_DISK) {
34843502
break;
34853503
}
34863504
#endif

0 commit comments

Comments
 (0)