Skip to content

Commit e08e26c

Browse files
committed
pythongh-112730: Make the test suite relient to color-activation environment variables
1 parent e338e1a commit e08e26c

21 files changed

+118
-16
lines changed

Lib/doctest.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1540,7 +1540,11 @@ def out(s):
15401540
# Make sure sys.displayhook just prints the value to stdout
15411541
save_displayhook = sys.displayhook
15421542
sys.displayhook = sys.__displayhook__
1543-
1543+
saved_can_colorize = traceback._can_colorize
1544+
traceback._can_colorize = lambda: False
1545+
color_variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None}
1546+
for key in color_variables:
1547+
color_variables[key] = os.environ.pop(key, None)
15441548
try:
15451549
return self.__run(test, compileflags, out)
15461550
finally:
@@ -1549,6 +1553,10 @@ def out(s):
15491553
sys.settrace(save_trace)
15501554
linecache.getlines = self.save_linecache_getlines
15511555
sys.displayhook = save_displayhook
1556+
traceback._can_colorize = saved_can_colorize
1557+
for key, value in color_variables.items():
1558+
if value is not None:
1559+
os.environ[key] = value
15521560
if clear_globs:
15531561
test.globs.clear()
15541562
import builtins

Lib/idlelib/idle_test/test_run.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from unittest import mock
99
import idlelib
1010
from idlelib.idle_test.mock_idle import Func
11+
from test.support import force_not_colorized
1112

1213
idlelib.testing = True # Use {} for executing test user code.
1314

@@ -46,6 +47,7 @@ def __eq__(self, other):
4647
"Did you mean: 'real'?\n"),
4748
)
4849

50+
@force_not_colorized
4951
def test_get_message(self):
5052
for code, exc, msg in self.data:
5153
with self.subTest(code=code):
@@ -57,6 +59,7 @@ def test_get_message(self):
5759
expect = f'{exc.__name__}: {msg}'
5860
self.assertEqual(actual, expect)
5961

62+
@force_not_colorized
6063
@mock.patch.object(run, 'cleanup_traceback',
6164
new_callable=lambda: (lambda t, e: None))
6265
def test_get_multiple_message(self, mock):

Lib/test/support/__init__.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
"LOOPBACK_TIMEOUT", "INTERNET_TIMEOUT", "SHORT_TIMEOUT", "LONG_TIMEOUT",
5959
"Py_DEBUG", "EXCEEDS_RECURSION_LIMIT", "Py_C_RECURSION_LIMIT",
6060
"skip_on_s390x",
61-
"without_optimizer",
61+
"without_optimizer", "set_not_colorized_globally", "reset_colorized_globally",
62+
"force_not_colorized"
6263
]
6364

6465

@@ -2555,3 +2556,22 @@ def copy_python_src_ignore(path, names):
25552556
'build',
25562557
}
25572558
return ignored
2559+
2560+
def force_not_colorized(func):
2561+
"""Force the terminal not to be colorized."""
2562+
@functools.wraps(func)
2563+
def wrapper(*args, **kwargs):
2564+
import traceback
2565+
original_fn = traceback._can_colorize
2566+
variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None}
2567+
try:
2568+
for key in variables:
2569+
variables[key] = os.environ.pop(key, None)
2570+
traceback._can_colorize = lambda: False
2571+
return func(*args, **kwargs)
2572+
finally:
2573+
traceback._can_colorize = original_fn
2574+
for key, value in variables.items():
2575+
if value is not None:
2576+
os.environ[key] = value
2577+
return wrapper

Lib/test/test_capi/test_misc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from test.support import import_helper
2626
from test.support import threading_helper
2727
from test.support import warnings_helper
28+
from test.support import force_not_colorized
2829
from test.support import requires_limited_api
2930
from test.support.script_helper import assert_python_failure, assert_python_ok, run_python_until_end
3031
try:
@@ -205,6 +206,7 @@ def test_c_type_with_ipow(self):
205206
self.assertEqual(o.__ipow__(1), (1, None))
206207
self.assertEqual(o.__ipow__(2, 2), (2, 2))
207208

209+
@force_not_colorized
208210
def test_return_null_without_error(self):
209211
# Issue #23571: A function must not return NULL without setting an
210212
# error
@@ -234,6 +236,7 @@ def test_return_null_without_error(self):
234236
'return_null_without_error.* '
235237
'returned NULL without setting an exception')
236238

