]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-45410: regrtest -W leaves stdout/err FD unchanged (GH-28915)
authorVictor Stinner <vstinner@python.org>
Wed, 13 Oct 2021 12:08:18 +0000 (14:08 +0200)
committerGitHub <noreply@github.com>
Wed, 13 Oct 2021 12:08:18 +0000 (14:08 +0200)
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.

Lib/test/libregrtest/cmdline.py
Lib/test/libregrtest/refleak.py
Lib/test/libregrtest/runtest.py
Lib/test/libregrtest/runtest_mp.py
Lib/test/libregrtest/utils.py
Lib/test/support/__init__.py
Lib/test/test_support.py

index 29f4ede523b0072c639c33b0596aa607ca5f1672..6e6ff1201d81c7f741a87604274394eea2043129 100644 (file)
@@ -1,7 +1,6 @@
 import argparse
 import os
 import sys
-from test import support
 from test.support import os_helper
 
 
index b776a2cce647d04e15b0e4f292bbeffea983444f..1069e2da008ff77c593058bbb78841bc0ce15d06 100644 (file)
@@ -1,5 +1,4 @@
 import os
-import re
 import sys
 import warnings
 from inspect import isabstract
index 31474a222ed0959ebae4d952379c3ba910755cac..6fb996a5f947b4bbb956e381b4064b511559e4d2 100644 (file)
@@ -1,4 +1,3 @@
-import contextlib
 import faulthandler
 import functools
 import gc
@@ -6,7 +5,6 @@ import importlib
 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
 
 
-@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
-
-
-@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)
index dea5992feb7b4a3204b9ffcd8d888f287bc58eec..f02f56e98bcda261f827d7c47f63bfffbbe65c01 100644 (file)
@@ -1,4 +1,3 @@
-import collections
 import faulthandler
 import json
 import os
index 256f9a4cb6eed7dc7567b999b516a26c55188fa9..8578a028c78bc2946e33e3cc00e4681d3a968e7e 100644 (file)
@@ -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:
index 45801dc317a6ab136089470899fe9185c63a601d..85fd74126b5f47afb2be7c1d2c967c97ce87b045 100644 (file)
@@ -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
index 8b55352b6eecc6fa0e43372a96ddfb1da7e3f9f8..d5a1d447f05634345608508fa14ec2e397094fb9 100644 (file)
@@ -469,12 +469,8 @@ class TestSupport(unittest.TestCase):
                 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 @@ class TestSupport(unittest.TestCase):
 
     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):