Skip to content

Commit 6f167d7

Browse files
hugovkserhiy-storchakavstinner
authored
gh-128595: Default to stdout isatty for colour detection instead of stderr (#128498)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent a429159 commit 6f167d7

File tree

8 files changed

+38
-23
lines changed

8 files changed

+38
-23
lines changed

Lib/_colorize.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@ class ANSIColors:
2626
setattr(NoColors, attr, "")
2727

2828

29-
def get_colors(colorize: bool = False) -> ANSIColors:
30-
if colorize or can_colorize():
29+
def get_colors(colorize: bool = False, *, file=None) -> ANSIColors:
30+
if colorize or can_colorize(file=file):
3131
return ANSIColors()
3232
else:
3333
return NoColors
3434

3535

36-
def can_colorize() -> bool:
36+
def can_colorize(*, file=None) -> bool:
37+
if file is None:
38+
file = sys.stdout
39+
3740
if not sys.flags.ignore_environment:
3841
if os.environ.get("PYTHON_COLORS") == "0":
3942
return False
@@ -49,7 +52,7 @@ def can_colorize() -> bool:
4952
if os.environ.get("TERM") == "dumb":
5053
return False
5154

52-
if not hasattr(sys.stderr, "fileno"):
55+
if not hasattr(file, "fileno"):
5356
return False
5457

5558
if sys.platform == "win32":
@@ -62,6 +65,6 @@ def can_colorize() -> bool:
6265
return False
6366

6467
try:
65-
return os.isatty(sys.stderr.fileno())
68+
return os.isatty(file.fileno())
6669
except io.UnsupportedOperation:
67-
return sys.stderr.isatty()
70+
return file.isatty()

Lib/doctest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1558,7 +1558,7 @@ def out(s):
15581558
save_displayhook = sys.displayhook
15591559
sys.displayhook = sys.__displayhook__
15601560
saved_can_colorize = _colorize.can_colorize
1561-
_colorize.can_colorize = lambda: False
1561+
_colorize.can_colorize = lambda *args, **kwargs: False
15621562
color_variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None}
15631563
for key in color_variables:
15641564
color_variables[key] = os.environ.pop(key, None)

Lib/test/libregrtest/single.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ def test_func():
162162
def _runtest_env_changed_exc(result: TestResult, runtests: RunTests,
163163
display_failure: bool = True) -> None:
164164
# Handle exceptions, detect environment changes.
165-
ansi = get_colors()
166-
red, reset, yellow = ansi.RED, ansi.RESET, ansi.YELLOW
165+
stdout = get_colors(file=sys.stdout)
166+
stderr = get_colors(file=sys.stderr)
167167

