summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_exceptions.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_exceptions.py')
-rw-r--r--Lib/test/test_exceptions.py100
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):