diff options
Diffstat (limited to 'Lib/test/test_exceptions.py')
-rw-r--r-- | Lib/test/test_exceptions.py | 100 |
1 files changed, 92 insertions, 8 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index d954302..96c3a48 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -7,7 +7,7 @@ import pickle import weakref import errno -from test.support import (TESTFN, captured_output, check_impl_detail, +from test.support import (TESTFN, captured_stderr, check_impl_detail, check_warnings, cpython_only, gc_collect, run_unittest, no_tracing, unlink, import_module) @@ -20,6 +20,10 @@ class SlottedNaiveException(Exception): def __init__(self, x): self.x = x +class BrokenStrException(Exception): + def __str__(self): + raise Exception("str() is broken") + # XXX This is not really enough, each *operation* should be tested! class ExceptionTests(unittest.TestCase): @@ -84,6 +88,7 @@ class ExceptionTests(unittest.TestCase): x += x # this simply shouldn't blow up self.raise_catch(RuntimeError, "RuntimeError") + self.raise_catch(RecursionError, "RecursionError") self.raise_catch(SyntaxError, "SyntaxError") try: exec('/\n') @@ -117,6 +122,8 @@ class ExceptionTests(unittest.TestCase): try: x = 1/0 except Exception as e: pass + self.raise_catch(StopAsyncIteration, "StopAsyncIteration") + def testSyntaxErrorMessage(self): # make sure the right exception message is raised for each of # these code fragments @@ -481,14 +488,14 @@ class ExceptionTests(unittest.TestCase): def testInfiniteRecursion(self): def f(): return f() - self.assertRaises(RuntimeError, f) + self.assertRaises(RecursionError, f) def g(): try: return g() except ValueError: return -1 - self.assertRaises(RuntimeError, g) + self.assertRaises(RecursionError, g) def test_str(self): # Make sure both instances and classes have a str representation. @@ -879,7 +886,7 @@ class ExceptionTests(unittest.TestCase): class MyException(Exception, metaclass=Meta): pass - with captured_output("stderr") as stderr: + with captured_stderr() as stderr: try: raise KeyError() except MyException as e: @@ -894,10 +901,10 @@ class ExceptionTests(unittest.TestCase): def g(): try: return g() - except RuntimeError: + except RecursionError: return sys.exc_info() e, v, tb = g() - self.assertTrue(isinstance(v, RuntimeError), type(v)) + self.assertTrue(isinstance(v, RecursionError), type(v)) self.assertIn("maximum recursion depth exceeded", str(v)) @@ -996,10 +1003,10 @@ class ExceptionTests(unittest.TestCase): # We cannot use assertRaises since it manually deletes the traceback try: inner() - except RuntimeError as e: + except RecursionError as e: self.assertNotEqual(wr(), None) else: - self.fail("RuntimeError not raised") + self.fail("RecursionError not raised") self.assertEqual(wr(), None) def test_errno_ENOTDIR(self): @@ -1008,6 +1015,66 @@ class ExceptionTests(unittest.TestCase): os.listdir(__file__) self.assertEqual(cm.exception.errno, errno.ENOTDIR, cm.exception) + def test_unraisable(self): + # Issue #22836: PyErr_WriteUnraisable() should give sensible reports + class BrokenDel: + def __del__(self): + exc = ValueError("del is broken") + # The following line is included in the traceback report: + raise exc + + class BrokenRepr(BrokenDel): + def __repr__(self): + raise AttributeError("repr() is broken") + + class BrokenExceptionDel: + def __del__(self): + exc = BrokenStrException() + # The following line is included in the traceback report: + raise exc + + for test_class in (BrokenDel, BrokenRepr, BrokenExceptionDel): + with self.subTest(test_class): + obj = test_class() + with captured_stderr() as stderr: + del obj + report = stderr.getvalue() + self.assertIn("Exception ignored", report) + if test_class is BrokenRepr: + self.assertIn("<object repr() failed>", report) + else: + self.assertIn(test_class.__del__.__qualname__, report) + self.assertIn("test_exceptions.py", report) + self.assertIn("raise exc", report) + if test_class is BrokenExceptionDel: + self.assertIn("BrokenStrException", report) + self.assertIn("<exception str() failed>", report) + else: + self.assertIn("ValueError", report) + self.assertIn("del is broken", report) + self.assertTrue(report.endswith("\n")) + + def test_unhandled(self): + # Check for sensible reporting of unhandled exceptions + for exc_type in (ValueError, BrokenStrException): + with self.subTest(exc_type): + try: + exc = exc_type("test message") + # The following line is included in the traceback report: + raise exc + except exc_type: + with captured_stderr() as stderr: + sys.__excepthook__(*sys.exc_info()) + report = stderr.getvalue() + self.assertIn("test_exceptions.py", report) + self.assertIn("raise exc", report) + self.assertIn(exc_type.__name__, report) + if exc_type is BrokenStrException: + self.assertIn("<exception str() failed>", report) + else: + self.assertIn("test message", report) + self.assertTrue(report.endswith("\n")) + class ImportErrorTests(unittest.TestCase): @@ -1029,6 +1096,23 @@ class ImportErrorTests(unittest.TestCase): self.assertEqual(exc.name, 'somename') self.assertEqual(exc.path, 'somepath') + msg = "'invalid' is an invalid keyword argument for this function" + with self.assertRaisesRegex(TypeError, msg): + ImportError('test', invalid='keyword') + + with self.assertRaisesRegex(TypeError, msg): + ImportError('test', name='name', invalid='keyword') + + with self.assertRaisesRegex(TypeError, msg): + ImportError('test', path='path', invalid='keyword') + + with self.assertRaisesRegex(TypeError, msg): + ImportError(invalid='keyword') + + msg = "'invalid|another' is an invalid keyword argument for this function" + with self.assertRaisesRegex(TypeError, msg): + ImportError('test', invalid='keyword', another=True) + def test_non_str_argument(self): # Issue #15778 with check_warnings(('', BytesWarning), quiet=True): |