Skip to content

Commit afb9dc8

Browse files
gh-128595: Add test class helper to force no terminal colour (#128687)
Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
1 parent ddd9599 commit afb9dc8

File tree

5 files changed

+49
-22
lines changed

5 files changed

+49
-22
lines changed

Lib/test/support/__init__.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"skip_on_s390x",
6161
"without_optimizer",
6262
"force_not_colorized",
63+
"force_not_colorized_test_class",
6364
"BrokenIter",
6465
"in_systemd_nspawn_sync_suppressed",
6566
"run_no_yield_async_fn", "run_yielding_async_fn", "async_yield",
@@ -2832,30 +2833,44 @@ def is_slot_wrapper(name, value):
28322833
yield name, True
28332834

28342835

2836+
@contextlib.contextmanager
2837+
def no_color():
2838+
import _colorize
2839+
from .os_helper import EnvironmentVarGuard
2840+
2841+
with (
2842+
swap_attr(_colorize, "can_colorize", lambda: False),
2843+
EnvironmentVarGuard() as env,
2844+
):
2845+
for var in {"FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS"}:
2846+
env.unset(var)
2847+
env.set("NO_COLOR", "1")
2848+
yield
2849+
2850+
28352851
def force_not_colorized(func):
28362852
"""Force the terminal not to be colorized."""
28372853
@functools.wraps(func)
28382854
def wrapper(*args, **kwargs):
2839-
import _colorize
2840-
original_fn = _colorize.can_colorize
2841-
variables: dict[str, str | None] = {
2842-
"PYTHON_COLORS": None, "FORCE_COLOR": None, "NO_COLOR": None
2843-
}
2844-
try:
2845-
for key in variables:
2846-
variables[key] = os.environ.pop(key, None)
2847-
os.environ["NO_COLOR"] = "1"
2848-
_colorize.can_colorize = lambda: False
2855+
with no_color():
28492856
return func(*args, **kwargs)
2850-
finally:
2851-
_colorize.can_colorize = original_fn
2852-
del os.environ["NO_COLOR"]
2853-
for key, value in variables.items():
2854-
if value is not None:
2855-
os.environ[key] = value
28562857
return wrapper
28572858

28582859

2860+
def force_not_colorized_test_class(cls):
2861+
"""Force the terminal not to be colorized for the entire test class."""
2862+
original_setUpClass = cls.setUpClass
2863+
2864+
@classmethod
2865+
@functools.wraps(cls.setUpClass)
2866+
def new_setUpClass(cls):
2867+
cls.enterClassContext(no_color())
2868+
original_setUpClass()
2869+
2870+
cls.setUpClass = new_setUpClass
2871+
return cls
2872+
2873+
28592874
def initialized_with_pyrepl():
28602875
"""Detect whether PyREPL was used during Python initialization."""
28612876
# If the main module has a __file__ attribute it's a Python module, which means PyREPL.

Lib/test/test_code_module.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
from textwrap import dedent
66
from contextlib import ExitStack
77
from unittest import mock
8+
from test.support import force_not_colorized_test_class
89
from test.support import import_helper
910

10-
1111
code = import_helper.import_module('code')
1212

1313

@@ -30,6 +30,7 @@ def mock_sys(self):
3030
del self.sysmod.ps2
3131

3232

33+
@force_not_colorized_test_class
3334
class TestInteractiveConsole(unittest.TestCase, MockSys):
3435
maxDiff = None
3536

Lib/test/test_exceptions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2274,6 +2274,7 @@ def test_range_of_offsets(self):
22742274
self.assertIn(expected, err.getvalue())
22752275
the_exception = exc
22762276

2277+
@force_not_colorized
22772278
def test_subclass(self):
22782279
class MySyntaxError(SyntaxError):
22792280
pass

Lib/test/test_traceback.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from test.support.os_helper import TESTFN, unlink
2222
from test.support.script_helper import assert_python_ok, assert_python_failure
2323
from test.support.import_helper import forget
24-
from test.support import force_not_colorized
24+
from test.support import force_not_colorized, force_not_colorized_test_class
2525

2626
import json
2727
import textwrap
@@ -1712,6 +1712,7 @@ def f():
17121712

17131713

17141714
@requires_debug_ranges()
1715+
@force_not_colorized_test_class
17151716
class PurePythonTracebackErrorCaretTests(
17161717
PurePythonExceptionFormattingMixin,
17171718
TracebackErrorLocationCaretTestBase,
@@ -1725,6 +1726,7 @@ class PurePythonTracebackErrorCaretTests(
17251726

17261727
@cpython_only
17271728
@requires_debug_ranges()
1729+
@force_not_colorized_test_class
17281730
class CPythonTracebackErrorCaretTests(
17291731
CAPIExceptionFormattingMixin,
17301732
TracebackErrorLocationCaretTestBase,
@@ -1736,6 +1738,7 @@ class CPythonTracebackErrorCaretTests(
17361738

17371739
@cpython_only
17381740
@requires_debug_ranges()
1741+
@force_not_colorized_test_class
17391742
class CPythonTracebackLegacyErrorCaretTests(
17401743
CAPIExceptionFormattingLegacyMixin,
17411744
TracebackErrorLocationCaretTestBase,
@@ -2149,10 +2152,12 @@ def test_print_exception_bad_type_python(self):
21492152
boundaries = re.compile(
21502153
'(%s|%s)' % (re.escape(cause_message), re.escape(context_message)))
21512154

2155+
@force_not_colorized_test_class
21522156
class TestTracebackFormat(unittest.TestCase, TracebackFormatMixin):
21532157
pass
21542158

21552159
@cpython_only
2160+
@force_not_colorized_test_class
21562161
class TestFallbackTracebackFormat(unittest.TestCase, TracebackFormatMixin):
21572162
DEBUG_RANGES = False
21582163
def setUp(self) -> None:
@@ -2940,6 +2945,7 @@ def f():
29402945
self.assertEqual(report, expected)
29412946

29422947

2948+
@force_not_colorized_test_class
29432949
class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
29442950
#
29452951
# This checks reporting through the 'traceback' module, with both
@@ -2956,6 +2962,7 @@ def get_report(self, e):
29562962
return s
29572963

29582964

2965+
@force_not_colorized_test_class
29592966
class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
29602967
#
29612968
# This checks built-in reporting by the interpreter.

Lib/test/test_unittest/test_result.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import io
22
import sys
33
import textwrap
4-
5-
from test.support import warnings_helper, captured_stdout
6-
74
import traceback
85
import unittest
96
from unittest.util import strclass
10-
from test.support import force_not_colorized
7+
from test.support import warnings_helper
8+
from test.support import (
9+
captured_stdout,
10+
force_not_colorized,
11+
force_not_colorized_test_class,
12+
)
1113
from test.test_unittest.support import BufferedWriter
1214

1315

@@ -772,6 +774,7 @@ def testFoo(self):
772774
runner.run(Test('testFoo'))
773775

774776

777+
@force_not_colorized_test_class
775778
class TestOutputBuffering(unittest.TestCase):
776779

777780
def setUp(self):

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy