diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 9d949b74cb8d0b9..cf05ea95b497ed5 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1628,6 +1628,23 @@ def child(wpipe): ) self.assertSequenceEqual(lines, expected) + def test_input_override_stdin(self): + input_buf = io.StringIO('quux\n') + input_received = input(infile=input_buf) + self.assertSequenceEqual('quux', input_received) + + def test_input_override_stdout(self): + output_buf = io.StringIO() + input_buf = io.StringIO('\n') + input('blah: ', infile=input_buf, outfile=output_buf) + self.assertSequenceEqual('blah: ', output_buf.getvalue()) + + def test_input_override_stderr(self): + error_buf = io.StringIO() + input_buf = io.StringIO('\n') + self.assertRaises(RuntimeError, input, 'blah: ', infile=input_buf, + outfile=None, errfile=error_buf) + class TestSorted(unittest.TestCase): def test_basic(self): diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 2269fe21657e61c..558a6cfc4b72198 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1842,23 +1842,30 @@ input as builtin_input prompt: object(c_default="NULL") = None / + * + infile: object(c_default="_PySys_GetObjectId(&PyId_stdin)") = None + outfile: object(c_default="_PySys_GetObjectId(&PyId_stdout)") = None + errfile: object(c_default="_PySys_GetObjectId(&PyId_stderr)") = None Read a string from standard input. The trailing newline is stripped. The prompt string, if given, is printed to standard output without a trailing newline before reading input. +The defaults for infile, outfile, and errfile if they not provided are equivalent +to sys.stdin, sys.stdout, and sys.stderr respectively. If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError. On *nix systems, readline is used if available. [clinic start generated code]*/ static PyObject * -builtin_input_impl(PyObject *module, PyObject *prompt) -/*[clinic end generated code: output=83db5a191e7a0d60 input=5e8bb70c2908fe3c]*/ +builtin_input_impl(PyObject *module, PyObject *prompt, PyObject *infile, + PyObject *outfile, PyObject *errfile) +/*[clinic end generated code: output=40acdfd4ce9e4222 input=f7d6c671b75027bc]*/ { - PyObject *fin = _PySys_GetObjectId(&PyId_stdin); - PyObject *fout = _PySys_GetObjectId(&PyId_stdout); - PyObject *ferr = _PySys_GetObjectId(&PyId_stderr); + PyObject *fin = infile; + PyObject *fout = outfile; + PyObject *ferr = errfile; PyObject *tmp; long fd; int tty; diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index fa327da0ffc352b..0a3bda547ca2d8b 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -528,35 +528,43 @@ builtin_pow(PyObject *module, PyObject **args, Py_ssize_t nargs) } PyDoc_STRVAR(builtin_input__doc__, -"input($module, prompt=None, /)\n" +"input($module, prompt=None, /, *, infile=None, outfile=None,\n" +" errfile=None)\n" "--\n" "\n" "Read a string from standard input. The trailing newline is stripped.\n" "\n" "The prompt string, if given, is printed to standard output without a\n" "trailing newline before reading input.\n" +"The defaults for infile, outfile, and errfile if they not provided are equivalent\n" +"to sys.stdin, sys.stdout, and sys.stderr respectively.\n" "\n" "If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.\n" "On *nix systems, readline is used if available."); #define BUILTIN_INPUT_METHODDEF \ - {"input", (PyCFunction)builtin_input, METH_FASTCALL, builtin_input__doc__}, + {"input", (PyCFunction)builtin_input, METH_FASTCALL|METH_KEYWORDS, builtin_input__doc__}, static PyObject * -builtin_input_impl(PyObject *module, PyObject *prompt); +builtin_input_impl(PyObject *module, PyObject *prompt, PyObject *infile, + PyObject *outfile, PyObject *errfile); static PyObject * -builtin_input(PyObject *module, PyObject **args, Py_ssize_t nargs) +builtin_input(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + static const char * const _keywords[] = {"", "infile", "outfile", "errfile", NULL}; + static _PyArg_Parser _parser = {"|O$OOO:input", _keywords, 0}; PyObject *prompt = NULL; + PyObject *infile = _PySys_GetObjectId(&PyId_stdin); + PyObject *outfile = _PySys_GetObjectId(&PyId_stdout); + PyObject *errfile = _PySys_GetObjectId(&PyId_stderr); - if (!_PyArg_UnpackStack(args, nargs, "input", - 0, 1, - &prompt)) { + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, + &prompt, &infile, &outfile, &errfile)) { goto exit; } - return_value = builtin_input_impl(module, prompt); + return_value = builtin_input_impl(module, prompt, infile, outfile, errfile); exit: return return_value; @@ -676,4 +684,4 @@ builtin_issubclass(PyObject *module, PyObject **args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=09752daa8cdd6ec7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4ca8e7840e9e74f3 input=a9049054013a1b77]*/