# HG changeset patch # Parent e670b37e7b14e3cc7d4e2aedbd26b25d57bc9450 Issue #24402: Fix input() when stdout.fileno() fails; diagnosed by Eryksun diff -r e670b37e7b14 Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Tue Oct 06 19:36:54 2015 -0700 +++ b/Lib/test/test_builtin.py Wed Oct 07 09:24:36 2015 +0000 @@ -1134,17 +1134,24 @@ sys.stdout = savestdout fp.close() - @unittest.skipUnless(pty, "the pty and signal modules must be available") + def pty_fork(self): + if pty is None: + self.skipTest("the pty and signal modules must be available") + try: + return pty.fork() + except (OSError, AttributeError) as e: + self.skipTest("pty.fork() raised {}".format(e)) + def check_input_tty(self, prompt, terminal_input, stdio_encoding=None): if not sys.stdin.isatty() or not sys.stdout.isatty(): self.skipTest("stdin and stdout must be ttys") r, w = os.pipe() try: - pid, fd = pty.fork() - except (OSError, AttributeError) as e: + pid, fd = self.pty_fork() + except: os.close(r) os.close(w) - self.skipTest("pty.fork() raised {}".format(e)) + raise if pid == 0: # Child try: @@ -1210,6 +1217,30 @@ # Check stdin/stdout error handler is used when invoking GNU readline self.check_input_tty("prompté", b"quux\xe9", "ascii") + def test_input_no_stdout_fileno(self): + # Issue #24402: If stdin is the original terminal but stdout.fileno() + # fails, do not use the original stdout file descriptor + pid, pty = self.pty_fork() + if pid: # Parent process + # Ideally this should read and write concurrently using select() + # or similar, to avoid the possibility of a deadlock. + os.write(pty, b"quux\r") + _, status = os.waitpid(pid, 0) + output = os.read(pty, 3000).decode("ascii", "backslashreplace") + os.close(pty) + self.assertEqual(status, 0, output) + else: # Child process + try: + self.assertTrue(sys.stdin.isatty(), "stdin not a terminal") + sys.stdout = io.StringIO() # Does not support fileno() + input("prompt") + self.assertEqual(sys.stdout.getvalue(), "prompt") + os._exit(0) # Success! + except: + sys.excepthook(*sys.exc_info()) + finally: + os._exit(1) # Failure + # test_int(): see test_int.py for tests of built-in function int(). def test_repr(self): diff -r e670b37e7b14 Misc/NEWS --- a/Misc/NEWS Tue Oct 06 19:36:54 2015 -0700 +++ b/Misc/NEWS Wed Oct 07 09:24:36 2015 +0000 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #24402: Fix input() to prompt to the redirected stdout when + sys.stdout.fileno() fails. + - Issue #24806: Prevent builtin types that are not allowed to be subclassed from being subclassed through multiple inheritance. diff -r e670b37e7b14 Python/bltinmodule.c --- a/Python/bltinmodule.c Tue Oct 06 19:36:54 2015 -0700 +++ b/Python/bltinmodule.c Wed Oct 07 09:24:36 2015 +0000 @@ -1723,8 +1723,10 @@ } if (tty) { tmp = _PyObject_CallMethodId(fout, &PyId_fileno, ""); - if (tmp == NULL) + if (tmp == NULL) { PyErr_Clear(); + tty = 0; + } else { fd = PyLong_AsLong(tmp); Py_DECREF(tmp);