[Python-checkins] bpo-37069: regrtest uses sys.unraisablehook (GH-13759)

Victor Stinner webhook-mailer at python.org
Wed Jun 12 19:09:08 EDT 2019


https://github.com/python/cpython/commit/95f61c8b1619e736bd5e29a0da0183234634b6e8
commit: 95f61c8b1619e736bd5e29a0da0183234634b6e8
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-06-13T01:09:04+02:00
summary:

bpo-37069: regrtest uses sys.unraisablehook (GH-13759)

regrtest now uses sys.unraisablehook() to mark a test as "environment
altered" (ENV_CHANGED) if it emits an "unraisable exception".
Moreover, regrtest logs a warning in this case.

Use "python3 -m test --fail-env-changed" to catch unraisable
exceptions in tests.

files:
A Misc/NEWS.d/next/Tests/2019-06-13-00-46-25.bpo-37069.wdktFo.rst
M Lib/test/libregrtest/setup.py
M Lib/test/libregrtest/utils.py
M Lib/test/test_regrtest.py

diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py
index fb5ac350cd08..36676bfa617b 100644
--- a/Lib/test/libregrtest/setup.py
+++ b/Lib/test/libregrtest/setup.py
@@ -10,6 +10,8 @@
 except ImportError:
     gc = None
 
+from test.libregrtest.utils import setup_unraisable_hook
+
 
 def setup_tests(ns):
     try:
@@ -93,6 +95,8 @@ def _test_audit_hook(name, args):
             pass
         sys.addaudithook(_test_audit_hook)
 
+    setup_unraisable_hook()
+
 
 def suppress_msvcrt_asserts(verbose):
     try:
diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py
index fb9971a64f66..2691a2c30ce8 100644
--- a/Lib/test/libregrtest/utils.py
+++ b/Lib/test/libregrtest/utils.py
@@ -2,6 +2,7 @@
 import os.path
 import sys
 import textwrap
+from test import support
 
 
 def format_duration(seconds):
@@ -59,3 +60,19 @@ def printlist(x, width=70, indent=4, file=None):
 
 def print_warning(msg):
     print(f"Warning -- {msg}", file=sys.stderr, flush=True)
+
+
+orig_unraisablehook = None
+
+
+def regrtest_unraisable_hook(unraisable):
+    global orig_unraisablehook
+    support.environment_altered = True
+    print_warning("Unraisable exception")
+    orig_unraisablehook(unraisable)
+
+
+def setup_unraisable_hook():
+    global orig_unraisablehook
+    orig_unraisablehook = sys.unraisablehook
+    sys.unraisablehook = regrtest_unraisable_hook
diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py
index b616e8974b9d..904b326d0e20 100644
--- a/Lib/test/test_regrtest.py
+++ b/Lib/test/test_regrtest.py
@@ -499,7 +499,7 @@ def run_command(self, args, input=None, exitcode=0, **kw):
         if not input:
             input = ''
         if 'stderr' not in kw:
-            kw['stderr'] = subprocess.PIPE
+            kw['stderr'] = subprocess.STDOUT
         proc = subprocess.run(args,
                               universal_newlines=True,
                               input=input,
@@ -1124,6 +1124,34 @@ def test_garbage(self):
                                   env_changed=[testname],
                                   fail_env_changed=True)
 
+    def test_unraisable_exc(self):
+        # --fail-env-changed must catch unraisable exception
+        code = textwrap.dedent(r"""
+            import unittest
+            import weakref
+
+            class MyObject:
+                pass
+
+            def weakref_callback(obj):
+                raise Exception("weakref callback bug")
+
+            class Tests(unittest.TestCase):
+                def test_unraisable_exc(self):
+                    obj = MyObject()
+                    ref = weakref.ref(obj, weakref_callback)
+                    # call weakref_callback() which logs
+                    # an unraisable exception
+                    obj = None
+        """)
+        testname = self.create_test(code=code)
+
+        output = self.run_tests("--fail-env-changed", "-v", testname, exitcode=3)
+        self.check_executed_tests(output, [testname],
+                                  env_changed=[testname],
+                                  fail_env_changed=True)
+        self.assertIn("Warning -- Unraisable exception", output)
+
 
 class TestUtils(unittest.TestCase):
     def test_format_duration(self):
diff --git a/Misc/NEWS.d/next/Tests/2019-06-13-00-46-25.bpo-37069.wdktFo.rst b/Misc/NEWS.d/next/Tests/2019-06-13-00-46-25.bpo-37069.wdktFo.rst
new file mode 100644
index 000000000000..f9f6474ac8cf
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2019-06-13-00-46-25.bpo-37069.wdktFo.rst
@@ -0,0 +1,7 @@
+regrtest now uses :func:`sys.unraisablehook` to mark a test as "environment
+altered" (ENV_CHANGED) if it emits an "unraisable exception". Moreover,
+regrtest logs a warning in this case.
+
+Use ``python3 -m test --fail-env-changed`` to catch unraisable exceptions in
+tests.
+



More information about the Python-checkins mailing list