summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorxdegaye <xdegaye@gmail.com>2017-10-26 13:09:06 (GMT)
committerGitHub <noreply@github.com>2017-10-26 13:09:06 (GMT)
commit56d1f5ca32892c7643eb8cee49c40c1644f1abfe (patch)
tree696b62e9bdd22063a894ecb5a7b9eafe60385edf /Lib/test
parent275d2d9c4663a1ea8d1f7c8778567a735b0372c1 (diff)
downloadcpython-56d1f5ca32892c7643eb8cee49c40c1644f1abfe.zip
cpython-56d1f5ca32892c7643eb8cee49c40c1644f1abfe.tar.gz
cpython-56d1f5ca32892c7643eb8cee49c40c1644f1abfe.tar.bz2
bpo-30697: Fix PyErr_NormalizeException() when no memory (GH-2327)
Diffstat (limited to 'Lib/test')
-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):