239+
@force_not_colorized
237240
def test_return_result_with_error(self):
238241
# Issue #23571: A function must not return a result with an error set
239242
if support.Py_DEBUG:
@@ -268,6 +271,7 @@ def test_return_result_with_error(self):
268271
'return_result_with_error.* '
269272
'returned a result with an exception set')
270273

274+
@force_not_colorized
271275
def test_getitem_with_error(self):
272276
# Test _Py_CheckSlotResult(). Raise an exception and then calls
273277
# PyObject_GetItem(): check that the assertion catches the bug.

Lib/test/test_cmd_line.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import unittest
1111
from test import support
1212
from test.support import os_helper
13+
from test.support import force_not_colorized
1314
from test.support.script_helper import (
1415
spawn_python, kill_python, assert_python_ok, assert_python_failure,
1516
interpreter_requires_environment
@@ -1027,6 +1028,7 @@ def test_sys_flags_not_set(self):
10271028

10281029

10291030
class SyntaxErrorTests(unittest.TestCase):
1031+
@force_not_colorized
10301032
def check_string(self, code):
10311033
proc = subprocess.run([sys.executable, "-"], input=code,
10321034
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

Lib/test/test_cmd_line_script.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import textwrap
1616
from test import support
17-
from test.support import import_helper, is_apple, os_helper
17+
from test.support import import_helper, is_apple, os_helper, force_not_colorized
1818
from test.support.script_helper import (
1919
make_pkg, make_script, make_zip_pkg, make_zip_script,
2020
assert_python_ok, assert_python_failure, spawn_python, kill_python)
@@ -195,6 +195,7 @@ def check_repl_stdout_flush(self, separate_stderr=False):
195195
p.stdin.flush()
196196
self.assertEqual(b'foo', p.stdout.readline().strip())
197197

198+
@force_not_colorized
198199
def check_repl_stderr_flush(self, separate_stderr=False):
199200
with self.interactive_python(separate_stderr) as p:
200201
p.stdin.write(b"1/0\n")
@@ -537,6 +538,7 @@ def test_dash_m_main_traceback(self):
537538
self.assertIn(b'Exception in __main__ module', err)
538539
self.assertIn(b'Traceback', err)
539540

541+
@force_not_colorized
540542
def test_pep_409_verbiage(self):
541543
# Make sure PEP 409 syntax properly suppresses
542544
# the context of an exception
@@ -602,6 +604,7 @@ def test_issue20500_exit_with_exception_value(self):
602604
text = stderr.decode('ascii')
603605
self.assertEqual(text.rstrip(), "some text")
604606

607+
@force_not_colorized
605608
def test_syntaxerror_unindented_caret_position(self):
606609
script = "1 + 1 = 2\n"
607610
with os_helper.temp_dir() as script_dir:
@@ -611,6 +614,7 @@ def test_syntaxerror_unindented_caret_position(self):
611614
# Confirm that the caret is located under the '=' sign
612615
self.assertIn("\n ^^^^^\n", text)
613616

617+
@force_not_colorized
614618
def test_syntaxerror_indented_caret_position(self):
615619
script = textwrap.dedent("""\
616620
if True:
@@ -634,6 +638,7 @@ def test_syntaxerror_indented_caret_position(self):
634638
self.assertNotIn("\f", text)
635639
self.assertIn("\n 1 + 1 = 2\n ^^^^^\n", text)
636640

641+
@force_not_colorized
637642
def test_syntaxerror_multi_line_fstring(self):
638643
script = 'foo = f"""{}\nfoo"""\n'
639644
with os_helper.temp_dir() as script_dir:
@@ -648,6 +653,7 @@ def test_syntaxerror_multi_line_fstring(self):
648653
],
649654
)
650655

656+
@force_not_colorized
651657
def test_syntaxerror_invalid_escape_sequence_multi_line(self):
652658
script = 'foo = """\\q"""\n'
653659
with os_helper.temp_dir() as script_dir:
@@ -663,6 +669,7 @@ def test_syntaxerror_invalid_escape_sequence_multi_line(self):
663669
],
664670
)
665671

672+
@force_not_colorized
666673
def test_syntaxerror_null_bytes(self):
667674
script = "x = '\0' nothing to see here\n';import os;os.system('echo pwnd')\n"
668675
with os_helper.temp_dir() as script_dir:
@@ -675,6 +682,7 @@ def test_syntaxerror_null_bytes(self):
675682
],
676683
)
677684

685+
@force_not_colorized
678686
def test_syntaxerror_null_bytes_in_multiline_string(self):
679687
scripts = ["\n'''\nmultilinestring\0\n'''", "\nf'''\nmultilinestring\0\n'''"] # Both normal and f-strings
680688
with os_helper.temp_dir() as script_dir:
@@ -688,6 +696,7 @@ def test_syntaxerror_null_bytes_in_multiline_string(self):
688696
]
689697
)
690698

699+
@force_not_colorized
691700
def test_source_lines_are_shown_when_running_source(self):
692701
_, _, stderr = assert_python_failure("-c", "1/0")
693702
expected_lines = [
@@ -698,6 +707,7 @@ def test_source_lines_are_shown_when_running_source(self):
698707
b'ZeroDivisionError: division by zero']
699708
self.assertEqual(stderr.splitlines(), expected_lines)
700709

710+
@force_not_colorized
701711
def test_syntaxerror_does_not_crash(self):
702712
script = "nonlocal x\n"
703713
with os_helper.temp_dir() as script_dir:

Lib/test/test_compileall.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from test import support
3030
from test.support import os_helper
3131
from test.support import script_helper
32+
from test.support import force_not_colorized
3233
from test.test_py_compile import without_source_date_epoch
3334
from test.test_py_compile import SourceDateEpochTestMeta
3435

@@ -760,6 +761,7 @@ def test_d_compile_error(self):
760761
rc, out, err = self.assertRunNotOK('-q', '-d', 'dinsdale', self.pkgdir)
761762
self.assertRegex(out, b'File "dinsdale')
762763

764+
@force_not_colorized
763765
def test_d_runtime_error(self):
764766
bazfn = script_helper.make_script(self.pkgdir, 'baz', 'raise Exception')
765767
self.assertRunOK('-q', '-d', 'dinsdale', self.pkgdir)

Lib/test/test_eof.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from test.support import os_helper
66
from test.support import script_helper
77
from test.support import warnings_helper
8+
from test.support import force_not_colorized
89
import unittest
910

1011
class EOFTestCase(unittest.TestCase):
@@ -58,6 +59,7 @@ def test_line_continuation_EOF(self):
5859
self.assertEqual(str(excinfo.exception), expect)
5960

6061
@unittest.skipIf(not sys.executable, "sys.executable required")
62+
@force_not_colorized
6163
def test_line_continuation_EOF_from_file_bpo2180(self):
6264
"""Ensure tok_nextc() does not add too many ending newlines."""
6365
with os_helper.temp_dir() as temp_dir:

Lib/test/test_exceptions.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
from test.support import (captured_stderr, check_impl_detail,
1313
cpython_only, gc_collect,
1414
no_tracing, script_helper,
15-
SuppressCrashReport)
15+
SuppressCrashReport,
16+
force_not_colorized)
1617
from test.support.import_helper import import_module
1718
from test.support.os_helper import TESTFN, unlink
1819
from test.support.warnings_helper import check_warnings
@@ -41,6 +42,7 @@ def __str__(self):
4142

4243
# XXX This is not really enough, each *operation* should be tested!
4344

45+
4446
class ExceptionTests(unittest.TestCase):
4547

4648
def raise_catch(self, exc, excname):
@@ -1438,6 +1440,7 @@ def gen():
14381440

14391441
@cpython_only
14401442
@unittest.skipIf(_testcapi is None, "requires _testcapi")
1443+
@force_not_colorized
14411444
def test_recursion_normalizing_infinite_exception(self):
14421445
# Issue #30697. Test that a RecursionError is raised when
14431446
# maximum recursion depth has been exceeded when creating
@@ -1993,6 +1996,7 @@ def write_source(self, source):
19931996
_rc, _out, err = script_helper.assert_python_failure('-Wd', '-X', 'utf8', TESTFN)
19941997
return err.decode('utf-8').splitlines()
19951998

1999+
@force_not_colorized
19962000
def test_assertion_error_location(self):
19972001
cases = [
19982002
('assert None',
@@ -2069,6 +2073,7 @@ def test_assertion_error_location(self):
20692073
result = self.write_source(source)
20702074
self.assertEqual(result[-3:], expected)
20712075

2076+
@force_not_colorized
20722077
def test_multiline_not_highlighted(self):
20732078
cases = [
20742079
("""
@@ -2101,6 +2106,7 @@ def test_multiline_not_highlighted(self):
21012106

21022107

21032108
class SyntaxErrorTests(unittest.TestCase):
2109+
@force_not_colorized
21042110
def test_range_of_offsets(self):
21052111
cases = [
21062112
# Basic range from 2->7
@@ -2191,6 +2197,7 @@ def test_range_of_offsets(self):
21912197
self.assertIn(expected, err.getvalue())
21922198
the_exception = exc
21932199

2200+
@force_not_colorized
21942201
def test_encodings(self):
21952202
source = (
21962203
'# -*- coding: cp437 -*-\n'
@@ -2220,6 +2227,7 @@ def test_encodings(self):
22202227
finally:
22212228
unlink(TESTFN)
22222229

2230+
@force_not_colorized
22232231
def test_non_utf8(self):
22242232
# Check non utf-8 characters
22252233
try:

Lib/test/test_inspect/test_inspect.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from test.support.os_helper import TESTFN, temp_cwd
3939
from test.support.script_helper import assert_python_ok, assert_python_failure, kill_python
4040
from test.support import has_subprocess_support, SuppressCrashReport
41+
from test.support import force_not_colorized
4142
from test import support
4243

4344
from test.test_inspect import inspect_fodder as mod
@@ -816,6 +817,7 @@ def test_getsource_on_code_object(self):
816817
self.assertSourceEqual(mod.eggs.__code__, 12, 18)
817818

818819
class TestGetsourceInteractive(unittest.TestCase):
820+
@force_not_colorized
819821
def test_getclasses_interactive(self):
820822
# bpo-44648: simulate a REPL session;
821823
# there is no `__file__` in the __main__ module

Lib/test/test_interpreters/test_api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# Raise SkipTest if subinterpreters not supported.
1111
_interpreters = import_helper.import_module('_xxsubinterpreters')
1212
from test.support import interpreters
13+
from test.support import force_not_colorized
1314
from test.support.interpreters import InterpreterNotFoundError
1415
from .utils import _captured_script, _run_output, _running, TestBase
1516

@@ -533,6 +534,7 @@ def test_failure(self):
533534
with self.assertRaises(interpreters.ExecutionFailed):
534535
interp.exec('raise Exception')
535536

537+
@force_not_colorized
536538
def test_display_preserved_exception(self):
537539
tempdir = self.temp_dir()
538540
modfile = self.make_module('spam', tempdir, text="""

Lib/test/test_regrtest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import unittest
2424
from test import support
2525
from test.support import os_helper, without_optimizer
26+
from test.support import force_not_colorized
2627
from test.libregrtest import cmdline
2728
from test.libregrtest import main
2829
from test.libregrtest import setup
@@ -1835,6 +1836,7 @@ def test_unraisable_exc(self):
18351836
self.assertIn("Warning -- Unraisable exception", output)
18361837
self.assertIn("Exception: weakref callback bug", output)
18371838

1839+
@force_not_colorized
18381840
def test_threading_excepthook(self):
18391841
# --fail-env-changed must catch uncaught thread exception.
18401842
# The exception must be displayed even if sys.stderr is redirected.

Lib/test/test_repl.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from textwrap import dedent
88
from test import support
99
from test.support import cpython_only, has_subprocess_support, SuppressCrashReport
10+
from test.support import force_not_colorized
1011
from test.support.script_helper import kill_python
1112
from test.support.import_helper import import_module
1213

@@ -15,6 +16,7 @@
1516
raise unittest.SkipTest("test module requires subprocess")
1617

1718

19+
@force_not_colorized
1820
def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw):
1921
"""Run the Python REPL with the given arguments.
2022
@@ -43,6 +45,7 @@ def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw):
4345
stdout=stdout, stderr=stderr,
4446
**kw)
4547

48+
@force_not_colorized
4649
def run_on_interactive_mode(source):
4750
"""Spawn a new Python interpreter, pass the given
4851
input source code from the stdin and return the

0 commit comments

Comments
 (0)