mirror of
https://github.com/python/cpython.git
synced 2025-08-28 20:56:54 +00:00
bpo-30697: Fix PyErr_NormalizeException() when no memory (GH-2327)
This commit is contained in:
parent
275d2d9c46
commit
56d1f5ca32
8 changed files with 203 additions and 53 deletions
|
@ -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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue