Skip to content

gh-127873: Only check sys.flags.ignore_environment for PYTHON* env vars #127877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -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
13 changes: 6 additions & 7 deletions Lib/_colorize.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"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",
Expand Down Expand Up @@ -2871,6 +2872,16 @@ def new_setUpClass(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.
Expand Down
10 changes: 9 additions & 1 deletion Lib/test/test__colorize.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
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

Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_cmd_line_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_compileall.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ def test_d_compile_error(self):
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)
Expand Down
4 changes: 3 additions & 1 deletion Lib/test/test_eof.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -44,6 +44,7 @@ def test_EOFS(self):
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:
Expand Down Expand Up @@ -123,6 +124,7 @@ def test_line_continuation_EOF(self):
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:
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,7 @@ def gen():

@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
Expand Down Expand Up @@ -2180,6 +2181,7 @@ def test_multiline_not_highlighted(self):
self.assertEqual(result[-len(expected):], expected)


@support.force_not_colorized_test_class
class SyntaxErrorTests(unittest.TestCase):
maxDiff = None

Expand Down
19 changes: 16 additions & 3 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,21 @@

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)
Expand Down Expand Up @@ -333,6 +345,7 @@ def _from_subinterp(cls, name, interpid, pipe, script_kwargs):
return cls.parse(text.decode())


@force_not_colorized_test_class
class ImportTests(unittest.TestCase):

def setUp(self):
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,7 @@ def test_getsource_stdlib_decimal(self):
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
Expand Down
10 changes: 0 additions & 10 deletions Lib/test/test_pyrepl/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,6 @@ def handle_all_events(
)


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)
Expand Down
3 changes: 1 addition & 2 deletions Lib/test/test_pyrepl/test_pyrepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
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
Expand All @@ -23,7 +23,6 @@
multiline_input,
code_to_events,
clean_screen,
make_clean_env,
)
from _pyrepl.console import Event
from _pyrepl.readline import (ReadlineAlikeReader, ReadlineConfig,
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_regrtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ def test_finds_expected_number_of_tests(self):
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
Expand Down Expand Up @@ -905,6 +906,7 @@ def test_pcbuild_rt(self):
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.
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def run_on_interactive_mode(source):
return output


@support.force_not_colorized_test_class
class TestInteractiveInterpreter(unittest.TestCase):

@cpython_only
Expand Down Expand Up @@ -273,6 +274,8 @@ def test_asyncio_repl_is_ok(self):

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):
Expand Down
11 changes: 9 additions & 2 deletions Lib/test/test_runpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@
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
Expand Down Expand Up @@ -758,6 +764,7 @@ def test_encoding(self):
self.assertEqual(result['s'], "non-ASCII: h\xe9")


@force_not_colorized_test_class
class TestExit(unittest.TestCase):
STATUS_CONTROL_C_EXIT = 0xC000013A
EXPECTED_CODE = (
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_tracemalloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,7 @@ def check_sys_xoptions_invalid(self, nframe):
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):
Expand Down
11 changes: 9 additions & 2 deletions Lib/test/test_unicodedata.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@
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):
Expand Down Expand Up @@ -277,6 +283,7 @@ def test_disallow_instantiation(self):
# 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
Expand Down
7 changes: 1 addition & 6 deletions Lib/test/test_unittest/test_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
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):
Expand Down Expand Up @@ -121,7 +121,6 @@ def run(self, test):
self.assertEqual(['test.test_unittest', 'test.test_unittest2'],
program.testNames)

@force_not_colorized
def test_NonExit(self):
stream = BufferedWriter()
program = unittest.main(exit=False,
Expand All @@ -137,7 +136,6 @@ def test_NonExit(self):
'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:
Expand All @@ -155,7 +153,6 @@ def test_Exit(self):
'expected failures=1, unexpected successes=1)\n')
self.assertEndsWith(out, expected)

@force_not_colorized
def test_ExitAsDefault(self):
stream = BufferedWriter()
with self.assertRaises(SystemExit):
Expand All @@ -171,7 +168,6 @@ def test_ExitAsDefault(self):
'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:
Expand All @@ -184,7 +180,6 @@ def test_ExitSkippedSuite(self):
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:
Expand Down
Loading
Loading