# HG changeset patch # Parent fa4d8276d0fbeb9fd9527ae0538d396ba5c5d249 Issue #13886: Fix input() tests to explicitly run with and without Readline Previously, the two non-ASCII tests would fail if the Readline module had been loaded. Now each test is repeated in a subprocess, with and without the Readline module loaded. Also changed the non-ASCII encoding test to use properly-encoded UTF-8 without needing error handling. Do not test error handling of Readline because it does not seem to be well-defined. diff -r fa4d8276d0fb Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Tue Dec 06 00:24:19 2016 +0200 +++ b/Lib/test/test_builtin.py Tue Dec 06 03:23:32 2016 +0000 @@ -12,6 +12,7 @@ import platform import random import re +import subprocess import sys import traceback import types @@ -1556,31 +1557,49 @@ os.close(fd) return lines - 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") - def child(wpipe): - # Check the error handlers are accounted for - if stdio_encoding: - sys.stdin = io.TextIOWrapper(sys.stdin.detach(), - encoding=stdio_encoding, - errors='surrogateescape') - sys.stdout = io.TextIOWrapper(sys.stdout.detach(), - encoding=stdio_encoding, - errors='replace') - print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe) - print(ascii(input(prompt)), file=wpipe) - lines = self.run_child(child, terminal_input + b"\r\n") - # Check we did exercise the GNU readline path - self.assertIn(lines[0], {'tty = True', 'tty = False'}) - if lines[0] != 'tty = True': - self.skipTest("standard IO in should have been a tty") - input_result = eval(lines[1]) # ascii() -> eval() roundtrip + def check_input_tty(self, prompt, terminal_input, stdio_encoding=None, *, + readline=True): + template = ( + 'import sys, io\n' + 'if {readline!a}:\n' + ' import readline\n' + '# Check the error handlers are accounted for\n' + 'stdio_encoding = {stdio_encoding!a}\n' + 'if stdio_encoding:\n' + ' sys.stdin = io.TextIOWrapper(sys.stdin.detach(),\n' + ' encoding=stdio_encoding,\n' + " errors='surrogateescape')\n" + ' sys.stdout = io.TextIOWrapper(sys.stdout.detach(),\n' + ' encoding=stdio_encoding,\n' + " errors='replace')\n" + '# Check we exercise the PyOS_Readline() path\n' + 'if not sys.stdin.isatty() or not sys.stdout.isatty():\n' + ' raise AssertionError("standard IO should be a tty")\n' + 'result = input({prompt!a})\n' + 'if result != {expected!a}:\n' + ' raise AssertionError("unexpected input " + ascii(result))\n' + ) if stdio_encoding: expected = terminal_input.decode(stdio_encoding, 'surrogateescape') else: expected = terminal_input.decode(sys.stdin.encoding) # what else? - self.assertEqual(input_result, expected) + tests = [False] + if readline: + tests.append(True) + for readline in tests: + with self.subTest(expected=expected, readline=readline): + code = template.format( + readline=readline, stdio_encoding=stdio_encoding, + prompt=prompt, expected=expected) + cmd = (sys.executable, '-s', '-c', code) + [master, slave] = pty.openpty() + with os.fdopen(master, 'wb') as writer, \ + subprocess.Popen(cmd, stdin=slave, stdout=slave, + stderr=subprocess.DEVNULL) as proc: + os.close(slave) + writer.write(terminal_input + b"\r\n") + writer.flush() + self.assertEqual(proc.returncode, 0) def test_input_tty(self): # Test input() functionality when wired to a tty (the code path @@ -1588,12 +1607,13 @@ self.check_input_tty("prompt", b"quux") def test_input_tty_non_ascii(self): - # Check stdin/stdout encoding is used when invoking GNU readline - self.check_input_tty("prompté", b"quux\xe9", "utf-8") + # Check stdin/stdout encoding is used when invoking PyOS_Readline() + self.check_input_tty("prompté", "quuxé".encode("utf-8"), "utf-8") def test_input_tty_non_ascii_unicode_errors(self): - # Check stdin/stdout error handler is used when invoking GNU readline - self.check_input_tty("prompté", b"quux\xe9", "ascii") + # Check stdin/stdout error handler used when invoking PyOS_Readline() + # GNU Readline does not seem to handle encoding errors consistently + self.check_input_tty("prompté", b"quux\xe9", "ascii", readline=False) def test_input_no_stdout_fileno(self): # Issue #24402: If stdin is the original terminal but stdout.fileno()