Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 13d49ee

Browse files
author
Victor Stinner
committed
Issue #10601: sys.displayhook uses 'backslashreplace' error handler on
UnicodeEncodeError.
1 parent 44588b4 commit 13d49ee

4 files changed

Lines changed: 125 additions & 4 deletions

File tree

Doc/library/sys.rst

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,39 @@ always available.
9999

100100
.. function:: displayhook(value)
101101

102-
If *value* is not ``None``, this function prints it to ``sys.stdout``, and saves
103-
it in ``builtins._``.
102+
If *value* is not ``None``, this function prints ``repr(value)`` to
103+
``sys.stdout``, and saves *value* in ``builtins._``. If ``repr(value)`` is
104+
not encodable to ``sys.stdout.encoding`` with ``sys.stdout.errors`` error
105+
handler (which is probably ``'strict'``), encode it to
106+
``sys.stdout.encoding`` with ``'backslashreplace'`` error handler.
104107

105108
``sys.displayhook`` is called on the result of evaluating an :term:`expression`
106109
entered in an interactive Python session. The display of these values can be
107110
customized by assigning another one-argument function to ``sys.displayhook``.
108111

112+
Pseudo-code::
113+
114+
def displayhook(value):
115+
if value is None:
116+
return
117+
# Set '_' to None to avoid recursion
118+
builtins._ = None
119+
text = repr(value)
120+
try:
121+
sys.stdout.write(text)
122+
except UnicodeEncodeError:
123+
bytes = text.encode(sys.stdout.encoding, 'backslashreplace')
124+
if hasattr(sys.stdout, 'buffer'):
125+
sys.stdout.buffer.write(bytes)
126+
else:
127+
text = bytes.decode(sys.stdout.encoding, 'strict')
128+
sys.stdout.write(text)
129+
sys.stdout.write("\n")
130+
builtins._ = value
131+
132+
.. versionchanged:: 3.2
133+
Use ``'backslashreplace'`` error handler on :exc:`UnicodeEncodeError`.
134+
109135

110136
.. function:: excepthook(type, value, traceback)
111137

Lib/test/test_cmd_line.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,24 @@ def test_large_PYTHONPATH(self):
221221
self.assertIn(path1.encode('ascii'), out)
222222
self.assertIn(path2.encode('ascii'), out)
223223

224+
def test_displayhook_unencodable(self):
225+
for encoding in ('ascii', 'latin1', 'utf8'):
226+
env = os.environ.copy()
227+
env['PYTHONIOENCODING'] = encoding
228+
p = subprocess.Popen(
229+
[sys.executable, '-i'],
230+
stdin=subprocess.PIPE,
231+
stdout=subprocess.PIPE,
232+
stderr=subprocess.STDOUT,
233+
env=env)
234+
# non-ascii, surrogate, non-BMP printable, non-BMP unprintable
235+
text = "a=\xe9 b=\uDC80 c=\U00010000 d=\U0010FFFF"
236+
p.stdin.write(ascii(text).encode('ascii') + b"\n")
237+
p.stdin.write(b'exit()\n')
238+
data = kill_python(p)
239+
escaped = repr(text).encode(encoding, 'backslashreplace')
240+
self.assertIn(escaped, data)
241+
224242

225243
def test_main():
226244
test.support.run_unittest(CmdLineTest)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ Core and Builtins
4949
Library
5050
-------
5151

52+
- Issue #10601: sys.displayhook uses 'backslashreplace' error handler on
53+
UnicodeEncodeError.
54+
5255
- Add the "display" and "undisplay" pdb commands.
5356

5457
- Issue #7245: Add a SIGINT handler in pdb that allows to break a program

Python/sysmodule.c

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,76 @@ PySys_SetObject(const char *name, PyObject *v)
6565
return PyDict_SetItemString(sd, name, v);
6666
}
6767

68+
/* Write repr(o) to sys.stdout using sys.stdout.encoding and 'backslashreplace'
69+
error handler. If sys.stdout has a buffer attribute, use
70+
sys.stdout.buffer.write(encoded), otherwise redecode the string and use
71+
sys.stdout.write(redecoded).
72+
73+
Helper function for sys_displayhook(). */
74+
static int
75+
sys_displayhook_unencodable(PyObject *outf, PyObject *o)
76+
{
77+
PyObject *stdout_encoding = NULL;
78+
PyObject *encoded, *escaped_str, *repr_str, *buffer, *result;
79+
char *stdout_encoding_str;
80+
int ret;
81+
82+
stdout_encoding = PyObject_GetAttrString(outf, "encoding");
83+
if (stdout_encoding == NULL)
84+
goto error;
85+
stdout_encoding_str = _PyUnicode_AsString(stdout_encoding);
86+
if (stdout_encoding_str == NULL)
87+
goto error;
88+
89+
repr_str = PyObject_Repr(o);
90+
if (repr_str == NULL)
91+
goto error;
92+
encoded = PyUnicode_AsEncodedString(repr_str,
93+
stdout_encoding_str,
94+
"backslashreplace");
95+
Py_DECREF(repr_str);
96+
if (encoded == NULL)
97+
goto error;
98+
99+
buffer = PyObject_GetAttrString(outf, "buffer");
100+
if (buffer) {
101+
result = PyObject_CallMethod(buffer, "write", "(O)", encoded);
102+
Py_DECREF(buffer);
103+
Py_DECREF(encoded);
104+
if (result == NULL)
105+
goto error;
106+
Py_DECREF(result);
107+
}
108+
else {
109+
PyErr_Clear();
110+
escaped_str = PyUnicode_FromEncodedObject(encoded,
111+
stdout_encoding_str,
112+
"strict");
113+
Py_DECREF(encoded);
114+
if (PyFile_WriteObject(escaped_str, outf, Py_PRINT_RAW) != 0) {
115+
Py_DECREF(escaped_str);
116+
goto error;
117+
}
118+
Py_DECREF(escaped_str);
119+
}
120+
ret = 0;
121+
goto finally;
122+
123+
error:
124+
ret = -1;
125+
finally:
126+
Py_XDECREF(stdout_encoding);
127+
return ret;
128+
}
129+
68130
static PyObject *
69131
sys_displayhook(PyObject *self, PyObject *o)
70132
{
71133
PyObject *outf;
72134
PyInterpreterState *interp = PyThreadState_GET()->interp;
73135
PyObject *modules = interp->modules;
74136
PyObject *builtins = PyDict_GetItemString(modules, "builtins");
137+
int err;
75138

76139
if (builtins == NULL) {
77140
PyErr_SetString(PyExc_RuntimeError, "lost builtins module");
@@ -92,8 +155,19 @@ sys_displayhook(PyObject *self, PyObject *o)
92155
PyErr_SetString(PyExc_RuntimeError, "lost sys.stdout");
93156
return NULL;
94157
}
95-
if (PyFile_WriteObject(o, outf, 0) != 0)
96-
return NULL;
158+
if (PyFile_WriteObject(o, outf, 0) != 0) {
159+
if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
160+
/* repr(o) is not encodable to sys.stdout.encoding with
161+
* sys.stdout.errors error handler (which is probably 'strict') */
162+
PyErr_Clear();
163+
err = sys_displayhook_unencodable(outf, o);
164+
if (err)
165+
return NULL;
166+
}
167+
else {
168+
return NULL;
169+
}
170+
}
97171
if (PyFile_WriteString("\n", outf) != 0)
98172
return NULL;
99173
if (PyObject_SetAttrString(builtins, "_", o) != 0)

0 commit comments

Comments
 (0)