mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
Issue #13342: input() used to ignore sys.stdin's and sys.stdout's unicode
error handler in interactive mode (when calling into PyOS_Readline()).
This commit is contained in:
parent
c2f0a46111
commit
0d776b1ce8
3 changed files with 114 additions and 42 deletions
|
@ -6,12 +6,17 @@ import sys
|
||||||
import warnings
|
import warnings
|
||||||
import collections
|
import collections
|
||||||
import io
|
import io
|
||||||
|
import os
|
||||||
import ast
|
import ast
|
||||||
import types
|
import types
|
||||||
import builtins
|
import builtins
|
||||||
import random
|
import random
|
||||||
from test.support import fcmp, TESTFN, unlink, run_unittest, check_warnings
|
from test.support import fcmp, TESTFN, unlink, run_unittest, check_warnings
|
||||||
from operator import neg
|
from operator import neg
|
||||||
|
try:
|
||||||
|
import pty
|
||||||
|
except ImportError:
|
||||||
|
pty = None
|
||||||
|
|
||||||
|
|
||||||
class Squares:
|
class Squares:
|
||||||
|
@ -988,6 +993,71 @@ class BuiltinTest(unittest.TestCase):
|
||||||
fp.close()
|
fp.close()
|
||||||
unlink(TESTFN)
|
unlink(TESTFN)
|
||||||
|
|
||||||
|
@unittest.skipUnless(pty, "the pty module isn't available")
|
||||||
|
def check_input_tty(self, prompt, terminal_input, stdio_encoding=None):
|
||||||
|
r, w = os.pipe()
|
||||||
|
try:
|
||||||
|
pid, fd = pty.fork()
|
||||||
|
except (OSError, AttributeError) as e:
|
||||||
|
os.close(r)
|
||||||
|
os.close(w)
|
||||||
|
self.skipTest("pty.fork() raised {}".format(e))
|
||||||
|
if pid == 0:
|
||||||
|
# Child
|
||||||
|
os.close(r)
|
||||||
|
# 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')
|
||||||
|
with open(w, "w") as wpipe:
|
||||||
|
try:
|
||||||
|
print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe)
|
||||||
|
print(ascii(input(prompt)), file=wpipe)
|
||||||
|
finally:
|
||||||
|
print(";EOF", file=wpipe)
|
||||||
|
# We don't want to return to unittest...
|
||||||
|
os._exit(0)
|
||||||
|
# Parent
|
||||||
|
os.close(w)
|
||||||
|
os.write(fd, terminal_input + b"\r\n")
|
||||||
|
# Get results from the pipe
|
||||||
|
with open(r, "r") as rpipe:
|
||||||
|
lines = []
|
||||||
|
while True:
|
||||||
|
line = rpipe.readline().strip()
|
||||||
|
if line == ";EOF":
|
||||||
|
break
|
||||||
|
lines.append(line)
|
||||||
|
# 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")
|
||||||
|
# Check the result was got and corresponds to the user's terminal input
|
||||||
|
self.assertEqual(len(lines), 2)
|
||||||
|
input_result = eval(lines[1]) # ascii() -> eval() roundtrip
|
||||||
|
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)
|
||||||
|
|
||||||
|
def test_input_tty(self):
|
||||||
|
# Test input() functionality when wired to a tty (the code path
|
||||||
|
# is different and invokes GNU readline if available).
|
||||||
|
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")
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
self.assertEqual(repr(''), '\'\'')
|
self.assertEqual(repr(''), '\'\'')
|
||||||
self.assertEqual(repr(0), '0')
|
self.assertEqual(repr(0), '0')
|
||||||
|
|
|
@ -10,6 +10,9 @@ What's New in Python 3.2.3?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #13342: input() used to ignore sys.stdin's and sys.stdout's unicode
|
||||||
|
error handler in interactive mode (when calling into PyOS_Readline()).
|
||||||
|
|
||||||
- Issue #13340: Accept None as start and stop parameters for
|
- Issue #13340: Accept None as start and stop parameters for
|
||||||
list.index() and tuple.index().
|
list.index() and tuple.index().
|
||||||
|
|
||||||
|
|
|
@ -1640,76 +1640,65 @@ builtin_input(PyObject *self, PyObject *args)
|
||||||
|
|
||||||
/* If we're interactive, use (GNU) readline */
|
/* If we're interactive, use (GNU) readline */
|
||||||
if (tty) {
|
if (tty) {
|
||||||
PyObject *po;
|
PyObject *po = NULL;
|
||||||
char *prompt;
|
char *prompt;
|
||||||
char *s;
|
char *s = NULL;
|
||||||
PyObject *stdin_encoding;
|
PyObject *stdin_encoding = NULL, *stdin_errors = NULL;
|
||||||
char *stdin_encoding_str;
|
PyObject *stdout_encoding = NULL, *stdout_errors = NULL;
|
||||||
|
char *stdin_encoding_str, *stdin_errors_str;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
stdin_encoding = PyObject_GetAttrString(fin, "encoding");
|
stdin_encoding = PyObject_GetAttrString(fin, "encoding");
|
||||||
if (!stdin_encoding)
|
stdin_errors = PyObject_GetAttrString(fin, "errors");
|
||||||
|
if (!stdin_encoding || !stdin_errors)
|
||||||
/* stdin is a text stream, so it must have an
|
/* stdin is a text stream, so it must have an
|
||||||
encoding. */
|
encoding. */
|
||||||
return NULL;
|
goto _readline_errors;
|
||||||
stdin_encoding_str = _PyUnicode_AsString(stdin_encoding);
|
stdin_encoding_str = _PyUnicode_AsString(stdin_encoding);
|
||||||
if (stdin_encoding_str == NULL) {
|
stdin_errors_str = _PyUnicode_AsString(stdin_errors);
|
||||||
Py_DECREF(stdin_encoding);
|
if (!stdin_encoding_str || !stdin_errors_str)
|
||||||
return NULL;
|
goto _readline_errors;
|
||||||
}
|
|
||||||
tmp = PyObject_CallMethod(fout, "flush", "");
|
tmp = PyObject_CallMethod(fout, "flush", "");
|
||||||
if (tmp == NULL)
|
if (tmp == NULL)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
else
|
else
|
||||||
Py_DECREF(tmp);
|
Py_DECREF(tmp);
|
||||||
if (promptarg != NULL) {
|
if (promptarg != NULL) {
|
||||||
|
/* We have a prompt, encode it as stdout would */
|
||||||
|
char *stdout_encoding_str, *stdout_errors_str;
|
||||||
PyObject *stringpo;
|
PyObject *stringpo;
|
||||||
PyObject *stdout_encoding;
|
|
||||||
char *stdout_encoding_str;
|
|
||||||
stdout_encoding = PyObject_GetAttrString(fout, "encoding");
|
stdout_encoding = PyObject_GetAttrString(fout, "encoding");
|
||||||
if (stdout_encoding == NULL) {
|
stdout_errors = PyObject_GetAttrString(fout, "errors");
|
||||||
Py_DECREF(stdin_encoding);
|
if (!stdout_encoding || !stdout_errors)
|
||||||
return NULL;
|
goto _readline_errors;
|
||||||
}
|
|
||||||
stdout_encoding_str = _PyUnicode_AsString(stdout_encoding);
|
stdout_encoding_str = _PyUnicode_AsString(stdout_encoding);
|
||||||
if (stdout_encoding_str == NULL) {
|
stdout_errors_str = _PyUnicode_AsString(stdout_errors);
|
||||||
Py_DECREF(stdin_encoding);
|
if (!stdout_encoding_str || !stdout_errors_str)
|
||||||
Py_DECREF(stdout_encoding);
|
goto _readline_errors;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
stringpo = PyObject_Str(promptarg);
|
stringpo = PyObject_Str(promptarg);
|
||||||
if (stringpo == NULL) {
|
if (stringpo == NULL)
|
||||||
Py_DECREF(stdin_encoding);
|
goto _readline_errors;
|
||||||
Py_DECREF(stdout_encoding);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
po = PyUnicode_AsEncodedString(stringpo,
|
po = PyUnicode_AsEncodedString(stringpo,
|
||||||
stdout_encoding_str, NULL);
|
stdout_encoding_str, stdout_errors_str);
|
||||||
Py_DECREF(stdout_encoding);
|
Py_CLEAR(stdout_encoding);
|
||||||
Py_DECREF(stringpo);
|
Py_CLEAR(stdout_errors);
|
||||||
if (po == NULL) {
|
Py_CLEAR(stringpo);
|
||||||
Py_DECREF(stdin_encoding);
|
if (po == NULL)
|
||||||
return NULL;
|
goto _readline_errors;
|
||||||
}
|
|
||||||
prompt = PyBytes_AsString(po);
|
prompt = PyBytes_AsString(po);
|
||||||
if (prompt == NULL) {
|
if (prompt == NULL)
|
||||||
Py_DECREF(stdin_encoding);
|
goto _readline_errors;
|
||||||
Py_DECREF(po);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
po = NULL;
|
po = NULL;
|
||||||
prompt = "";
|
prompt = "";
|
||||||
}
|
}
|
||||||
s = PyOS_Readline(stdin, stdout, prompt);
|
s = PyOS_Readline(stdin, stdout, prompt);
|
||||||
Py_XDECREF(po);
|
|
||||||
if (s == NULL) {
|
if (s == NULL) {
|
||||||
if (!PyErr_Occurred())
|
if (!PyErr_Occurred())
|
||||||
PyErr_SetNone(PyExc_KeyboardInterrupt);
|
PyErr_SetNone(PyExc_KeyboardInterrupt);
|
||||||
Py_DECREF(stdin_encoding);
|
goto _readline_errors;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
len = strlen(s);
|
len = strlen(s);
|
||||||
|
@ -1727,12 +1716,22 @@ builtin_input(PyObject *self, PyObject *args)
|
||||||
len--; /* strip trailing '\n' */
|
len--; /* strip trailing '\n' */
|
||||||
if (len != 0 && s[len-1] == '\r')
|
if (len != 0 && s[len-1] == '\r')
|
||||||
len--; /* strip trailing '\r' */
|
len--; /* strip trailing '\r' */
|
||||||
result = PyUnicode_Decode(s, len, stdin_encoding_str, NULL);
|
result = PyUnicode_Decode(s, len, stdin_encoding_str,
|
||||||
|
stdin_errors_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Py_DECREF(stdin_encoding);
|
Py_DECREF(stdin_encoding);
|
||||||
|
Py_DECREF(stdin_errors);
|
||||||
|
Py_XDECREF(po);
|
||||||
PyMem_FREE(s);
|
PyMem_FREE(s);
|
||||||
return result;
|
return result;
|
||||||
|
_readline_errors:
|
||||||
|
Py_XDECREF(stdin_encoding);
|
||||||
|
Py_XDECREF(stdout_encoding);
|
||||||
|
Py_XDECREF(stdin_errors);
|
||||||
|
Py_XDECREF(stdout_errors);
|
||||||
|
Py_XDECREF(po);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fallback if we're not interactive */
|
/* Fallback if we're not interactive */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue