[Python-checkins] bpo-45410: regrtest -W leaves stdout/err FD unchanged (GH-28915)
vstinner
webhook-mailer at python.org
Wed Oct 13 08:08:49 EDT 2021
https://github.com/python/cpython/commit/773330773968f211c77abc7b5b525faa7b3c35a2
commit: 773330773968f211c77abc7b5b525faa7b3c35a2
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-10-13T14:08:18+02:00
summary:
bpo-45410: regrtest -W leaves stdout/err FD unchanged (GH-28915)
support.print_warning() now stores the original value of
sys.__stderr__ and uses it to log warnings. libregrtest uses the same
stream to log unraisable exceptions and uncaught threading
exceptions.
Partially revert commit dbe213de7ef28712bbfdb9d94a33abb9c33ef0c2:
libregrtest no longer replaces sys.__stdout__, sys.__stderr__, and
stdout and stderr file descriptors.
Remove also a few unused imports in libregrtest.
files:
M Lib/test/libregrtest/cmdline.py
M Lib/test/libregrtest/refleak.py
M Lib/test/libregrtest/runtest.py
M Lib/test/libregrtest/runtest_mp.py
M Lib/test/libregrtest/utils.py
M Lib/test/support/__init__.py
M Lib/test/test_support.py
diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py
index 29f4ede523b00..6e6ff1201d81c 100644
--- a/Lib/test/libregrtest/cmdline.py
+++ b/Lib/test/libregrtest/cmdline.py
@@ -1,7 +1,6 @@
import argparse
import os
import sys
-from test import support
from test.support import os_helper
diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py
index b776a2cce647d..1069e2da008ff 100644
--- a/Lib/test/libregrtest/refleak.py
+++ b/Lib/test/libregrtest/refleak.py
@@ -1,5 +1,4 @@
import os
-import re
import sys
import warnings
from inspect import isabstract
diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py
index 31474a222ed09..6fb996a5f947b 100644
--- a/Lib/test/libregrtest/runtest.py
+++ b/Lib/test/libregrtest/runtest.py
@@ -1,4 +1,3 @@
-import contextlib
import faulthandler
import functools
import gc
@@ -6,7 +5,6 @@
import io
import os
import sys
-import tempfile
import time
import traceback
import unittest
@@ -175,63 +173,6 @@ def get_abs_module(ns: Namespace, test_name: str) -> str:
return 'test.' + test_name
- at contextlib.contextmanager
-def override_fd(fd, fd2):
- fd2_copy = os.dup(fd2)
- try:
- os.dup2(fd, fd2)
- yield
- finally:
- os.dup2(fd2_copy, fd2)
- os.close(fd2_copy)
-
-
-def get_stream_fd(stream):
- if stream is None:
- return None
- try:
- return stream.fileno()
- except io.UnsupportedOperation:
- return None
-
-
- at contextlib.contextmanager
-def capture_std_streams():
- """
- Redirect all standard streams to a temporary file:
-
- * stdout and stderr file descriptors (fd 1 and fd 2)
- * sys.stdout, sys.__stdout__
- * sys.stderr, sys.__stderr__
- """
- try:
- stderr_fd = sys.stderr.fileno()
- except io.UnsupportedOperation:
- stderr_fd = None
-
- # Use a temporary file to support fileno() operation
- tmp_file = tempfile.TemporaryFile(mode='w+',
- # line buffering
- buffering=1,
- encoding=sys.stderr.encoding,
- errors=sys.stderr.errors)
- with contextlib.ExitStack() as stack:
- stack.enter_context(tmp_file)
-
- # Override stdout and stderr file descriptors
- tmp_fd = tmp_file.fileno()
- for stream in (sys.stdout, sys.stderr):
- fd = get_stream_fd(stream)
- if fd is not None:
- stack.enter_context(override_fd(tmp_fd, fd))
-
- # Override sys attributes
- for name in ('stdout', 'stderr', '__stdout__', '__stderr__'):
- stack.enter_context(support.swap_attr(sys, name, tmp_file))
-
- yield tmp_file
-
-
def _runtest(ns: Namespace, test_name: str) -> TestResult:
# Handle faulthandler timeout, capture stdout+stderr, XML serialization
# and measure time.
@@ -252,13 +193,20 @@ def _runtest(ns: Namespace, test_name: str) -> TestResult:
if output_on_failure:
support.verbose = True
+ stream = io.StringIO()
+ orig_stdout = sys.stdout
+ orig_stderr = sys.stderr
output = None
- with capture_std_streams() as stream:
+ try:
+ sys.stdout = stream
+ sys.stderr = stream
result = _runtest_inner(ns, test_name,
display_failure=False)
if not isinstance(result, Passed):
- stream.seek(0)
- output = stream.read()
+ output = stream.getvalue()
+ finally:
+ sys.stdout = orig_stdout
+ sys.stderr = orig_stderr
if output is not None:
sys.stderr.write(output)
diff --git a/Lib/test/libregrtest/runtest_mp.py b/Lib/test/libregrtest/runtest_mp.py
index dea5992feb7b4..f02f56e98bcda 100644
--- a/Lib/test/libregrtest/runtest_mp.py
+++ b/Lib/test/libregrtest/runtest_mp.py
@@ -1,4 +1,3 @@
-import collections
import faulthandler
import json
import os
diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py
index 256f9a4cb6eed..8578a028c78bc 100644
--- a/Lib/test/libregrtest/utils.py
+++ b/Lib/test/libregrtest/utils.py
@@ -75,7 +75,7 @@ def regrtest_unraisable_hook(unraisable):
old_stderr = sys.stderr
try:
support.flush_std_streams()
- sys.stderr = sys.__stderr__
+ sys.stderr = support.print_warning.orig_stderr
orig_unraisablehook(unraisable)
sys.stderr.flush()
finally:
@@ -98,7 +98,7 @@ def regrtest_threading_excepthook(args):
old_stderr = sys.stderr
try:
support.flush_std_streams()
- sys.stderr = sys.__stderr__
+ sys.stderr = support.print_warning.orig_stderr
orig_threading_excepthook(args)
sys.stderr.flush()
finally:
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 45801dc317a6a..85fd74126b5f4 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -1177,13 +1177,15 @@ def flush_std_streams():
def print_warning(msg):
# bpo-45410: Explicitly flush stdout to keep logs in order
flush_std_streams()
- # bpo-39983: Print into sys.__stderr__ to display the warning even
- # when sys.stderr is captured temporarily by a test
- stream = sys.__stderr__
+ stream = print_warning.orig_stderr
for line in msg.splitlines():
print(f"Warning -- {line}", file=stream)
stream.flush()
+# bpo-39983: Store the original sys.stderr at Python startup to be able to
+# log warnings even if sys.stderr is captured temporarily by a test.
+print_warning.orig_stderr = sys.stderr
+
# Flag used by saved_test_environment of test.libregrtest.save_env,
# to check if a test modified the environment. The flag should be set to False
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
index 8b55352b6eecc..d5a1d447f0563 100644
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -469,12 +469,8 @@ def test_reap_children(self):
if time.monotonic() > deadline:
self.fail("timeout")
- old_stderr = sys.__stderr__
- try:
- sys.__stderr__ = stderr
+ with support.swap_attr(support.print_warning, 'orig_stderr', stderr):
support.reap_children()
- finally:
- sys.__stderr__ = old_stderr
# Use environment_altered to check if reap_children() found
# the child process
@@ -674,14 +670,8 @@ def test_fd_count(self):
def check_print_warning(self, msg, expected):
stderr = io.StringIO()
-
- old_stderr = sys.__stderr__
- try:
- sys.__stderr__ = stderr
+ with support.swap_attr(support.print_warning, 'orig_stderr', stderr):
support.print_warning(msg)
- finally:
- sys.__stderr__ = old_stderr
-
self.assertEqual(stderr.getvalue(), expected)
def test_print_warning(self):
More information about the Python-checkins
mailing list