import os.path
import sys
import textwrap
+from test import support
def format_duration(seconds):
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
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,
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):
--- /dev/null
+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.
+