diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 290ba2cad8e..f64ad7ffbf7 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -75,6 +75,23 @@ class BitBucket: def write(self, line): pass +class MalformedInputStream: + def readline(self): + return "foobar\n" + + def fileno(self): + return 0 + +class MalformedOutputStream: + def __init__(self): + self.value = "" + + def fileno(self): + return 1 + + def write(self, value): + self.value += value + test_conv_no_sign = [ ('0', 0), ('1', 1), @@ -1302,6 +1319,13 @@ class BuiltinTest(unittest.TestCase): sys.stdin = io.StringIO() self.assertRaises(EOFError, input) + # input() in tty mode with a malformed input stream should attempt + # to call .readline() + sys.stdin = MalformedInputStream() + sys.stdout = MalformedOutputStream() + self.assertEqual(input("baz"), "foobar") # strips \n + self.assertEqual(sys.stdout.value, "baz") + del sys.stdout self.assertRaises(RuntimeError, input, 'prompt') del sys.stdin diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 82ca317c7e9..c98329f7fd9 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2010,17 +2010,21 @@ builtin_input_impl(PyObject *module, PyObject *prompt) /* stdin is a text stream, so it must have an encoding. */ stdin_encoding = _PyObject_GetAttrId(fin, &PyId_encoding); + if (!stdin_encoding || !PyUnicode_Check(stdin_encoding)) { + goto _readline_disable_tty_and_fall_back; + } stdin_errors = _PyObject_GetAttrId(fin, &PyId_errors); - if (!stdin_encoding || !stdin_errors || - !PyUnicode_Check(stdin_encoding) || - !PyUnicode_Check(stdin_errors)) { - tty = 0; - goto _readline_errors; + if (!stdin_errors || !PyUnicode_Check(stdin_errors)) { + goto _readline_disable_tty_and_fall_back; } stdin_encoding_str = PyUnicode_AsUTF8(stdin_encoding); - stdin_errors_str = PyUnicode_AsUTF8(stdin_errors); - if (!stdin_encoding_str || !stdin_errors_str) + if (!stdin_encoding_str) { goto _readline_errors; + } + stdin_errors_str = PyUnicode_AsUTF8(stdin_errors); + if (!stdin_errors_str) { + goto _readline_errors; + } tmp = _PyObject_CallMethodIdNoArgs(fout, &PyId_flush); if (tmp == NULL) PyErr_Clear(); @@ -2099,6 +2103,8 @@ builtin_input_impl(PyObject *module, PyObject *prompt) return result; + _readline_disable_tty_and_fall_back: + tty = 0; _readline_errors: Py_XDECREF(stdin_encoding); Py_XDECREF(stdout_encoding);