Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2c1108b
Add colour to doctest output and create _colorize module
hugovk Apr 6, 2024
d27c0a8
Use _colorize in traceback module
hugovk Apr 6, 2024
bb591b6
Fix whitespace
hugovk Apr 6, 2024
42079be
Use f-strings
hugovk Apr 6, 2024
0088579
Remove underscores from members of an underscored module
hugovk Apr 6, 2024
d3034fa
Add blurb
hugovk Apr 6, 2024
39780cb
Remove underscores from members of an underscored module
hugovk Apr 6, 2024
c5aec15
Revert "Fix whitespace"
hugovk Apr 6, 2024
7e40133
Move _colorize to stdlib block, colour->color
hugovk Apr 6, 2024
e484465
Move imports together
hugovk Apr 6, 2024
1c7b025
Move imports together
hugovk Apr 6, 2024
ab2c94c
Move imports together
hugovk Apr 6, 2024
1aaeab8
Revert notests -> no_tests
hugovk Apr 6, 2024
cd02e4a
Revert "Use f-strings"
hugovk Apr 6, 2024
06543ff
Fix local tests
hugovk Apr 6, 2024
31c6647
Use red divider for failed test
hugovk Apr 7, 2024
9be3d81
Fix local tests
hugovk Apr 7, 2024
e4ff3e3
Less red
hugovk Apr 7, 2024
b62500a
Revert unnecessary changes
hugovk Apr 7, 2024
eb4f8dc
Move colour tests to test__colorize.py
hugovk Apr 7, 2024
976bfb4
Refactor asserts
hugovk Apr 7, 2024
ad7a946
Add missing captured_output to test.support's __all__ to fix IDE warning
hugovk Apr 7, 2024
796e9f2
Only move test_colorized_detection_checks_for_environment_variables f…
hugovk Apr 7, 2024
99d4d0c
Apply suggestions from code review
hugovk Apr 7, 2024
95b9831
Use unittest's enterContext
hugovk Apr 7, 2024
d5417b4
Merge remote-tracking branch 'upstream/main' into doctest-tidy-output…
hugovk Apr 16, 2024
ece3ce0
Keep colorize functionality in traceback module for now
hugovk Apr 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use _colorize in traceback module
  • Loading branch information
hugovk committed Apr 6, 2024
commit d27c0a853d44d768ef7e0f7fdcaacf0ab04035ae
55 changes: 29 additions & 26 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
from functools import partial
from pathlib import Path

import _colorize

MODULE_PREFIX = f'{__name__}.' if __name__ == '__main__' else ''

test_code = namedtuple('code', ['co_filename', 'co_name'])
Expand All @@ -45,12 +47,12 @@ class TracebackCases(unittest.TestCase):
# formatting of SyntaxErrors works based on changes for 2.1.
def setUp(self):
super().setUp()
self.colorize = traceback._COLORIZE
traceback._COLORIZE = False
self.colorize = _colorize._COLORIZE
_colorize._COLORIZE = False

def tearDown(self):
super().tearDown()
traceback._COLORIZE = self.colorize
_colorize._COLORIZE = self.colorize