168168
# Reset the environment_altered flag to detect if a test altered
169169
# the environment
@@ -184,28 +184,34 @@ def _runtest_env_changed_exc(result: TestResult, runtests: RunTests,
184184
_load_run_test(result, runtests)
185185
except support.ResourceDenied as exc:
186186
if not quiet and not pgo:
187-
print(f"{yellow}{test_name} skipped -- {exc}{reset}", flush=True)
187+
print(
188+
f"{stdout.YELLOW}{test_name} skipped -- {exc}{stdout.RESET}",
189+
flush=True,
190+
)
188191
result.state = State.RESOURCE_DENIED
189192
return
190193
except unittest.SkipTest as exc:
191194
if not quiet and not pgo:
192-
print(f"{yellow}{test_name} skipped -- {exc}{reset}", flush=True)
195+
print(
196+
f"{stdout.YELLOW}{test_name} skipped -- {exc}{stdout.RESET}",
197+
flush=True,
198+
)
193199
result.state = State.SKIPPED
194200
return
195201
except support.TestFailedWithDetails as exc:
196-
msg = f"{red}test {test_name} failed{reset}"
202+
msg = f"{stderr.RED}test {test_name} failed{stderr.RESET}"
197203
if display_failure:
198-
msg = f"{red}{msg} -- {exc}{reset}"
204+
msg = f"{stderr.RED}{msg} -- {exc}{stderr.RESET}"
199205
print(msg, file=sys.stderr, flush=True)
200206
result.state = State.FAILED
201207
result.errors = exc.errors
202208
result.failures = exc.failures
203209
result.stats = exc.stats
204210
return
205211
except support.TestFailed as exc:
206-
msg = f"{red}test {test_name} failed{reset}"
212+
msg = f"{stderr.RED}test {test_name} failed{stderr.RESET}"
207213
if display_failure:
208-
msg = f"{red}{msg} -- {exc}{reset}"
214+
msg = f"{stderr.RED}{msg} -- {exc}{stderr.RESET}"
209215
print(msg, file=sys.stderr, flush=True)
210216
result.state = State.FAILED
211217
result.stats = exc.stats
@@ -220,8 +226,11 @@ def _runtest_env_changed_exc(result: TestResult, runtests: RunTests,
220226
except:
221227
if not pgo:
222228
msg = traceback.format_exc()
223-
print(f"{red}test {test_name} crashed -- {msg}{reset}",
224-
file=sys.stderr, flush=True)
229+
print(
230+
f"{stderr.RED}test {test_name} crashed -- {msg}{stderr.RESET}",
231+
file=sys.stderr,
232+
flush=True,
233+
)
225234
result.state = State.UNCAUGHT_EXC
226235
return
227236

@@ -303,7 +312,7 @@ def run_single_test(test_name: TestName, runtests: RunTests) -> TestResult:
303312
If runtests.use_junit, xml_data is a list containing each generated
304313
testsuite element.
305314
"""
306-
ansi = get_colors()
315+
ansi = get_colors(file=sys.stderr)
307316
red, reset, yellow = ansi.BOLD_RED, ansi.RESET, ansi.YELLOW
308317

309318
start_time = time.perf_counter()

Lib/test/support/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2839,7 +2839,7 @@ def no_color():
28392839
from .os_helper import EnvironmentVarGuard
28402840

28412841
with (
2842-
swap_attr(_colorize, "can_colorize", lambda: False),
2842+
swap_attr(_colorize, "can_colorize", lambda file=None: False),
28432843
EnvironmentVarGuard() as env,
28442844
):
28452845
for var in {"FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS"}:

Lib/traceback.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
135135

136136
def _print_exception_bltin(exc, /):
137137
file = sys.stderr if sys.stderr is not None else sys.__stderr__
138-
colorize = _colorize.can_colorize()
138+
colorize = _colorize.can_colorize(file=file)
139139
return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize)
140140

141141

Lib/unittest/result.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ def _exc_info_to_string(self, err, test):
191191
capture_locals=self.tb_locals, compact=True)
192192
from _colorize import can_colorize
193193

194-
msgLines = list(tb_e.format(colorize=can_colorize()))
194+
colorize = hasattr(self, "stream") and can_colorize(file=self.stream)
195+
msgLines = list(tb_e.format(colorize=colorize))
195196

196197
if self.buffer:
197198
output = sys.stdout.getvalue()

Lib/unittest/runner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def __init__(self, stream, descriptions, verbosity, *, durations=None):
4545
self.showAll = verbosity > 1
4646
self.dots = verbosity == 1
4747
self.descriptions = descriptions
48-
self._ansi = get_colors()
48+
self._ansi = get_colors(file=stream)
4949
self._newline = True
5050
self.durations = durations
5151

@@ -286,7 +286,7 @@ def run(self, test):
286286
expected_fails, unexpected_successes, skipped = results
287287

288288
infos = []
289-
ansi = get_colors()
289+
ansi = get_colors(file=self.stream)
290290
bold_red = ansi.BOLD_RED
291291
green = ansi.GREEN
292292
red = ansi.RED
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Default to stdout isatty for color detection instead of stderr. Patch by
2+
Hugo van Kemenade.

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