Skip to content

Commit 8cbffc4

Browse files
authored
bpo-37467: Fix PyErr_Display() for bytes filename (GH-14504) (GH-14515)
Fix sys.excepthook() and PyErr_Display() if a filename is a bytes string. For example, for a SyntaxError exception where the filename attribute is a bytes string. Cleanup also test_sys: * Sort imports. * Rename numruns global var to INTERN_NUMRUNS. * Add DisplayHookTest and ExceptHookTest test case classes. * Don't save/restore sys.stdout and sys.displayhook using setUp()/tearDown(): do it in each test method. * Test error case (call hook with no argument) after the success case. (cherry picked from commit f9b7457)
1 parent 45c10da commit 8cbffc4

File tree

3 files changed

+79
-49
lines changed

3 files changed

+79
-49
lines changed

Lib/test/test_sys.py

Lines changed: 75 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,103 @@
1-
import unittest, test.support
1+
from test import support
22
from test.support.script_helper import assert_python_ok, assert_python_failure
3-
import sys, io, os
3+
import builtins
4+
import codecs
5+
import gc
6+
import locale
7+
import operator
8+
import os
49
import struct
510
import subprocess
11+
import sys
12+
import sysconfig
13+
import test.support
614
import textwrap
15+
import unittest
716
import warnings
8-
import operator
9-
import codecs
10-
import gc
11-
import sysconfig
12-
import locale
13-
import threading
17+
1418

1519
# count the number of test runs, used to create unique
1620
# strings to intern in test_intern()
17-
numruns = 0
21+
INTERN_NUMRUNS = 0
1822

1923

20-
class SysModuleTest(unittest.TestCase):
24+
class DisplayHookTest(unittest.TestCase):
2125

22-
def setUp(self):
23-
self.orig_stdout = sys.stdout
24-
self.orig_stderr = sys.stderr
25-
self.orig_displayhook = sys.displayhook
26+
def test_original_displayhook(self):
27+
dh = sys.__displayhook__
2628

27-
def tearDown(self):
28-
sys.stdout = self.orig_stdout
29-
sys.stderr = self.orig_stderr
30-
sys.displayhook = self.orig_displayhook
31-
test.support.reap_children()
29+
with support.captured_stdout() as out:
30+
dh(42)
3231

33-
def test_original_displayhook(self):
34-
import builtins
35-
out = io.StringIO()
36-
sys.stdout = out
32+
self.assertEqual(out.getvalue(), "42\n")
33+
self.assertEqual(builtins._, 42)
3734

38-
dh = sys.__displayhook__
35+
del builtins._
3936

40-
self.assertRaises(TypeError, dh)
41-
if hasattr(builtins, "_"):
42-
del builtins._
37+
with support.captured_stdout() as out:
38+
dh(None)
4339

44-
dh(None)
4540
self.assertEqual(out.getvalue(), "")
4641
self.assertTrue(not hasattr(builtins, "_"))
47-
dh(42)
48-
self.assertEqual(out.getvalue(), "42\n")
49-
self.assertEqual(builtins._, 42)
5042

51-
del sys.stdout
52-
self.assertRaises(RuntimeError, dh, 42)
43+
# sys.displayhook() requires arguments
44+
self.assertRaises(TypeError, dh)
45+
46+
stdout = sys.stdout
47+
try:
48+
del sys.stdout
49+
self.assertRaises(RuntimeError, dh, 42)
50+
finally:
51+
sys.stdout = stdout
5352

5453
def test_lost_displayhook(self):
55-
del sys.displayhook
56-
code = compile("42", "<string>", "single")
57-
self.assertRaises(RuntimeError, eval, code)
54+
displayhook = sys.displayhook
55+
try:
56+
del sys.displayhook
57+
code = compile("42", "<string>", "single")
58+
self.assertRaises(RuntimeError, eval, code)
59+
finally:
60+
sys.displayhook = displayhook
5861

5962
def test_custom_displayhook(self):
6063
def baddisplayhook(obj):
6164
raise ValueError
62-
sys.displayhook = baddisplayhook
63-
code = compile("42", "<string>", "single")
64-
self.assertRaises(ValueError, eval, code)
6565

66-
def test_original_excepthook(self):
67-
err = io.StringIO()
68-
sys.stderr = err
66+
with support.swap_attr(sys, 'displayhook', baddisplayhook):
67+
code = compile("42", "<string>", "single")
68+
self.assertRaises(ValueError, eval, code)
69+
6970

70-
eh = sys.__excepthook__
71+
class ExceptHookTest(unittest.TestCase):
7172

72-
self.assertRaises(TypeError, eh)
73+
def test_original_excepthook(self):
7374
try:
7475
raise ValueError(42)
7576
except ValueError as exc:
76-
eh(*sys.exc_info())
77+
with support.captured_stderr() as err:
78+
sys.__excepthook__(*sys.exc_info())
7779

7880
self.assertTrue(err.getvalue().endswith("ValueError: 42\n"))
7981

82+
self.assertRaises(TypeError, sys.__excepthook__)
83+
84+
def test_excepthook_bytes_filename(self):
85+
# bpo-37467: sys.excepthook() must not crash if a filename
86+
# is a bytes string
87+
with warnings.catch_warnings():
88+
warnings.simplefilter('ignore', BytesWarning)
89+
90+
try:
91+
raise SyntaxError("msg", (b"bytes_filename", 123, 0, "text"))
92+
except SyntaxError as exc:
93+
with support.captured_stderr() as err:
94+
sys.__excepthook__(*sys.exc_info())
95+
96+
err = err.getvalue()
97+
self.assertIn(""" File "b'bytes_filename'", line 123\n""", err)
98+
self.assertIn(""" text\n""", err)
99+
self.assertTrue(err.endswith("SyntaxError: msg\n"))
100+
80101
def test_excepthook(self):
81102
with test.support.captured_output("stderr") as stderr:
82103
sys.excepthook(1, '1', 1)
@@ -86,6 +107,12 @@ def test_excepthook(self):
86107
# FIXME: testing the code for a lost or replaced excepthook in
87108
# Python/pythonrun.c::PyErr_PrintEx() is tricky.
88109

110+
111+
class SysModuleTest(unittest.TestCase):
112+
113+
def tearDown(self):
114+
test.support.reap_children()
115+
89116
def test_exit(self):
90117
# call with two arguments
91118
self.assertRaises(TypeError, sys.exit, 42, 42)
@@ -502,10 +529,10 @@ def test_43581(self):
502529
self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding)
503530

504531
def test_intern(self):
505-
global numruns
506-
numruns += 1
532+
global INTERN_NUMRUNS
533+
INTERN_NUMRUNS += 1
507534
self.assertRaises(TypeError, sys.intern)
508-
s = "never interned before" + str(numruns)
535+
s = "never interned before" + str(INTERN_NUMRUNS)
509536
self.assertTrue(sys.intern(s) is s)
510537
s2 = s.swapcase().swapcase()
511538
self.assertTrue(sys.intern(s2) is s)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`sys.excepthook` and :c:func:`PyErr_Display` if a filename is a
2+
bytes string. For example, for a SyntaxError exception where the filename
3+
attribute is a bytes string.

Python/pythonrun.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ print_exception(PyObject *f, PyObject *value)
750750
Py_DECREF(value);
751751
value = message;
752752

753-
line = PyUnicode_FromFormat(" File \"%U\", line %d\n",
753+
line = PyUnicode_FromFormat(" File \"%S\", line %d\n",
754754
filename, lineno);
755755
Py_DECREF(filename);
756756
if (line != NULL) {

0 commit comments

Comments
 (0)