Skip to content

Commit e8251dc

Browse files
ZeroIntensitypicnixzvstinner
authored
gh-134170: Add colorization to unraisable exceptions (#134183)
Default implementation of sys.unraisablehook() now uses traceback._print_exception_bltin() to print exceptions with colorized text. Co-authored-by: Bénédikt Tran <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent 8943bb7 commit e8251dc

File tree

11 files changed

+55
-6
lines changed

11 files changed

+55
-6
lines changed

Doc/library/sys.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2152,11 +2152,16 @@ always available. Unless explicitly noted otherwise, all variables are read-only
21522152

21532153
The default hook formats :attr:`!err_msg` and :attr:`!object` as:
21542154
``f'{err_msg}: {object!r}'``; use "Exception ignored in" error message
2155-
if :attr:`!err_msg` is ``None``.
2155+
if :attr:`!err_msg` is ``None``. Similar to the :mod:`traceback` module,
2156+
this adds color to exceptions by default. This can be disabled using
2157+
:ref:`environment variables <using-on-controlling-color>`.
21562158

21572159
:func:`sys.unraisablehook` can be overridden to control how unraisable
21582160
exceptions are handled.
21592161

2162+
.. versionchanged:: next
2163+
Exceptions are now printed with colorful text.
2164+
21602165
.. seealso::
21612166

21622167
:func:`excepthook` which handles uncaught exceptions.

Doc/whatsnew/3.15.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ Other language changes
200200
* Several error messages incorrectly using the term "argument" have been corrected.
201201
(Contributed by Stan Ulbrych in :gh:`133382`.)
202202

203+
* Unraisable exceptions are now highlighted with color by default. This can be
204+
controlled by :ref:`environment variables <using-on-controlling-color>`.
205+
(Contributed by Peter Bierma in :gh:`134170`.)
206+
203207

204208
New modules
205209
===========

Lib/test/test_capi/test_exceptions.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import textwrap
77

88
from test import support
9-
from test.support import import_helper
9+
from test.support import import_helper, force_not_colorized
1010
from test.support.os_helper import TESTFN, TESTFN_UNDECODABLE
1111
from test.support.script_helper import assert_python_failure, assert_python_ok
1212
from test.support.testcase import ExceptionIsLikeMixin
@@ -337,6 +337,10 @@ def test_err_writeunraisable(self):
337337
self.assertIsNone(cm.unraisable.err_msg)
338338
self.assertIsNone(cm.unraisable.object)
339339

340+
@force_not_colorized
341+
def test_err_writeunraisable_lines(self):
342+
writeunraisable = _testcapi.err_writeunraisable
343+
340344
with (support.swap_attr(sys, 'unraisablehook', None),
341345
support.captured_stderr() as stderr):
342346
writeunraisable(CustomError('oops!'), hex)
@@ -387,6 +391,10 @@ def test_err_formatunraisable(self):
387391
self.assertIsNone(cm.unraisable.err_msg)
388392
self.assertIsNone(cm.unraisable.object)
389393

394+
@force_not_colorized
395+
def test_err_formatunraisable_lines(self):
396+
formatunraisable = _testcapi.err_formatunraisable
397+
390398
with (support.swap_attr(sys, 'unraisablehook', None),
391399
support.captured_stderr() as stderr):
392400
formatunraisable(CustomError('oops!'), b'Error in %R', [])

Lib/test/test_cmd_line.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ def test_unmached_quote(self):
489489
self.assertRegex(err.decode('ascii', 'ignore'), 'SyntaxError')
490490
self.assertEqual(b'', out)
491491

492+
@force_not_colorized
492493
def test_stdout_flush_at_shutdown(self):
493494
# Issue #5319: if stdout.flush() fails at shutdown, an error should
494495
# be printed out.

Lib/test/test_concurrent_futures/test_shutdown.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def test_interpreter_shutdown(self):
4949
self.assertFalse(err)
5050
self.assertEqual(out.strip(), b"apple")
5151

52+
@support.force_not_colorized
5253
def test_submit_after_interpreter_shutdown(self):
5354
# Test the atexit hook for shutdown of worker threads and processes
5455
rc, out, err = assert_python_ok('-c', """if 1:

Lib/test/test_signal.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import unittest
1515
from test import support
1616
from test.support import (
17-
is_apple, is_apple_mobile, os_helper, threading_helper
17+
force_not_colorized, is_apple, is_apple_mobile, os_helper, threading_helper
1818
)
1919
from test.support.script_helper import assert_python_ok, spawn_python
2020
try:
@@ -353,6 +353,7 @@ def check_signum(signals):
353353

354354
@unittest.skipIf(_testcapi is None, 'need _testcapi')
355355
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
356+
@force_not_colorized
356357
def test_wakeup_write_error(self):
357358
# Issue #16105: write() errors in the C signal handler should not
358359
# pass silently.

Lib/test/test_sys.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,7 @@ def test_disable_gil_abi(self):
13401340

13411341

13421342
@test.support.cpython_only
1343+
@force_not_colorized
13431344
class UnraisableHookTest(unittest.TestCase):
13441345
def test_original_unraisablehook(self):
13451346
_testcapi = import_helper.import_module('_testcapi')

Lib/test/test_threading.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,6 +2494,7 @@ def test_atexit_called_once(self):
24942494

24952495
self.assertFalse(err)
24962496

2497+
@force_not_colorized
24972498
def test_atexit_after_shutdown(self):
24982499
# The only way to do this is by registering an atexit within
24992500
# an atexit, which is intended to raise an exception.

Lib/traceback.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
137137
BUILTIN_EXCEPTION_LIMIT = object()
138138

139139

140-
def _print_exception_bltin(exc, /):
141-
file = sys.stderr if sys.stderr is not None else sys.__stderr__
140+
def _print_exception_bltin(exc, file=None, /):
141+
if file is None:
142+
file = sys.stderr if sys.stderr is not None else sys.__stderr__
142143
colorize = _colorize.can_colorize(file=file)
143144
return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize)
144145

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add colorization to :func:`sys.unraisablehook` by default.

0 commit comments

Comments
 (0)