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.py103
1 files changed, 101 insertions, 2 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index cef8d44..8dceb5c 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -10,8 +10,8 @@ import errno
from test.support import (TESTFN, captured_stderr, check_impl_detail,
check_warnings, cpython_only, gc_collect, run_unittest,
- no_tracing, unlink, import_module, script_helper)
-
+ no_tracing, unlink, import_module, script_helper,
+ SuppressCrashReport)
class NaiveException(Exception):
def __init__(self, x):
self.x = x
@@ -936,6 +936,105 @@ class ExceptionTests(unittest.TestCase):
self.assertIsInstance(v, RecursionError, type(v))
self.assertIn("maximum recursion depth exceeded", str(v))
+ @cpython_only
+ def test_recursion_normalizing_exception(self):
+ # Issue #22898.
+ # Test that a RecursionError is raised when tstate->recursion_depth is
+ # equal to recursion_limit in PyErr_NormalizeException() and check
+ # that a ResourceWarning is printed.
+ # Prior to #22898, the recursivity of PyErr_NormalizeException() was
+ # controled by tstate->recursion_depth and a PyExc_RecursionErrorInst
+ # singleton was being used in that case, that held traceback data and
+ # locals indefinitely and would cause a segfault in _PyExc_Fini() upon
+ # finalization of these locals.
+ code = """if 1:
+ import sys
+ from _testcapi import get_recursion_depth
+
+ class MyException(Exception): pass
+
+ def setrecursionlimit(depth):
+ while 1:
+ try:
+ sys.setrecursionlimit(depth)
+ return depth
+ except RecursionError:
+ # sys.setrecursionlimit() raises a RecursionError if
+ # the new recursion limit is too low (issue #25274).
+ depth += 1
+
+ def recurse(cnt):
+ cnt -= 1
+ if cnt:
+ recurse(cnt)
+ else:
+ generator.throw(MyException)
+
+ def gen():
+ f = open(%a, mode='rb', buffering=0)
+ yield
+
+ generator = gen()
+ next(generator)
+ recursionlimit = sys.getrecursionlimit()
+ depth = get_recursion_depth()
+ try:
+ # Upon the last recursive invocation of recurse(),
+ # tstate->recursion_depth is equal to (recursion_limit - 1)
+ # and is equal to recursion_limit when _gen_throw() calls
+ # PyErr_NormalizeException().
+ recurse(setrecursionlimit(depth + 2) - depth - 1)
+ finally:
+ sys.setrecursionlimit(recursionlimit)
+ print('Done.')
+ """ % __file__
+ rc, out, err = script_helper.assert_python_failure("-Wd", "-c", code)
+ # Check that the program does not fail with SIGABRT.
+ self.assertEqual(rc, 1)
+ self.assertIn(b'RecursionError', err)
+ self.assertIn(b'ResourceWarning', err)
+ self.assertIn(b'Done.', out)
+
+ @cpython_only
+ def test_recursion_normalizing_infinite_exception(self):
+ # Issue #30697. Test that a RecursionError is raised when
+ # PyErr_NormalizeException() maximum recursion depth has been
+ # exceeded.
+ code = """if 1:
+ import _testcapi
+ try:
+ raise _testcapi.RecursingInfinitelyError
+ finally:
+ print('Done.')
+ """
+ rc, out, err = script_helper.assert_python_failure("-c", code)
+ self.assertEqual(rc, 1)
+ self.assertIn(b'RecursionError: maximum recursion depth exceeded '
+ b'while normalizing an exception', err)
+ self.assertIn(b'Done.', out)
+
+ @cpython_only
+ def test_recursion_normalizing_with_no_memory(self):
+ # Issue #30697. Test that in the abort that occurs when there is no
+ # memory left and the size of the Python frames stack is greater than
+ # the size of the list of preallocated MemoryError instances, the
+ # Fatal Python error message mentions MemoryError.
+ code = """if 1:
+ import _testcapi
+ class C(): pass
+ def recurse(cnt):
+ cnt -= 1
+ if cnt:
+ recurse(cnt)
+ else:
+ _testcapi.set_nomemory(0)
+ C()
+ recurse(16)
+ """
+ with SuppressCrashReport():
+ rc, out, err = script_helper.assert_python_failure("-c", code)
+ self.assertIn(b'Fatal Python error: Cannot recover from '
+ b'MemoryErrors while normalizing exceptions.', err)
@cpython_only
def test_MemoryError(self):