def get_exception_format(self, func, exc):
try:
Expand Down Expand Up @@ -4291,9 +4293,9 @@ def bar():
e, capture_locals=True
)
lines = "".join(exc.format(colorize=True))
red = traceback._ANSIColors.RED
boldr = traceback._ANSIColors.BOLD_RED
reset = traceback._ANSIColors.RESET
red = _colorize._ANSIColors.RED
boldr = _colorize._ANSIColors.BOLD_RED
reset = _colorize._ANSIColors.RESET
self.assertIn("y = " + red + "x['a']['b']" + reset + boldr + "['c']" + reset, lines)
self.assertIn("return " + red + "foo" + reset + boldr + "(1,2,3,4)" + reset, lines)
self.assertIn("return " + red + "baz" + reset + boldr + "(1," + reset, lines)
Expand All @@ -4309,11 +4311,11 @@ def test_colorized_syntax_error(self):
e, capture_locals=True
)
actual = "".join(exc.format(colorize=True))
red = traceback._ANSIColors.RED
magenta = traceback._ANSIColors.MAGENTA
boldm = traceback._ANSIColors.BOLD_MAGENTA
boldr = traceback._ANSIColors.BOLD_RED
reset = traceback._ANSIColors.RESET
red = _colorize._ANSIColors.RED
magenta = _colorize._ANSIColors.MAGENTA
boldm = _colorize._ANSIColors.BOLD_MAGENTA
boldr = _colorize._ANSIColors.BOLD_RED
reset = _colorize._ANSIColors.RESET
expected = "".join([
f' File {magenta}"<string>"{reset}, line {magenta}1{reset}\n',
f' a {boldr}${reset} b\n',
Expand All @@ -4332,15 +4334,15 @@ def foo():
self.fail("No exception thrown.")
except Exception as e:
with captured_output("stderr") as tbstderr:
with unittest.mock.patch('traceback._can_colorize', return_value=True):
with unittest.mock.patch('_colorize._can_colorize', return_value=True):
exception_print(e)
actual = tbstderr.getvalue().splitlines()

red = traceback._ANSIColors.RED
boldr = traceback._ANSIColors.BOLD_RED
magenta = traceback._ANSIColors.MAGENTA
boldm = traceback._ANSIColors.BOLD_MAGENTA
reset = traceback._ANSIColors.RESET
red = _colorize._ANSIColors.RED
boldr = _colorize._ANSIColors.BOLD_RED
magenta = _colorize._ANSIColors.MAGENTA
boldm = _colorize._ANSIColors.BOLD_MAGENTA
reset = _colorize._ANSIColors.RESET
lno_foo = foo.__code__.co_firstlineno
expected = ['Traceback (most recent call last):',
f' File {magenta}"{__file__}"{reset}, '
Expand All @@ -4363,23 +4365,24 @@ def test_colorized_detection_checks_for_environment_variables(self):
with unittest.mock.patch("os.isatty") as isatty_mock:
isatty_mock.return_value = True
with unittest.mock.patch("os.environ", {'TERM': 'dumb'}):
self.assertEqual(traceback._can_colorize(), False)
self.assertEqual(_colorize._can_colorize(), False)
with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}):
self.assertEqual(traceback._can_colorize(), True)
self.assertEqual(_colorize._can_colorize(), True)
with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}):
self.assertEqual(traceback._can_colorize(), False)
self.assertEqual(_colorize._can_colorize(), False)
with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}):
self.assertEqual(traceback._can_colorize(), False)
self.assertEqual(_colorize._can_colorize(), False)
with unittest.mock.patch("os.environ", {'NO_COLOR': '1', "PYTHON_COLORS": '1'}):
self.assertEqual(traceback._can_colorize(), True)
self.assertEqual(_colorize._can_colorize(), True)
with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}):
self.assertEqual(traceback._can_colorize(), True)
self.assertEqual(_colorize._can_colorize(), True)
with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', 'NO_COLOR': '1'}):
self.assertEqual(traceback._can_colorize(), False)
self.assertEqual(_colorize._can_colorize(), False)
with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}):
self.assertEqual(traceback._can_colorize(), False)
self.assertEqual(_colorize._can_colorize(), False)
isatty_mock.return_value = False
self.assertEqual(traceback._can_colorize(), False)
self.assertEqual(_colorize._can_colorize(), False)


if __name__ == "__main__":
unittest.main()
41 changes: 5 additions & 36 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"""Extract, format and print information about Python stack traces."""

import os
import io
import collections.abc
import itertools
import linecache
Expand All @@ -10,19 +8,20 @@
import warnings
from contextlib import suppress

from _colorize import _ANSIColors

__all__ = ['extract_stack', 'extract_tb', 'format_exception',
'format_exception_only', 'format_list', 'format_stack',
'format_tb', 'print_exc', 'format_exc', 'print_exception',
'print_last', 'print_stack', 'print_tb', 'clear_frames',
'FrameSummary', 'StackSummary', 'TracebackException',
'walk_stack', 'walk_tb']


#
# Formatting and printing lists of traceback lines.
#

_COLORIZE = True

def print_list(extracted_list, file=None):
"""Print the list of tuples as returned by extract_tb() or
extract_stack() as a formatted stack trace to the given file."""
Expand Down Expand Up @@ -133,33 +132,10 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \

BUILTIN_EXCEPTION_LIMIT = object()

def _can_colorize():
if sys.platform == "win32":
try:
import nt
if not nt._supports_virtual_terminal():
return False
except (ImportError, AttributeError):
return False

if os.environ.get("PYTHON_COLORS") == "0":
return False
if os.environ.get("PYTHON_COLORS") == "1":
return True
if "NO_COLOR" in os.environ:
return False
if not _COLORIZE:
return False
if "FORCE_COLOR" in os.environ:
return True
if os.environ.get("TERM") == "dumb":
return False
try:
return os.isatty(sys.stderr.fileno())
except io.UnsupportedOperation:
return sys.stderr.isatty()

def _print_exception_bltin(exc, /):
from _colorize import _can_colorize

file = sys.stderr if sys.stderr is not None else sys.__stderr__
colorize = _can_colorize()
return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize)
Expand Down Expand Up @@ -443,13 +419,6 @@ def _get_code_position(code, instruction_index):

_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c.

class _ANSIColors:
RED = '\x1b[31m'
BOLD_RED = '\x1b[1;31m'
MAGENTA = '\x1b[35m'
BOLD_MAGENTA = '\x1b[1;35m'
GREY = '\x1b[90m'
RESET = '\x1b[0m'

class StackSummary(list):
"""A list of FrameSummary objects, representing a stack of frames."""
Expand Down