# HG changeset patch # Parent ccf42cdffc6d5ba3e0f11e3d84c58d413041292d Issue #1927: Always write input() to stdout Initial patch contributed by Michael Domingues. diff -r ccf42cdffc6d Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Sat Dec 05 05:42:18 2015 +0000 +++ b/Lib/test/test_builtin.py Tue Jan 19 02:59:34 2016 +0000 @@ -9,6 +9,7 @@ import pickle import platform import random +import subprocess import sys import traceback import types @@ -1493,6 +1494,19 @@ """Tests that use a pseudo terminal to guarantee stdin and stdout are terminals in the test environment""" + def read_master(self, fd): + # Beware of Linux raising EIO when the slave is closed + child_output = bytearray() + while True: + try: + chunk = os.read(fd, 3000) + except OSError: # Assume EIO + break + if not chunk: + break + child_output.extend(chunk) + return child_output + def run_child(self, child, terminal_input): r, w = os.pipe() # Pipe test results from child back to parent try: @@ -1530,16 +1544,7 @@ # Check the result was got and corresponds to the user's terminal input if len(lines) != 2: # Something went wrong, try to get at stderr - # Beware of Linux raising EIO when the slave is closed - child_output = bytearray() - while True: - try: - chunk = os.read(fd, 3000) - except OSError: # Assume EIO - break - if not chunk: - break - child_output.extend(chunk) + child_output = self.read_master(fd) os.close(fd) child_output = child_output.decode("ascii", "ignore") self.fail("got %d lines in pipe but expected 2, child output was:\n%s" @@ -1601,6 +1606,21 @@ ) self.assertSequenceEqual(lines, expected) + def test_prompt_stdout(self): + # Issue #1927: If stdin and stdout are the original terminal, the + # prompt should be written to stdout, not stderr + [master, slave] = pty.openpty() + with os.fdopen(master, "wb") as writer: + cmd = (sys.executable, "-I", "-c", "input('prompt')") + # Do not capture stderr + with subprocess.Popen(cmd, stdin=slave, stdout=slave) as proc: + os.close(slave) + writer.write(b"quux\r") + writer.flush() + output = self.read_master(master) + self.assertIn(b"prompt", output) + self.assertEqual(proc.returncode, 0) + class TestSorted(unittest.TestCase): def test_basic(self): diff -r ccf42cdffc6d Misc/ACKS --- a/Misc/ACKS Sat Dec 05 05:42:18 2015 +0000 +++ b/Misc/ACKS Tue Jan 19 02:59:34 2016 +0000 @@ -18,6 +18,7 @@ Marc Abramowitz Eldar Abusalimov Ron Adam +Matt Adams Anton Afanasyev Ali Afshar Nitika Agarwal @@ -353,6 +354,7 @@ Josip Djolonga Walter Dörwald Jaromir Dolecek +Michael Domingues Ismail Donmez Robert Donohue Marcos Donolo diff -r ccf42cdffc6d Misc/NEWS --- a/Misc/NEWS Sat Dec 05 05:42:18 2015 +0000 +++ b/Misc/NEWS Tue Jan 19 02:59:34 2016 +0000 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #1927: The interactive interpreter and the input() builtin now print + their prompts to stdout, rather than stderr. + - Issue #25709: Fixed problem with in-place string concatenation and utf-8 cache. - Issue #5319: New Py_FinalizeEx() API allowing Python to set an exit status diff -r ccf42cdffc6d Parser/myreadline.c --- a/Parser/myreadline.c Sat Dec 05 05:42:18 2015 +0000 +++ b/Parser/myreadline.c Tue Jan 19 02:59:34 2016 +0000 @@ -115,10 +115,10 @@ if (p == NULL) return NULL; + fflush(stderr); + if (prompt) + fprintf(sys_stdout, "%s", prompt); fflush(sys_stdout); - if (prompt) - fprintf(stderr, "%s", prompt); - fflush(stderr); switch (my_fgets(p, (int)n, sys_stdin)) { case 0: /* Normal case */ diff -r ccf42cdffc6d Parser/pgenmain.c --- a/Parser/pgenmain.c Sat Dec 05 05:42:18 2015 +0000 +++ b/Parser/pgenmain.c Tue Jan 19 02:59:34 2016 +0000 @@ -146,7 +146,7 @@ char *q; if (p == NULL) return NULL; - fprintf(stderr, "%s", prompt); + fprintf(sys_stdout, "%s", prompt); q = fgets(p, n, sys_stdin); if (q == NULL) { *p = '\0'; diff -r ccf42cdffc6d Parser/tokenizer.c --- a/Parser/tokenizer.c Sat Dec 05 05:42:18 2015 +0000 +++ b/Parser/tokenizer.c Tue Jan 19 02:59:34 2016 +0000 @@ -932,7 +932,7 @@ return Py_CHARMASK(*tok->cur++); } if (tok->prompt != NULL) { - char *newtok = PyOS_Readline(stdin, stdout, tok->prompt); + char *newtok = PyOS_Readline(stdin, stderr, tok->prompt); #ifndef PGEN if (newtok != NULL) { char *translated = translate_newlines(newtok, 0, tok);