summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/_colorize.py64
-rw-r--r--Lib/doctest.py38
-rw-r--r--Lib/test/support/__init__.py9
-rw-r--r--Lib/test/test__colorize.py59
-rw-r--r--Lib/test/test_doctest/test_doctest.py56
-rw-r--r--Lib/test/test_traceback.py75
-rw-r--r--Lib/traceback.py96
-rw-r--r--Python/stdlib_module_names.h1
8 files changed, 218 insertions, 180 deletions
diff --git a/Lib/_colorize.py b/Lib/_colorize.py
new file mode 100644
index 0000000..845fb57
--- /dev/null
+++ b/Lib/_colorize.py
@@ -0,0 +1,64 @@
+import io
+import os
+import sys
+
+COLORIZE = True
+
+
+class ANSIColors:
+ BOLD_GREEN = "\x1b[1;32m"
+ BOLD_MAGENTA = "\x1b[1;35m"
+ BOLD_RED = "\x1b[1;31m"
+ GREEN = "\x1b[32m"
+ GREY = "\x1b[90m"
+ MAGENTA = "\x1b[35m"
+ RED = "\x1b[31m"
+ RESET = "\x1b[0m"
+ YELLOW = "\x1b[33m"
+
+
+NoColors = ANSIColors()
+
+for attr in dir(NoColors):
+ if not attr.startswith("__"):
+ setattr(NoColors, attr, "")
+
+
+def get_colors(colorize: bool = False) -> ANSIColors:
+ if colorize or can_colorize():
+ return ANSIColors()
+ else:
+ return NoColors
+
+
+def can_colorize() -> bool:
+ if sys.platform == "win32":
+ try:
+ import nt
+
+ if not nt._supports_virtual_terminal():
+ return False
+ except (ImportError, AttributeError):
+ return False
+ if not sys.flags.ignore_environment:
+ 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 not sys.flags.ignore_environment:
+ if "FORCE_COLOR" in os.environ:
+ return True
+ if os.environ.get("TERM") == "dumb":
+ return False
+
+ if not hasattr(sys.stderr, "fileno"):
+ return False
+
+ try:
+ return os.isatty(sys.stderr.fileno())
+ except io.UnsupportedOperation:
+ return sys.stderr.isatty()
diff --git a/Lib/doctest.py b/Lib/doctest.py
index d8c632e..c531e3c 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -104,7 +104,8 @@ import traceback
import unittest
from io import StringIO, IncrementalNewlineDecoder
from collections import namedtuple
-from traceback import _ANSIColors, _can_colorize
+import _colorize # Used in doctests
+from _colorize import ANSIColors, can_colorize
class TestResults(namedtuple('TestResults', 'failed attempted')):
@@ -1180,8 +1181,8 @@ class DocTestRunner:
The `run` method is used to process a single DocTest case. It
returns a TestResults instance.
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> tests = DocTestFinder().find(_TestClass)
>>> runner = DocTestRunner(verbose=False)
@@ -1234,7 +1235,7 @@ class DocTestRunner:
overriding the methods `report_start`, `report_success`,
`report_unexpected_exception`, and `report_failure`.
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
# This divider string is used to separate failure messages, and to
# separate sections of the summary.
@@ -1314,7 +1315,7 @@ class DocTestRunner:
def _failure_header(self, test, example):
red, reset = (
- (_ANSIColors.RED, _ANSIColors.RESET) if _can_colorize() else ("", "")
+ (ANSIColors.RED, ANSIColors.RESET) if can_colorize() else ("", "")
)
out = [f"{red}{self.DIVIDER}{reset}"]
if test.filename:
@@ -1556,8 +1557,8 @@ class DocTestRunner:
# Make sure sys.displayhook just prints the value to stdout
save_displayhook = sys.displayhook
sys.displayhook = sys.__displayhook__
- saved_can_colorize = traceback._can_colorize
- traceback._can_colorize = lambda: False
+ saved_can_colorize = _colorize.can_colorize
+ _colorize.can_colorize = lambda: False
color_variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None}
for key in color_variables:
color_variables[key] = os.environ.pop(key, None)
@@ -1569,7 +1570,7 @@ class DocTestRunner:
sys.settrace(save_trace)
linecache.getlines = self.save_linecache_getlines
sys.displayhook = save_displayhook
- traceback._can_colorize = saved_can_colorize
+ _colorize.can_colorize = saved_can_colorize
for key, value in color_variables.items():
if value is not None:
os.environ[key] = value
@@ -1609,20 +1610,13 @@ class DocTestRunner:
else:
failed.append((name, (failures, tries, skips)))
- if _can_colorize():
- bold_green = _ANSIColors.BOLD_GREEN
- bold_red = _ANSIColors.BOLD_RED
- green = _ANSIColors.GREEN
- red = _ANSIColors.RED
- reset = _ANSIColors.RESET
- yellow = _ANSIColors.YELLOW
- else:
- bold_green = ""
- bold_red = ""
- green = ""
- red = ""
- reset = ""
- yellow = ""
+ ansi = _colorize.get_colors()
+ bold_green = ansi.BOLD_GREEN
+ bold_red = ansi.BOLD_RED
+ green = ansi.GREEN
+ red = ansi.RED
+ reset = ansi.RESET
+ yellow = ansi.YELLOW
if verbose:
if notests:
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 52573e6..999fffb 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -2579,20 +2579,21 @@ def copy_python_src_ignore(path, names):
}
return ignored
+
def force_not_colorized(func):
"""Force the terminal not to be colorized."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
- import traceback
- original_fn = traceback._can_colorize
+ import _colorize
+ original_fn = _colorize.can_colorize
variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None}
try:
for key in variables:
variables[key] = os.environ.pop(key, None)
- traceback._can_colorize = lambda: False
+ _colorize.can_colorize = lambda: False
return func(*args, **kwargs)
finally:
- traceback._can_colorize = original_fn
+ _colorize.can_colorize = original_fn
for key, value in variables.items():
if value is not None:
os.environ[key] = value
diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py
new file mode 100644
index 0000000..d55b97a
--- /dev/null
+++ b/Lib/test/test__colorize.py
@@ -0,0 +1,59 @@
+import contextlib
+import sys
+import unittest
+import unittest.mock
+import _colorize
+from test.support import force_not_colorized
+
+ORIGINAL_CAN_COLORIZE = _colorize.can_colorize
+
+
+def setUpModule():
+ _colorize.can_colorize = lambda: False
+
+
+def tearDownModule():
+ _colorize.can_colorize = ORIGINAL_CAN_COLORIZE
+
+
+class TestColorizeFunction(unittest.TestCase):
+ @force_not_colorized
+ def test_colorized_detection_checks_for_environment_variables(self):
+ if sys.platform == "win32":
+ virtual_patching = unittest.mock.patch("nt._supports_virtual_terminal",
+ return_value=True)
+ else:
+ virtual_patching = contextlib.nullcontext()
+ with virtual_patching:
+
+ flags = unittest.mock.MagicMock(ignore_environment=False)
+ with (unittest.mock.patch("os.isatty") as isatty_mock,
+ unittest.mock.patch("sys.flags", flags),
+ unittest.mock.patch("_colorize.can_colorize", ORIGINAL_CAN_COLORIZE)):
+ isatty_mock.return_value = True
+ with unittest.mock.patch("os.environ", {'TERM': 'dumb'}):
+ self.assertEqual(_colorize.can_colorize(), False)
+ with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}):
+ self.assertEqual(_colorize.can_colorize(), True)
+ with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}):
+ self.assertEqual(_colorize.can_colorize(), False)
+ with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}):
+ self.assertEqual(_colorize.can_colorize(), False)
+ with unittest.mock.patch("os.environ",
+ {'NO_COLOR': '1', "PYTHON_COLORS": '1'}):
+ self.assertEqual(_colorize.can_colorize(), True)
+ with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}):
+ self.assertEqual(_colorize.can_colorize(), True)
+ with unittest.mock.patch("os.environ",
+ {'FORCE_COLOR': '1', 'NO_COLOR': '1'}):
+ self.assertEqual(_colorize.can_colorize(), False)
+ with unittest.mock.patch("os.environ",
+ {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}):
+ self.assertEqual(_colorize.can_colorize(), False)
+ isatty_mock.return_value = False
+ with unittest.mock.patch("os.environ", {}):
+ self.assertEqual(_colorize.can_colorize(), False)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py
index 0f1e584..3a173e8 100644
--- a/Lib/test/test_doctest/test_doctest.py
+++ b/Lib/test/test_doctest/test_doctest.py
@@ -16,7 +16,7 @@ import unittest
import tempfile
import types
import contextlib
-import traceback
+import _colorize
def doctest_skip_if(condition):
@@ -893,8 +893,8 @@ Unit tests for the `DocTestRunner` class.
DocTestRunner is used to run DocTest test cases, and to accumulate
statistics. Here's a simple DocTest case we can use:
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> def f(x):
... '''
@@ -951,7 +951,7 @@ the failure and proceeds to the next example:
ok
TestResults(failed=1, attempted=3)
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
def verbose_flag(): r"""
The `verbose` flag makes the test runner generate more detailed
@@ -1027,8 +1027,8 @@ An expected exception is specified with a traceback message. The
lines between the first line and the type/value may be omitted or
replaced with any other string:
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> def f(x):
... '''
@@ -1261,7 +1261,7 @@ unexpected exception:
ZeroDivisionError: integer division or modulo by zero
TestResults(failed=1, attempted=1)
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
def displayhook(): r"""
Test that changing sys.displayhook doesn't matter for doctest.
@@ -1303,8 +1303,8 @@ together).
The DONT_ACCEPT_TRUE_FOR_1 flag disables matches between True/False
and 1/0:
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> def f(x):
... '>>> True\n1\n'
@@ -1725,7 +1725,7 @@ more than one flag value. Here we verify that's fixed:
Clean up.
>>> del doctest.OPTIONFLAGS_BY_NAME[unlikely]
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
@@ -1736,8 +1736,8 @@ Option directives can be used to turn option flags on or off for a
single example. To turn an option on for an example, follow that
example with a comment of the form ``# doctest: +OPTION``:
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> def f(x): r'''
... >>> print(list(range(10))) # should fail: no ellipsis
@@ -1947,7 +1947,7 @@ source:
Traceback (most recent call last):
ValueError: line 0 of the doctest for s has an option directive on a line with no example: '# doctest: +ELLIPSIS'
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
def test_testsource(): r"""
@@ -2031,8 +2031,8 @@ if not hasattr(sys, 'gettrace') or not sys.gettrace():
with a version that restores stdout. This is necessary for you to
see debugger output.
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> doc = '''
... >>> x = 42
@@ -2157,7 +2157,7 @@ if not hasattr(sys, 'gettrace') or not sys.gettrace():
9
TestResults(failed=1, attempted=3)
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
def test_pdb_set_trace_nested():
@@ -2694,8 +2694,8 @@ calling module. The return value is (#failures, #tests).
We don't want color or `-v` in sys.argv for these tests.
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> save_argv = sys.argv
>>> if '-v' in sys.argv:
@@ -2863,7 +2863,7 @@ Test the verbose output:
TestResults(failed=0, attempted=2)
>>> doctest.master = None # Reset master.
>>> sys.argv = save_argv
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
class TestImporter(importlib.abc.MetaPathFinder, importlib.abc.ResourceLoader):
@@ -3001,8 +3001,8 @@ if supports_unicode:
def test_unicode(): """
Check doctest with a non-ascii filename:
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> doc = '''
... >>> raise Exception('clé')
@@ -3030,7 +3030,7 @@ Check doctest with a non-ascii filename:
Exception: clé
TestResults(failed=1, attempted=1)
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
@@ -3325,8 +3325,8 @@ def test_run_doctestsuite_multiple_times():
def test_exception_with_note(note):
"""
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> test_exception_with_note('Note')
Traceback (most recent call last):
@@ -3378,7 +3378,7 @@ def test_exception_with_note(note):
note
TestResults(failed=1, attempted=...)
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
exc = ValueError('Text')
exc.add_note(note)
@@ -3459,8 +3459,8 @@ def test_syntax_error_subclass_from_stdlib():
def test_syntax_error_with_incorrect_expected_note():
"""
- >>> save_colorize = traceback._COLORIZE
- >>> traceback._COLORIZE = False
+ >>> save_colorize = _colorize.COLORIZE
+ >>> _colorize.COLORIZE = False
>>> def f(x):
... r'''
@@ -3491,7 +3491,7 @@ def test_syntax_error_with_incorrect_expected_note():
note2
TestResults(failed=1, attempted=...)
- >>> traceback._COLORIZE = save_colorize
+ >>> _colorize.COLORIZE = save_colorize
"""
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 41f8251..8969e01 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -26,9 +26,9 @@ from test.support import force_not_colorized
import json
import textwrap
import traceback
-import contextlib
from functools import partial
from pathlib import Path
+import _colorize
MODULE_PREFIX = f'{__name__}.' if __name__ == '__main__' else ''
@@ -40,25 +40,18 @@ test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next', 'tb_lasti'])
LEVENSHTEIN_DATA_FILE = Path(__file__).parent / 'levenshtein_examples.json'
-ORIGINAL_CAN_COLORIZE = traceback._can_colorize
-
-def setUpModule():
- traceback._can_colorize = lambda: False
-
-def tearDownModule():
- traceback._can_colorize = ORIGINAL_CAN_COLORIZE
class TracebackCases(unittest.TestCase):
# For now, a very minimal set of tests. I want to be sure that
# 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:
@@ -4478,9 +4471,9 @@ class TestColorizedTraceback(unittest.TestCase):
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 + "(lambda *args: foo(*args))" + reset + boldr + "(1,2,3,4)" + reset, lines)
self.assertIn("return (lambda *args: " + red + "foo" + reset + boldr + "(*args)" + reset + ")(1,2,3,4)", lines)
@@ -4496,11 +4489,11 @@ class TestColorizedTraceback(unittest.TestCase):
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',
@@ -4519,15 +4512,15 @@ class TestColorizedTraceback(unittest.TestCase):
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}, '
@@ -4541,38 +4534,6 @@ class TestColorizedTraceback(unittest.TestCase):
f'{boldm}ZeroDivisionError{reset}: {magenta}division by zero{reset}']
self.assertEqual(actual, expected)
- @force_not_colorized
- def test_colorized_detection_checks_for_environment_variables(self):
- if sys.platform == "win32":
- virtual_patching = unittest.mock.patch("nt._supports_virtual_terminal", return_value=True)
- else:
- virtual_patching = contextlib.nullcontext()
- with virtual_patching:
-
- flags = unittest.mock.MagicMock(ignore_environment=False)
- with (unittest.mock.patch("os.isatty") as isatty_mock,
- unittest.mock.patch("sys.flags", flags),
- unittest.mock.patch("traceback._can_colorize", ORIGINAL_CAN_COLORIZE)):
- isatty_mock.return_value = True
- with unittest.mock.patch("os.environ", {'TERM': 'dumb'}):
- self.assertEqual(traceback._can_colorize(), False)
- with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}):
- self.assertEqual(traceback._can_colorize(), True)
- with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}):
- self.assertEqual(traceback._can_colorize(), False)
- with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}):
- self.assertEqual(traceback._can_colorize(), False)
- with unittest.mock.patch("os.environ", {'NO_COLOR': '1', "PYTHON_COLORS": '1'}):
- self.assertEqual(traceback._can_colorize(), True)
- with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}):
- self.assertEqual(traceback._can_colorize(), True)
- with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', 'NO_COLOR': '1'}):
- self.assertEqual(traceback._can_colorize(), False)
- with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}):
- self.assertEqual(traceback._can_colorize(), False)
- isatty_mock.return_value = False
- with unittest.mock.patch("os.environ", {}):
- self.assertEqual(traceback._can_colorize(), False)
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/traceback.py b/Lib/traceback.py
index 6ce745f..8403173 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -1,7 +1,5 @@
"""Extract, format and print information about Python stack traces."""
-import os
-import io
import collections.abc
import itertools
import linecache
@@ -9,6 +7,8 @@ import sys
import textwrap
import warnings
from contextlib import suppress
+import _colorize
+from _colorize import ANSIColors
__all__ = ['extract_stack', 'extract_tb', 'format_exception',
'format_exception_only', 'format_list', 'format_stack',
@@ -21,7 +21,6 @@ __all__ = ['extract_stack', 'extract_tb', 'format_exception',
# 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
@@ -133,41 +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 not sys.flags.ignore_environment:
- 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 not sys.flags.ignore_environment:
- if "FORCE_COLOR" in os.environ:
- return True
- if os.environ.get("TERM") == "dumb":
- return False
-
- if not hasattr(sys.stderr, "fileno"):
- return False
-
- try:
- return os.isatty(sys.stderr.fileno())
- except io.UnsupportedOperation:
- return sys.stderr.isatty()
-
def _print_exception_bltin(exc, /):
file = sys.stderr if sys.stderr is not None else sys.__stderr__
- colorize = _can_colorize()
+ colorize = _colorize.can_colorize()
return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize)
@@ -214,9 +182,9 @@ def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=
end_char = "\n" if insert_final_newline else ""
if colorize:
if value is None or not valuestr:
- line = f"{_ANSIColors.BOLD_MAGENTA}{etype}{_ANSIColors.RESET}{end_char}"
+ line = f"{ANSIColors.BOLD_MAGENTA}{etype}{ANSIColors.RESET}{end_char}"
else:
- line = f"{_ANSIColors.BOLD_MAGENTA}{etype}{_ANSIColors.RESET}: {_ANSIColors.MAGENTA}{valuestr}{_ANSIColors.RESET}{end_char}"
+ line = f"{ANSIColors.BOLD_MAGENTA}{etype}{ANSIColors.RESET}: {ANSIColors.MAGENTA}{valuestr}{ANSIColors.RESET}{end_char}"
else:
if value is None or not valuestr:
line = f"{etype}{end_char}"
@@ -224,6 +192,7 @@ def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=
line = f"{etype}: {valuestr}{end_char}"
return line
+
def _safe_string(value, what, func=str):
try:
return func(value)
@@ -449,17 +418,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'
- GREEN = "\x1b[32m"
- BOLD_GREEN = "\x1b[1;32m"
- GREY = '\x1b[90m'
- RESET = '\x1b[0m'
- YELLOW = "\x1b[33m"
-
class StackSummary(list):
"""A list of FrameSummary objects, representing a stack of frames."""
@@ -564,15 +522,15 @@ class StackSummary(list):
filename = "<stdin>"
if colorize:
row.append(' File {}"{}"{}, line {}{}{}, in {}{}{}\n'.format(
- _ANSIColors.MAGENTA,
+ ANSIColors.MAGENTA,
filename,
- _ANSIColors.RESET,
- _ANSIColors.MAGENTA,
+ ANSIColors.RESET,
+ ANSIColors.MAGENTA,
frame_summary.lineno,
- _ANSIColors.RESET,
- _ANSIColors.MAGENTA,
+ ANSIColors.RESET,
+ ANSIColors.MAGENTA,
frame_summary.name,
- _ANSIColors.RESET,
+ ANSIColors.RESET,
)
)
else:
@@ -696,11 +654,11 @@ class StackSummary(list):
for color, group in itertools.groupby(itertools.zip_longest(line, carets, fillvalue=""), key=lambda x: x[1]):
caret_group = list(group)
if color == "^":
- colorized_line_parts.append(_ANSIColors.BOLD_RED + "".join(char for char, _ in caret_group) + _ANSIColors.RESET)
- colorized_carets_parts.append(_ANSIColors.BOLD_RED + "".join(caret for _, caret in caret_group) + _ANSIColors.RESET)
+ colorized_line_parts.append(ANSIColors.BOLD_RED + "".join(char for char, _ in caret_group) + ANSIColors.RESET)
+ colorized_carets_parts.append(ANSIColors.BOLD_RED + "".join(caret for _, caret in caret_group) + ANSIColors.RESET)
elif color == "~":
- colorized_line_parts.append(_ANSIColors.RED + "".join(char for char, _ in caret_group) + _ANSIColors.RESET)
- colorized_carets_parts.append(_ANSIColors.RED + "".join(caret for _, caret in caret_group) + _ANSIColors.RESET)
+ colorized_line_parts.append(ANSIColors.RED + "".join(char for char, _ in caret_group) + ANSIColors.RESET)
+ colorized_carets_parts.append(ANSIColors.RED + "".join(caret for _, caret in caret_group) + ANSIColors.RESET)
else:
colorized_line_parts.append("".join(char for char, _ in caret_group))
colorized_carets_parts.append("".join(caret for _, caret in caret_group))
@@ -1307,12 +1265,12 @@ class TracebackException:
if self.lineno is not None:
if colorize:
yield ' File {}"{}"{}, line {}{}{}\n'.format(
- _ANSIColors.MAGENTA,
+ ANSIColors.MAGENTA,
self.filename or "<string>",
- _ANSIColors.RESET,
- _ANSIColors.MAGENTA,
+ ANSIColors.RESET,
+ ANSIColors.MAGENTA,
self.lineno,
- _ANSIColors.RESET,
+ ANSIColors.RESET,
)
else:
yield ' File "{}", line {}\n'.format(
@@ -1352,11 +1310,11 @@ class TracebackException:
# colorize from colno to end_colno
ltext = (
ltext[:colno] +
- _ANSIColors.BOLD_RED + ltext[colno:end_colno] + _ANSIColors.RESET +
+ ANSIColors.BOLD_RED + ltext[colno:end_colno] + ANSIColors.RESET +
ltext[end_colno:]
)
- start_color = _ANSIColors.BOLD_RED
- end_color = _ANSIColors.RESET
+ start_color = ANSIColors.BOLD_RED
+ end_color = ANSIColors.RESET
yield ' {}\n'.format(ltext)
yield ' {}{}{}{}\n'.format(
"".join(caretspace),
@@ -1369,12 +1327,12 @@ class TracebackException:
msg = self.msg or "<no detail available>"
if colorize:
yield "{}{}{}: {}{}{}{}\n".format(
- _ANSIColors.BOLD_MAGENTA,
+ ANSIColors.BOLD_MAGENTA,
stype,
- _ANSIColors.RESET,
- _ANSIColors.MAGENTA,
+ ANSIColors.RESET,
+ ANSIColors.MAGENTA,
msg,
- _ANSIColors.RESET,
+ ANSIColors.RESET,
filename_suffix)
else:
yield "{}: {}{}\n".format(stype, msg, filename_suffix)
diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h
index f44abf1..ba32084 100644
--- a/Python/stdlib_module_names.h
+++ b/Python/stdlib_module_names.h
@@ -20,6 +20,7 @@ static const char* _Py_stdlib_module_names[] = {
"_codecs_tw",
"_collections",
"_collections_abc",
+"_colorize",
"_compat_pickle",
"_compression",
"_contextvars",