Commit f9b7457b authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

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.
parent ec6c1bd0
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 codecs
import gc
import io
import locale
import operator
import os
import struct import struct
import subprocess import subprocess
import sys
import sysconfig
import test.support
import textwrap import textwrap
import unittest
import warnings import warnings
import operator
import codecs
import gc
import sysconfig
import locale
# 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): def test_original_displayhook(self):
self.orig_stdout = sys.stdout dh = sys.__displayhook__
self.orig_stderr = sys.stderr
self.orig_displayhook = sys.displayhook
def tearDown(self): with support.captured_stdout() as out:
sys.stdout = self.orig_stdout dh(42)
sys.stderr = self.orig_stderr
sys.displayhook = self.orig_displayhook
test.support.reap_children()
def test_original_displayhook(self): self.assertEqual(out.getvalue(), "42\n")
import builtins self.assertEqual(builtins._, 42)
out = io.StringIO()
sys.stdout = out
dh = sys.__displayhook__ del builtins._
self.assertRaises(TypeError, dh) with support.captured_stdout() as out:
if hasattr(builtins, "_"): dh(None)
del builtins._
dh(None)
self.assertEqual(out.getvalue(), "") self.assertEqual(out.getvalue(), "")
self.assertTrue(not hasattr(builtins, "_")) self.assertTrue(not hasattr(builtins, "_"))
dh(42)
self.assertEqual(out.getvalue(), "42\n")
self.assertEqual(builtins._, 42)
del sys.stdout # sys.displayhook() requires arguments
self.assertRaises(RuntimeError, dh, 42) 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")
self.assertRaises(ValueError, eval, code)
def test_original_excepthook(self): with support.swap_attr(sys, 'displayhook', baddisplayhook):
err = io.StringIO() code = compile("42", "<string>", "single")
sys.stderr = err self.assertRaises(ValueError, eval, code)
eh = sys.__excepthook__
self.assertRaises(TypeError, eh) class ExceptHookTest(unittest.TestCase):
def test_original_excepthook(self):
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): ...@@ -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)
...@@ -492,10 +521,10 @@ class SysModuleTest(unittest.TestCase): ...@@ -492,10 +521,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)
......
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.
...@@ -797,7 +797,7 @@ print_exception(PyObject *f, PyObject *value) ...@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment