bpo-37467: Fix PyErr_Display() for bytes filename (GH-14504)

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 f9b7457bd7)

Co-authored-by: Victor Stinner <vstinner@redhat.com>
This commit is contained in:
Miss Islington (bot) 2019-07-01 08:11:15 -07:00 committed by GitHub
parent e224d2865a
commit 2683ded568
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 51 deletions

View file

@ -1,81 +1,104 @@
import unittest, test.support from test import support
from test.support.script_helper import assert_python_ok, assert_python_failure from test.support.script_helper import assert_python_ok, assert_python_failure
import sys, io, os import builtins
import struct
import subprocess
import textwrap
import warnings
import operator
import codecs import codecs
import gc import gc
import sysconfig import io
import locale import locale
import operator
import os
import struct
import subprocess
import sys
import sysconfig
import test.support
import textwrap
import unittest
import warnings
# count the number of test runs, used to create unique # count the number of test runs, used to create unique
# strings to intern in test_intern() # strings to intern in test_intern()
numruns = 0 INTERN_NUMRUNS = 0
class SysModuleTest(unittest.TestCase): class DisplayHookTest(unittest.TestCase):
def setUp(self):
self.orig_stdout = sys.stdout
self.orig_stderr = sys.stderr
self.orig_displayhook = sys.displayhook
def tearDown(self):
sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr
sys.displayhook = self.orig_displayhook
test.support.reap_children()
def test_original_displayhook(self): def test_original_displayhook(self):
import builtins
out = io.StringIO()
sys.stdout = out
dh = sys.__displayhook__ dh = sys.__displayhook__
self.assertRaises(TypeError, dh) with support.captured_stdout() as out:
if hasattr(builtins, "_"): dh(42)
del builtins._
dh(None)
self.assertEqual(out.getvalue(), "")
self.assertTrue(not hasattr(builtins, "_"))
dh(42)
self.assertEqual(out.getvalue(), "42\n") self.assertEqual(out.getvalue(), "42\n")
self.assertEqual(builtins._, 42) self.assertEqual(builtins._, 42)
del sys.stdout del builtins._
self.assertRaises(RuntimeError, dh, 42)
with support.captured_stdout() as out:
dh(None)
self.assertEqual(out.getvalue(), "")
self.assertTrue(not hasattr(builtins, "_"))
# sys.displayhook() requires arguments
self.assertRaises(TypeError, dh)
stdout = sys.stdout
try:
del sys.stdout
self.assertRaises(RuntimeError, dh, 42)
finally:
sys.stdout = stdout
def test_lost_displayhook(self): def test_lost_displayhook(self):
del sys.displayhook displayhook = sys.displayhook
code = compile("42", "<string>", "single") try:
self.assertRaises(RuntimeError, eval, code) del sys.displayhook
code = compile("42", "<string>", "single")
self.assertRaises(RuntimeError, eval, code)
finally:
sys.displayhook = displayhook
def test_custom_displayhook(self): def test_custom_displayhook(self):
def baddisplayhook(obj): def baddisplayhook(obj):
raise ValueError raise ValueError
sys.displayhook = baddisplayhook
code = compile("42", "<string>", "single") with support.swap_attr(sys, 'displayhook', baddisplayhook):
self.assertRaises(ValueError, eval, code) code = compile("42", "<string>", "single")
self.assertRaises(ValueError, eval, code)
class ExceptHookTest(unittest.TestCase):
def test_original_excepthook(self): def test_original_excepthook(self):
err = io.StringIO()
sys.stderr = err
eh = sys.__excepthook__
self.assertRaises(TypeError, eh)
try: try:
raise ValueError(42) raise ValueError(42)
except ValueError as exc: except ValueError as exc:
eh(*sys.exc_info()) with support.captured_stderr() as err:
sys.__excepthook__(*sys.exc_info())
self.assertTrue(err.getvalue().endswith("ValueError: 42\n")) self.assertTrue(err.getvalue().endswith("ValueError: 42\n"))
self.assertRaises(TypeError, sys.__excepthook__)
def test_excepthook_bytes_filename(self):
# bpo-37467: sys.excepthook() must not crash if a filename
# is a bytes string
with warnings.catch_warnings():
warnings.simplefilter('ignore', BytesWarning)
try:
raise SyntaxError("msg", (b"bytes_filename", 123, 0, "text"))
except SyntaxError as exc:
with support.captured_stderr() as err:
sys.__excepthook__(*sys.exc_info())
err = err.getvalue()
self.assertIn(""" File "b'bytes_filename'", line 123\n""", err)
self.assertIn(""" text\n""", err)
self.assertTrue(err.endswith("SyntaxError: msg\n"))
def test_excepthook(self): def test_excepthook(self):
with test.support.captured_output("stderr") as stderr: with test.support.captured_output("stderr") as stderr:
sys.excepthook(1, '1', 1) sys.excepthook(1, '1', 1)
@ -85,6 +108,12 @@ class SysModuleTest(unittest.TestCase):
# FIXME: testing the code for a lost or replaced excepthook in # FIXME: testing the code for a lost or replaced excepthook in
# Python/pythonrun.c::PyErr_PrintEx() is tricky. # Python/pythonrun.c::PyErr_PrintEx() is tricky.
class SysModuleTest(unittest.TestCase):
def tearDown(self):
test.support.reap_children()
def test_exit(self): def test_exit(self):
# call with two arguments # call with two arguments
self.assertRaises(TypeError, sys.exit, 42, 42) self.assertRaises(TypeError, sys.exit, 42, 42)
@ -501,10 +530,10 @@ class SysModuleTest(unittest.TestCase):
self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding) self.assertEqual(sys.__stdout__.encoding, sys.__stderr__.encoding)
def test_intern(self): def test_intern(self):
global numruns global INTERN_NUMRUNS
numruns += 1 INTERN_NUMRUNS += 1
self.assertRaises(TypeError, sys.intern) self.assertRaises(TypeError, sys.intern)
s = "never interned before" + str(numruns) s = "never interned before" + str(INTERN_NUMRUNS)
self.assertTrue(sys.intern(s) is s) self.assertTrue(sys.intern(s) is s)
s2 = s.swapcase().swapcase() s2 = s.swapcase().swapcase()
self.assertTrue(sys.intern(s2) is s) self.assertTrue(sys.intern(s2) is s)

View file

@ -0,0 +1,3 @@
Fix :func:`sys.excepthook` and :c:func:`PyErr_Display` if a filename is a
bytes string. For example, for a SyntaxError exception where the filename
attribute is a bytes string.

View file

@ -797,7 +797,7 @@ print_exception(PyObject *f, PyObject *value)
Py_DECREF(value); Py_DECREF(value);
value = message; value = message;
line = PyUnicode_FromFormat(" File \"%U\", line %d\n", line = PyUnicode_FromFormat(" File \"%S\", line %d\n",
filename, lineno); filename, lineno);
Py_DECREF(filename); Py_DECREF(filename);
if (line != NULL) { if (line != NULL) {