]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-127873: Only check `sys.flags.ignore_environment` for `PYTHON*` env vars (#127877)
authorHugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Tue, 21 Jan 2025 16:10:08 +0000 (18:10 +0200)
committerGitHub <noreply@github.com>
Tue, 21 Jan 2025 16:10:08 +0000 (16:10 +0000)
22 files changed:
.github/CODEOWNERS
Lib/_colorize.py
Lib/test/support/__init__.py
Lib/test/test__colorize.py
Lib/test/test_capi/test_misc.py
Lib/test/test_cmd_line_script.py
Lib/test/test_compileall.py
Lib/test/test_eof.py
Lib/test/test_exceptions.py
Lib/test/test_import/__init__.py
Lib/test/test_inspect/test_inspect.py
Lib/test/test_pyrepl/support.py
Lib/test/test_pyrepl/test_pyrepl.py
Lib/test/test_regrtest.py
Lib/test/test_repl.py
Lib/test/test_runpy.py
Lib/test/test_tracemalloc.py
Lib/test/test_unicodedata.py
Lib/test/test_unittest/test_program.py
Lib/test/test_unittest/test_result.py
Lib/test/test_unittest/test_runner.py
Misc/NEWS.d/next/Library/2024-12-12-18-25-50.gh-issue-127873.WJRwfz.rst [new file with mode: 0644]

index 76b61e700b5f7296d93d06014f107660d6388e33..0add48e777866c4bf10097a8cc29dedaadecd6d3 100644 (file)
@@ -304,3 +304,7 @@ Lib/test/test_configparser.py @jaraco
 Doc/reference/                @willingc @AA-Turner
 
 **/*weakref*                  @kumaraditya303
+
+# Colorize
+Lib/_colorize.py              @hugovk
+Lib/test/test__colorize.py    @hugovk
index bab2e599b2c810977e1fbffb4b29e69ebf2e9628..5e36e45734a5fb9c431775e6d80bafbc7d472579 100644 (file)
@@ -42,15 +42,14 @@ def can_colorize(*, file=None) -> bool:
             return False
         if os.environ.get("PYTHON_COLORS") == "1":
             return True
-        if "NO_COLOR" in os.environ:
-            return False
+    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 "FORCE_COLOR" in os.environ:
+        return True
+    if os.environ.get("TERM") == "dumb":
+        return False
 
     if not hasattr(file, "fileno"):
         return False
index 084b2411e799f47db3de0309aea719f50612fbf9..89f2a6b916bfc251f0db069e2bc35cbff8418d6e 100644 (file)
@@ -61,6 +61,7 @@ __all__ = [
     "without_optimizer",
     "force_not_colorized",
     "force_not_colorized_test_class",
+    "make_clean_env",
     "BrokenIter",
     "in_systemd_nspawn_sync_suppressed",
     "run_no_yield_async_fn", "run_yielding_async_fn", "async_yield",
@@ -2871,6 +2872,16 @@ def force_not_colorized_test_class(cls):
     return cls
 
 
+def make_clean_env() -> dict[str, str]:
+    clean_env = os.environ.copy()
+    for k in clean_env.copy():
+        if k.startswith("PYTHON"):
+            clean_env.pop(k)
+    clean_env.pop("FORCE_COLOR", None)
+    clean_env.pop("NO_COLOR", None)
+    return clean_env
+
+
 def initialized_with_pyrepl():
     """Detect whether PyREPL was used during Python initialization."""
     # If the main module has a __file__ attribute it's a Python module, which means PyREPL.
index 77e74fa3e23c2c821830528cfe20a7502c63415a..25519ba7e92e1fca5c059e1f5de927fc381c4ba1 100644 (file)
@@ -3,7 +3,7 @@ import sys
 import unittest
 import unittest.mock
 import _colorize
-from test.support import force_not_colorized
+from test.support import force_not_colorized, make_clean_env
 
 ORIGINAL_CAN_COLORIZE = _colorize.can_colorize
 
@@ -17,6 +17,14 @@ def tearDownModule():
 
 
 class TestColorizeFunction(unittest.TestCase):
+    def setUp(self):
+        # Remove PYTHON* environment variables to isolate from local user
+        # settings and simulate running with `-E`. Such variables should be
+        # added to test methods later to patched os.environ.
+        patcher = unittest.mock.patch("os.environ", new=make_clean_env())
+        self.addCleanup(patcher.stop)
+        patcher.start()
+
     @force_not_colorized
     def test_colorized_detection_checks_for_environment_variables(self):
         flags = unittest.mock.MagicMock(ignore_environment=False)
index 31a4a224ec8f8873e76b998bcfde97db4407de7a..114e7cdfd0cd9c82c64b5a8e84fd1e458b7e2a0a 100644 (file)
@@ -75,8 +75,11 @@ class InstanceMethod:
     id = _testcapi.instancemethod(id)
     testfunction = _testcapi.instancemethod(testfunction)
 
+
 CURRENT_THREAD_REGEX = r'Current thread.*:\n' if not support.Py_GIL_DISABLED else r'Stack .*:\n'
 
+
+@support.force_not_colorized_test_class
 class CAPITest(unittest.TestCase):
 
     def test_instancemethod(self):
index 527d51857fc904b517b8a2bbf50f46e342bc2604..e7f3e46c1868f759beb5ae85ed522d8da2b2badb 100644 (file)
@@ -88,6 +88,8 @@ def _make_test_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
     importlib.invalidate_caches()
     return to_return
 
+
+@support.force_not_colorized_test_class
 class CmdLineTest(unittest.TestCase):
     def _check_output(self, script_name, exit_code, data,
                              expected_file, expected_argv0,
index 3a34c6822bc07931ddca924550708a0342a617c2..a580a240d9f47469e7f8ef62e5bfbe2522140a0b 100644 (file)
@@ -766,6 +766,7 @@ class CommandLineTestsBase:
         rc, out, err = self.assertRunNotOK('-q', '-d', 'dinsdale', self.pkgdir)
         self.assertRegex(out, b'File "dinsdale')
 
+    @support.force_not_colorized
     def test_d_runtime_error(self):
         bazfn = script_helper.make_script(self.pkgdir, 'baz', 'raise Exception')
         self.assertRunOK('-q', '-d', 'dinsdale', self.pkgdir)
index e377383450e19d5eae3b0e7a9665d31333ebf9c1..582e5b6de6e68702e0c3ac819fa782838b004a2b 100644 (file)
@@ -2,7 +2,7 @@
 
 import sys
 from codecs import BOM_UTF8
-from test import support
+from test.support import force_not_colorized
 from test.support import os_helper
 from test.support import script_helper
 from test.support import warnings_helper
@@ -44,6 +44,7 @@ class EOFTestCase(unittest.TestCase):
         self.assertEqual(cm.exception.text, "ä = '''thîs is ")
         self.assertEqual(cm.exception.offset, 5)
 
+    @force_not_colorized
     def test_EOFS_with_file(self):
         expect = ("(<string>, line 1)")
         with os_helper.temp_dir() as temp_dir:
@@ -123,6 +124,7 @@ class EOFTestCase(unittest.TestCase):
         self.assertEqual(str(cm.exception), expect)
 
     @unittest.skipIf(not sys.executable, "sys.executable required")
+    @force_not_colorized
     def test_line_continuation_EOF_from_file_bpo2180(self):
         """Ensure tok_nextc() does not add too many ending newlines."""
         with os_helper.temp_dir() as temp_dir:
index 206e22e791e02a6b64c8e65070824dfc00a03435..2d324827451b5458a14d98096c2a6f799dfd7ed9 100644 (file)
@@ -1465,6 +1465,7 @@ class ExceptionTests(unittest.TestCase):
 
     @cpython_only
     @unittest.skipIf(_testcapi is None, "requires _testcapi")
+    @force_not_colorized
     def test_recursion_normalizing_infinite_exception(self):
         # Issue #30697. Test that a RecursionError is raised when
         # maximum recursion depth has been exceeded when creating
@@ -2180,6 +2181,7 @@ class AssertionErrorTests(unittest.TestCase):
                 self.assertEqual(result[-len(expected):], expected)
 
 
+@support.force_not_colorized_test_class
 class SyntaxErrorTests(unittest.TestCase):
     maxDiff = None
 
index 654b9f5bd7ab50d579b4359d4eb59604c22d380d..207b7ae7517450583ad594d1c0774081cb101709 100644 (file)
@@ -29,9 +29,21 @@ import _imp
 
 from test.support import os_helper
 from test.support import (
-    STDLIB_DIR, swap_attr, swap_item, cpython_only, is_apple_mobile, is_emscripten,
-    is_wasi, run_in_subinterp, run_in_subinterp_with_config, Py_TRACE_REFS,
-    requires_gil_enabled, Py_GIL_DISABLED, no_rerun)
+    STDLIB_DIR,
+    swap_attr,
+    swap_item,
+    cpython_only,
+    is_apple_mobile,
+    is_emscripten,
+    is_wasi,
+    run_in_subinterp,
+    run_in_subinterp_with_config,
+    Py_TRACE_REFS,
+    requires_gil_enabled,
+    Py_GIL_DISABLED,
+    no_rerun,
+    force_not_colorized_test_class,
+)
 from test.support.import_helper import (
     forget, make_legacy_pyc, unlink, unload, ready_to_import,
     DirsOnSysPath, CleanImport, import_module)
@@ -333,6 +345,7 @@ class ModuleSnapshot(types.SimpleNamespace):
         return cls.parse(text.decode())
 
 
+@force_not_colorized_test_class
 class ImportTests(unittest.TestCase):
 
     def setUp(self):
index 6457bc523de4600ca56dc3842c7899da682e4806..8e47df21cfef2e84128d639c8d7fa833fa1759d8 100644 (file)
@@ -886,6 +886,7 @@ class TestGetsourceStdlib(unittest.TestCase):
         self.assertEqual(src.splitlines(True), lines)
 
 class TestGetsourceInteractive(unittest.TestCase):
+    @support.force_not_colorized
     def test_getclasses_interactive(self):
         # bpo-44648: simulate a REPL session;
         # there is no `__file__` in the __main__ module
index 672d4896c92283b556297eeef4aced7e7cb2225c..45e3bf758f17de69cccefd449dc932eeda88b74c 100644 (file)
@@ -101,16 +101,6 @@ handle_events_narrow_console = partial(
 )
 
 
-def make_clean_env() -> dict[str, str]:
-    clean_env = os.environ.copy()
-    for k in clean_env.copy():
-        if k.startswith("PYTHON"):
-            clean_env.pop(k)
-    clean_env.pop("FORCE_COLOR", None)
-    clean_env.pop("NO_COLOR", None)
-    return clean_env
-
-
 class FakeConsole(Console):
     def __init__(self, events, encoding="utf-8") -> None:
         self.events = iter(events)
index f29a7ffbd7cafdff2d47370782e0205552d42301..bbe19612437a1d7844d2e49a63cd62f4d1f8bee9 100644 (file)
@@ -10,7 +10,7 @@ import sys
 import tempfile
 from unittest import TestCase, skipUnless, skipIf
 from unittest.mock import patch
-from test.support import force_not_colorized
+from test.support import force_not_colorized, make_clean_env
 from test.support import SHORT_TIMEOUT
 from test.support.import_helper import import_module
 from test.support.os_helper import unlink
@@ -23,7 +23,6 @@ from .support import (
     multiline_input,
     code_to_events,
     clean_screen,
-    make_clean_env,
 )
 from _pyrepl.console import Event
 from _pyrepl.readline import (ReadlineAlikeReader, ReadlineConfig,
index 5707b355e94337a8e5f196490a8171f884ab2754..e9ef830c848aad59115d382b411eb864a3100a91 100644 (file)
@@ -792,6 +792,7 @@ class CheckActualTests(BaseTestCase):
                            f'{", ".join(output.splitlines())}')
 
 
+@support.force_not_colorized_test_class
 class ProgramsTestCase(BaseTestCase):
     """
     Test various ways to run the Python test suite. Use options close
@@ -905,6 +906,7 @@ class ProgramsTestCase(BaseTestCase):
         self.run_batch(script, *rt_args, *self.regrtest_args, *self.tests)
 
 
+@support.force_not_colorized_test_class
 class ArgsTestCase(BaseTestCase):
     """
     Test arguments of the Python test suite.
index e764e60560db2340fa2eb9ec46958f2402e8c80e..356ff5b198d637943d5001c4c24f7c02c88316fb 100644 (file)
@@ -70,6 +70,7 @@ def run_on_interactive_mode(source):
     return output
 
 
+@support.force_not_colorized_test_class
 class TestInteractiveInterpreter(unittest.TestCase):
 
     @cpython_only
@@ -273,6 +274,8 @@ class TestInteractiveInterpreter(unittest.TestCase):
 
         self.assertEqual(exit_code, 0, "".join(output))
 
+
+@support.force_not_colorized_test_class
 class TestInteractiveModeSyntaxErrors(unittest.TestCase):
 
     def test_interactive_syntax_error_correct_line(self):
index b64383f6546f31e1bc1de35f6231d32e4d8b48f6..ada78ec8e6b0c757d83a9802c2c1cbc8b3c46a89 100644 (file)
@@ -12,8 +12,14 @@ import tempfile
 import textwrap
 import unittest
 import warnings
-from test.support import (infinite_recursion, no_tracing, verbose,
-                          requires_subprocess, requires_resource)
+from test.support import (
+    force_not_colorized_test_class,
+    infinite_recursion,
+    no_tracing,
+    requires_resource,
+    requires_subprocess,
+    verbose,
+)
 from test.support.import_helper import forget, make_legacy_pyc, unload
 from test.support.os_helper import create_empty_file, temp_dir, FakePath
 from test.support.script_helper import make_script, make_zip_script
@@ -758,6 +764,7 @@ s = "non-ASCII: h\xe9"
             self.assertEqual(result['s'], "non-ASCII: h\xe9")
 
 
+@force_not_colorized_test_class
 class TestExit(unittest.TestCase):
     STATUS_CONTROL_C_EXIT = 0xC000013A
     EXPECTED_CODE = (
index a848363fcd1de9e3f69445cbcdb513237f36a014..238ae14b388c7634df12c958cf8bdc323e9cc261 100644 (file)
@@ -981,6 +981,7 @@ class TestCommandLine(unittest.TestCase):
             return
         self.fail(f"unexpected output: {stderr!a}")
 
+    @force_not_colorized
     def test_sys_xoptions_invalid(self):
         for nframe in INVALID_NFRAME:
             with self.subTest(nframe=nframe):
index c7d09a6b460c194d8f1bd363e208c5c4fb86631d..0285f0d51f236593ca66bb8828235ef097153ee1 100644 (file)
@@ -11,8 +11,14 @@ from http.client import HTTPException
 import sys
 import unicodedata
 import unittest
-from test.support import (open_urlresource, requires_resource, script_helper,
-                          cpython_only, check_disallow_instantiation)
+from test.support import (
+    open_urlresource,
+    requires_resource,
+    script_helper,
+    cpython_only,
+    check_disallow_instantiation,
+    force_not_colorized,
+)
 
 
 class UnicodeMethodsTest(unittest.TestCase):
@@ -277,6 +283,7 @@ class UnicodeMiscTest(UnicodeDatabaseTest):
         # Ensure that the type disallows instantiation (bpo-43916)
         check_disallow_instantiation(self, unicodedata.UCD)
 
+    @force_not_colorized
     def test_failed_import_during_compiling(self):
         # Issue 4367
         # Decoding \N escapes requires the unicodedata module. If it can't be
index 58d0cef9708c95bbdb4e9611fb5c3478f1b30af6..6092ed292d8f60fa79b5e2c300b1f3725c7acda5 100644 (file)
@@ -4,10 +4,10 @@ import subprocess
 from test import support
 import unittest
 import test.test_unittest
-from test.support import force_not_colorized
 from test.test_unittest.test_result import BufferedWriter
 
 
+@support.force_not_colorized_test_class
 class Test_TestProgram(unittest.TestCase):
 
     def test_discovery_from_dotted_path(self):
@@ -121,7 +121,6 @@ class Test_TestProgram(unittest.TestCase):
         self.assertEqual(['test.test_unittest', 'test.test_unittest2'],
                           program.testNames)
 
-    @force_not_colorized
     def test_NonExit(self):
         stream = BufferedWriter()
         program = unittest.main(exit=False,
@@ -137,7 +136,6 @@ class Test_TestProgram(unittest.TestCase):
                     'expected failures=1, unexpected successes=1)\n')
         self.assertEndsWith(out, expected)
 
-    @force_not_colorized
     def test_Exit(self):
         stream = BufferedWriter()
         with self.assertRaises(SystemExit) as cm:
@@ -155,7 +153,6 @@ class Test_TestProgram(unittest.TestCase):
                     'expected failures=1, unexpected successes=1)\n')
         self.assertEndsWith(out, expected)
 
-    @force_not_colorized
     def test_ExitAsDefault(self):
         stream = BufferedWriter()
         with self.assertRaises(SystemExit):
@@ -171,7 +168,6 @@ class Test_TestProgram(unittest.TestCase):
                     'expected failures=1, unexpected successes=1)\n')
         self.assertEndsWith(out, expected)
 
-    @force_not_colorized
     def test_ExitSkippedSuite(self):
         stream = BufferedWriter()
         with self.assertRaises(SystemExit) as cm:
@@ -184,7 +180,6 @@ class Test_TestProgram(unittest.TestCase):
         expected = '\n\nOK (skipped=1)\n'
         self.assertEndsWith(out, expected)
 
-    @force_not_colorized
     def test_ExitEmptySuite(self):
         stream = BufferedWriter()
         with self.assertRaises(SystemExit) as cm:
index 327b246452bedff58cb1397e2979f50accecd0ab..9ac4c52449c2ff97af7bd607b42d1ac6aa728a96 100644 (file)
@@ -5,11 +5,7 @@ import traceback
 import unittest
 from unittest.util import strclass
 from test.support import warnings_helper
-from test.support import (
-    captured_stdout,
-    force_not_colorized,
-    force_not_colorized_test_class,
-)
+from test.support import captured_stdout, force_not_colorized_test_class
 from test.test_unittest.support import BufferedWriter
 
 
@@ -37,6 +33,7 @@ def bad_cleanup2():
     raise ValueError('bad cleanup2')
 
 
+@force_not_colorized_test_class
 class Test_TestResult(unittest.TestCase):
     # Note: there are not separate tests for TestResult.wasSuccessful(),
     # TestResult.errors, TestResult.failures, TestResult.testsRun or
@@ -208,7 +205,6 @@ class Test_TestResult(unittest.TestCase):
         self.assertIs(test_case, test)
         self.assertIsInstance(formatted_exc, str)
 
-    @force_not_colorized
     def test_addFailure_filter_traceback_frames(self):
         class Foo(unittest.TestCase):
             def test_1(self):
@@ -235,7 +231,6 @@ class Test_TestResult(unittest.TestCase):
         self.assertEqual(len(dropped), 1)
         self.assertIn("raise self.failureException(msg)", dropped[0])
 
-    @force_not_colorized
     def test_addFailure_filter_traceback_frames_context(self):
         class Foo(unittest.TestCase):
             def test_1(self):
@@ -265,7 +260,6 @@ class Test_TestResult(unittest.TestCase):
         self.assertEqual(len(dropped), 1)
         self.assertIn("raise self.failureException(msg)", dropped[0])
 
-    @force_not_colorized
     def test_addFailure_filter_traceback_frames_chained_exception_self_loop(self):
         class Foo(unittest.TestCase):
             def test_1(self):
@@ -291,7 +285,6 @@ class Test_TestResult(unittest.TestCase):
         formatted_exc = result.failures[0][1]
         self.assertEqual(formatted_exc.count("Exception: Loop\n"), 1)
 
-    @force_not_colorized
     def test_addFailure_filter_traceback_frames_chained_exception_cycle(self):
         class Foo(unittest.TestCase):
             def test_1(self):
@@ -453,7 +446,6 @@ class Test_TestResult(unittest.TestCase):
         result.addUnexpectedSuccess(None)
         self.assertTrue(result.shouldStop)
 
-    @force_not_colorized
     def testFailFastSetByRunner(self):
         stream = BufferedWriter()
         runner = unittest.TextTestRunner(stream=stream, failfast=True)
@@ -465,6 +457,7 @@ class Test_TestResult(unittest.TestCase):
         self.assertEndsWith(stream.getvalue(), '\n\nOK\n')
 
 
+@force_not_colorized_test_class
 class Test_TextTestResult(unittest.TestCase):
     maxDiff = None
 
@@ -627,7 +620,6 @@ class Test_TextTestResult(unittest.TestCase):
         test.run(result)
         return stream.getvalue()
 
-    @force_not_colorized
     def testDotsOutput(self):
         self.assertEqual(self._run_test('testSuccess', 1), '.')
         self.assertEqual(self._run_test('testSkip', 1), 's')
@@ -636,7 +628,6 @@ class Test_TextTestResult(unittest.TestCase):
         self.assertEqual(self._run_test('testExpectedFailure', 1), 'x')
         self.assertEqual(self._run_test('testUnexpectedSuccess', 1), 'u')
 
-    @force_not_colorized
     def testLongOutput(self):
         classname = f'{__name__}.{self.Test.__qualname__}'
         self.assertEqual(self._run_test('testSuccess', 2),
@@ -652,21 +643,17 @@ class Test_TextTestResult(unittest.TestCase):
         self.assertEqual(self._run_test('testUnexpectedSuccess', 2),
                          f'testUnexpectedSuccess ({classname}.testUnexpectedSuccess) ... unexpected success\n')
 
-    @force_not_colorized
     def testDotsOutputSubTestSuccess(self):
         self.assertEqual(self._run_test('testSubTestSuccess', 1), '.')
 
-    @force_not_colorized
     def testLongOutputSubTestSuccess(self):
         classname = f'{__name__}.{self.Test.__qualname__}'
         self.assertEqual(self._run_test('testSubTestSuccess', 2),
                          f'testSubTestSuccess ({classname}.testSubTestSuccess) ... ok\n')
 
-    @force_not_colorized
     def testDotsOutputSubTestMixed(self):
         self.assertEqual(self._run_test('testSubTestMixed', 1), 'sFE')
 
-    @force_not_colorized
     def testLongOutputSubTestMixed(self):
         classname = f'{__name__}.{self.Test.__qualname__}'
         self.assertEqual(self._run_test('testSubTestMixed', 2),
@@ -675,7 +662,6 @@ class Test_TextTestResult(unittest.TestCase):
                 f'  testSubTestMixed ({classname}.testSubTestMixed) [fail] (c=3) ... FAIL\n'
                 f'  testSubTestMixed ({classname}.testSubTestMixed) [error] (d=4) ... ERROR\n')
 
-    @force_not_colorized
     def testDotsOutputTearDownFail(self):
         out = self._run_test('testSuccess', 1, AssertionError('fail'))
         self.assertEqual(out, 'F')
@@ -686,7 +672,6 @@ class Test_TextTestResult(unittest.TestCase):
         out = self._run_test('testSkip', 1, AssertionError('fail'))
         self.assertEqual(out, 'sF')
 
-    @force_not_colorized
     def testLongOutputTearDownFail(self):
         classname = f'{__name__}.{self.Test.__qualname__}'
         out = self._run_test('testSuccess', 2, AssertionError('fail'))
index 1131cd73128866f8326dd4a556aa32ebed474352..4d3cfd60b8d9c39b405fad3d17f349ea4144c13c 100644 (file)
@@ -4,7 +4,6 @@ import sys
 import pickle
 import subprocess
 from test import support
-from test.support import force_not_colorized
 
 import unittest
 from unittest.case import _Outcome
@@ -107,7 +106,7 @@ class TestCleanUp(unittest.TestCase):
         self.assertTrue(test.doCleanups())
         self.assertEqual(cleanups, [(2, (), {}), (1, (1, 2, 3), dict(four='hello', five='goodbye'))])
 
-    @force_not_colorized
+    @support.force_not_colorized
     def testCleanUpWithErrors(self):
         class TestableTest(unittest.TestCase):
             def testNothing(self):
@@ -251,6 +250,7 @@ class TestCleanUp(unittest.TestCase):
         self.assertEqual(test._cleanups, [])
 
 
+@support.force_not_colorized_test_class
 class TestClassCleanup(unittest.TestCase):
     def test_addClassCleanUp(self):
         class TestableTest(unittest.TestCase):
@@ -418,7 +418,6 @@ class TestClassCleanup(unittest.TestCase):
         self.assertIsInstance(e2[1], CustomError)
         self.assertEqual(str(e2[1]), 'cleanup1')
 
-    @force_not_colorized
     def test_with_errors_addCleanUp(self):
         ordering = []
         class TestableTest(unittest.TestCase):
@@ -442,7 +441,6 @@ class TestClassCleanup(unittest.TestCase):
                          ['setUpClass', 'setUp', 'cleanup_exc',
                           'tearDownClass', 'cleanup_good'])
 
-    @force_not_colorized
     def test_run_with_errors_addClassCleanUp(self):
         ordering = []
         class TestableTest(unittest.TestCase):
@@ -466,7 +464,6 @@ class TestClassCleanup(unittest.TestCase):
                          ['setUpClass', 'setUp', 'test', 'cleanup_good',
                           'tearDownClass', 'cleanup_exc'])
 
-    @force_not_colorized
     def test_with_errors_in_addClassCleanup_and_setUps(self):
         ordering = []
         class_blow_up = False
@@ -519,7 +516,6 @@ class TestClassCleanup(unittest.TestCase):
                          ['setUpClass', 'setUp', 'tearDownClass',
                           'cleanup_exc'])
 
-    @force_not_colorized
     def test_with_errors_in_tearDownClass(self):
         ordering = []
         class TestableTest(unittest.TestCase):
@@ -596,7 +592,6 @@ class TestClassCleanup(unittest.TestCase):
                 'inner setup', 'inner test', 'inner cleanup',
                 'end outer test', 'outer cleanup'])
 
-    @force_not_colorized
     def test_run_empty_suite_error_message(self):
         class EmptyTest(unittest.TestCase):
             pass
@@ -608,6 +603,7 @@ class TestClassCleanup(unittest.TestCase):
         self.assertIn("\nNO TESTS RAN\n", runner.stream.getvalue())
 
 
+@support.force_not_colorized_test_class
 class TestModuleCleanUp(unittest.TestCase):
     def test_add_and_do_ModuleCleanup(self):
         module_cleanups = []
@@ -670,7 +666,6 @@ class TestModuleCleanUp(unittest.TestCase):
         self.assertEqual(cleanups,
                          [((1, 2), {'function': 'hello'})])
 
-    @force_not_colorized
     def test_run_module_cleanUp(self):
         blowUp = True
         ordering = []
@@ -810,7 +805,6 @@ class TestModuleCleanUp(unittest.TestCase):
                                     'tearDownClass', 'cleanup_good'])
         self.assertEqual(unittest.case._module_cleanups, [])
 
-    @force_not_colorized
     def test_run_module_cleanUp_when_teardown_exception(self):
         ordering = []
         class Module(object):
@@ -972,7 +966,6 @@ class TestModuleCleanUp(unittest.TestCase):
         self.assertEqual(cleanups,
                          [((1, 2), {'function': 3, 'self': 4})])
 
-    @force_not_colorized
     def test_with_errors_in_addClassCleanup(self):
         ordering = []
 
@@ -1006,7 +999,6 @@ class TestModuleCleanUp(unittest.TestCase):
                          ['setUpModule', 'setUpClass', 'test', 'tearDownClass',
                           'cleanup_exc', 'tearDownModule', 'cleanup_good'])
 
-    @force_not_colorized
     def test_with_errors_in_addCleanup(self):
         ordering = []
         class Module(object):
@@ -1037,7 +1029,6 @@ class TestModuleCleanUp(unittest.TestCase):
                          ['setUpModule', 'setUp', 'test', 'tearDown',
                           'cleanup_exc', 'tearDownModule', 'cleanup_good'])
 
-    @force_not_colorized
     def test_with_errors_in_addModuleCleanup_and_setUps(self):
         ordering = []
         module_blow_up = False
@@ -1330,7 +1321,7 @@ class Test_TextTestRunner(unittest.TestCase):
         expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY)
         self.assertEqual(runner._makeResult(), expectedresult)
 
-    @force_not_colorized
+    @support.force_not_colorized
     @support.requires_subprocess()
     def test_warnings(self):
         """
diff --git a/Misc/NEWS.d/next/Library/2024-12-12-18-25-50.gh-issue-127873.WJRwfz.rst b/Misc/NEWS.d/next/Library/2024-12-12-18-25-50.gh-issue-127873.WJRwfz.rst
new file mode 100644 (file)
index 0000000..d7575c7
--- /dev/null
@@ -0,0 +1,3 @@
+When ``-E`` is set, only ignore ``PYTHON_COLORS`` and not
+``FORCE_COLOR``/``NO_COLOR``/``TERM`` when colourising output.
+Patch by Hugo van Kemenade.