Issue #19235: Add new RecursionError exception. Patch by Georg Brandl.
This commit is contained in:
parent
27be130ec7
commit
f488fb422a
|
@ -683,12 +683,12 @@ recursion depth automatically).
|
|||
sets a :exc:`MemoryError` and returns a nonzero value.
|
||||
|
||||
The function then checks if the recursion limit is reached. If this is the
|
||||
case, a :exc:`RuntimeError` is set and a nonzero value is returned.
|
||||
case, a :exc:`RecursionError` is set and a nonzero value is returned.
|
||||
Otherwise, zero is returned.
|
||||
|
||||
*where* should be a string such as ``" in instance check"`` to be
|
||||
concatenated to the :exc:`RuntimeError` message caused by the recursion depth
|
||||
limit.
|
||||
concatenated to the :exc:`RecursionError` message caused by the recursion
|
||||
depth limit.
|
||||
|
||||
.. c:function:: void Py_LeaveRecursiveCall()
|
||||
|
||||
|
@ -800,6 +800,8 @@ the variables:
|
|||
+-----------------------------------------+---------------------------------+----------+
|
||||
| :c:data:`PyExc_ProcessLookupError` | :exc:`ProcessLookupError` | |
|
||||
+-----------------------------------------+---------------------------------+----------+
|
||||
| :c:data:`PyExc_RecursionError` | :exc:`RecursionError` | |
|
||||
+-----------------------------------------+---------------------------------+----------+
|
||||
| :c:data:`PyExc_ReferenceError` | :exc:`ReferenceError` | \(2) |
|
||||
+-----------------------------------------+---------------------------------+----------+
|
||||
| :c:data:`PyExc_RuntimeError` | :exc:`RuntimeError` | |
|
||||
|
@ -829,6 +831,9 @@ the variables:
|
|||
:c:data:`PyExc_PermissionError`, :c:data:`PyExc_ProcessLookupError`
|
||||
and :c:data:`PyExc_TimeoutError` were introduced following :pep:`3151`.
|
||||
|
||||
.. versionadded:: 3.5
|
||||
:c:data:`PyExc_RecursionError`.
|
||||
|
||||
|
||||
These are compatibility aliases to :c:data:`PyExc_OSError`:
|
||||
|
||||
|
@ -877,6 +882,7 @@ These are compatibility aliases to :c:data:`PyExc_OSError`:
|
|||
single: PyExc_OverflowError
|
||||
single: PyExc_PermissionError
|
||||
single: PyExc_ProcessLookupError
|
||||
single: PyExc_RecursionError
|
||||
single: PyExc_ReferenceError
|
||||
single: PyExc_RuntimeError
|
||||
single: PyExc_SyntaxError
|
||||
|
|
|
@ -282,6 +282,16 @@ The following exceptions are the exceptions that are usually raised.
|
|||
handling in C, most floating point operations are not checked.
|
||||
|
||||
|
||||
.. exception:: RecursionError
|
||||
|
||||
This exception is derived from :exc:`RuntimeError`. It is raised when the
|
||||
interpreter detects that the maximum recursion depth (see
|
||||
:func:`sys.getrecursionlimit`) is exceeded.
|
||||
|
||||
.. versionadded:: 3.5
|
||||
Previously, a plain :exc:`RuntimeError` was raised.
|
||||
|
||||
|
||||
.. exception:: ReferenceError
|
||||
|
||||
This exception is raised when a weak reference proxy, created by the
|
||||
|
|
|
@ -425,7 +425,7 @@ The following types can be pickled:
|
|||
Attempts to pickle unpicklable objects will raise the :exc:`PicklingError`
|
||||
exception; when this happens, an unspecified number of bytes may have already
|
||||
been written to the underlying file. Trying to pickle a highly recursive data
|
||||
structure may exceed the maximum recursion depth, a :exc:`RuntimeError` will be
|
||||
structure may exceed the maximum recursion depth, a :exc:`RecursionError` will be
|
||||
raised in this case. You can carefully raise this limit with
|
||||
:func:`sys.setrecursionlimit`.
|
||||
|
||||
|
|
|
@ -87,6 +87,8 @@ New built-in features:
|
|||
* Generators have new ``gi_yieldfrom`` attribute, which returns the
|
||||
object being iterated by ``yield from`` expressions. (Contributed
|
||||
by Benno Leslie and Yury Selivanov in :issue:`24450`.)
|
||||
* New :exc:`RecursionError` exception. (Contributed by Georg Brandl
|
||||
in :issue:`19235`.)
|
||||
|
||||
Implementation improvements:
|
||||
|
||||
|
|
|
@ -48,16 +48,16 @@ PyAPI_FUNC(int) Py_MakePendingCalls(void);
|
|||
|
||||
In Python 3.0, this protection has two levels:
|
||||
* normal anti-recursion protection is triggered when the recursion level
|
||||
exceeds the current recursion limit. It raises a RuntimeError, and sets
|
||||
exceeds the current recursion limit. It raises a RecursionError, and sets
|
||||
the "overflowed" flag in the thread state structure. This flag
|
||||
temporarily *disables* the normal protection; this allows cleanup code
|
||||
to potentially outgrow the recursion limit while processing the
|
||||
RuntimeError.
|
||||
RecursionError.
|
||||
* "last chance" anti-recursion protection is triggered when the recursion
|
||||
level exceeds "current recursion limit + 50". By construction, this
|
||||
protection can only be triggered when the "overflowed" flag is set. It
|
||||
means the cleanup code has itself gone into an infinite loop, or the
|
||||
RuntimeError has been mistakingly ignored. When this protection is
|
||||
RecursionError has been mistakingly ignored. When this protection is
|
||||
triggered, the interpreter aborts with a Fatal Error.
|
||||
|
||||
In addition, the "overflowed" flag is automatically reset when the
|
||||
|
|
|
@ -167,6 +167,7 @@ PyAPI_DATA(PyObject *) PyExc_MemoryError;
|
|||
PyAPI_DATA(PyObject *) PyExc_NameError;
|
||||
PyAPI_DATA(PyObject *) PyExc_OverflowError;
|
||||
PyAPI_DATA(PyObject *) PyExc_RuntimeError;
|
||||
PyAPI_DATA(PyObject *) PyExc_RecursionError;
|
||||
PyAPI_DATA(PyObject *) PyExc_NotImplementedError;
|
||||
PyAPI_DATA(PyObject *) PyExc_SyntaxError;
|
||||
PyAPI_DATA(PyObject *) PyExc_IndentationError;
|
||||
|
|
|
@ -194,7 +194,7 @@ class BasicWrapTestCase(unittest.TestCase):
|
|||
|
||||
a = A()
|
||||
a._as_parameter_ = a
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.assertRaises(RecursionError):
|
||||
c_int.from_param(a)
|
||||
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ BaseException
|
|||
+-- ReferenceError
|
||||
+-- RuntimeError
|
||||
| +-- NotImplementedError
|
||||
| +-- RecursionError
|
||||
+-- SyntaxError
|
||||
| +-- IndentationError
|
||||
| +-- TabError
|
||||
|
|
|
@ -56,7 +56,7 @@ class CommonTest(seq_tests.CommonTest):
|
|||
l0 = []
|
||||
for i in range(sys.getrecursionlimit() + 100):
|
||||
l0 = [l0]
|
||||
self.assertRaises(RuntimeError, repr, l0)
|
||||
self.assertRaises(RecursionError, repr, l0)
|
||||
|
||||
def test_print(self):
|
||||
d = self.type2test(range(200))
|
||||
|
|
|
@ -500,10 +500,10 @@ class ClassTests(unittest.TestCase):
|
|||
|
||||
try:
|
||||
a() # This should not segfault
|
||||
except RuntimeError:
|
||||
except RecursionError:
|
||||
pass
|
||||
else:
|
||||
self.fail("Failed to raise RuntimeError")
|
||||
self.fail("Failed to raise RecursionError")
|
||||
|
||||
def testForExceptionsRaisedInInstanceGetattr2(self):
|
||||
# Tests for exceptions raised in instance_getattr2().
|
||||
|
|
|
@ -534,7 +534,7 @@ if 1:
|
|||
broken = prefix + repeated * fail_depth
|
||||
details = "Compiling ({!r} + {!r} * {})".format(
|
||||
prefix, repeated, fail_depth)
|
||||
with self.assertRaises(RuntimeError, msg=details):
|
||||
with self.assertRaises(RecursionError, msg=details):
|
||||
self.compile_single(broken)
|
||||
|
||||
check_limit("a", "()")
|
||||
|
|
|
@ -327,7 +327,7 @@ class TestCopy(unittest.TestCase):
|
|||
x.append(x)
|
||||
y = copy.deepcopy(x)
|
||||
for op in comparisons:
|
||||
self.assertRaises(RuntimeError, op, y, x)
|
||||
self.assertRaises(RecursionError, op, y, x)
|
||||
self.assertIsNot(y, x)
|
||||
self.assertIs(y[0], y)
|
||||
self.assertEqual(len(y), 1)
|
||||
|
@ -354,7 +354,7 @@ class TestCopy(unittest.TestCase):
|
|||
x[0].append(x)
|
||||
y = copy.deepcopy(x)
|
||||
for op in comparisons:
|
||||
self.assertRaises(RuntimeError, op, y, x)
|
||||
self.assertRaises(RecursionError, op, y, x)
|
||||
self.assertIsNot(y, x)
|
||||
self.assertIsNot(y[0], x[0])
|
||||
self.assertIs(y[0][0], y)
|
||||
|
@ -373,7 +373,7 @@ class TestCopy(unittest.TestCase):
|
|||
for op in order_comparisons:
|
||||
self.assertRaises(TypeError, op, y, x)
|
||||
for op in equality_comparisons:
|
||||
self.assertRaises(RuntimeError, op, y, x)
|
||||
self.assertRaises(RecursionError, op, y, x)
|
||||
self.assertIsNot(y, x)
|
||||
self.assertIs(y['foo'], y)
|
||||
self.assertEqual(len(y), 1)
|
||||
|
|
|
@ -3342,7 +3342,7 @@ order (MRO) for bases """
|
|||
A.__call__ = A()
|
||||
try:
|
||||
A()()
|
||||
except RuntimeError:
|
||||
except RecursionError:
|
||||
pass
|
||||
else:
|
||||
self.fail("Recursion limit should have been reached for __call__()")
|
||||
|
@ -4317,8 +4317,8 @@ order (MRO) for bases """
|
|||
pass
|
||||
Foo.__repr__ = Foo.__str__
|
||||
foo = Foo()
|
||||
self.assertRaises(RuntimeError, str, foo)
|
||||
self.assertRaises(RuntimeError, repr, foo)
|
||||
self.assertRaises(RecursionError, str, foo)
|
||||
self.assertRaises(RecursionError, repr, foo)
|
||||
|
||||
def test_mixing_slot_wrappers(self):
|
||||
class X(dict):
|
||||
|
|
|
@ -195,7 +195,7 @@ class DictSetTest(unittest.TestCase):
|
|||
def test_recursive_repr(self):
|
||||
d = {}
|
||||
d[42] = d.values()
|
||||
self.assertRaises(RuntimeError, repr, d)
|
||||
self.assertRaises(RecursionError, repr, d)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -84,6 +84,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')
|
||||
|
@ -474,14 +475,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.
|
||||
|
@ -887,10 +888,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))
|
||||
|
||||
|
||||
|
@ -989,10 +990,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):
|
||||
|
|
|
@ -258,18 +258,18 @@ class TestIsInstanceIsSubclass(unittest.TestCase):
|
|||
self.assertEqual(True, issubclass(str, (str, (Child, NewChild, str))))
|
||||
|
||||
def test_subclass_recursion_limit(self):
|
||||
# make sure that issubclass raises RuntimeError before the C stack is
|
||||
# make sure that issubclass raises RecursionError before the C stack is
|
||||
# blown
|
||||
self.assertRaises(RuntimeError, blowstack, issubclass, str, str)
|
||||
self.assertRaises(RecursionError, blowstack, issubclass, str, str)
|
||||
|
||||
def test_isinstance_recursion_limit(self):
|
||||
# make sure that issubclass raises RuntimeError before the C stack is
|
||||
# make sure that issubclass raises RecursionError before the C stack is
|
||||
# blown
|
||||
self.assertRaises(RuntimeError, blowstack, isinstance, '', str)
|
||||
self.assertRaises(RecursionError, blowstack, isinstance, '', str)
|
||||
|
||||
def blowstack(fxn, arg, compare_to):
|
||||
# Make sure that calling isinstance with a deeply nested tuple for its
|
||||
# argument will raise RuntimeError eventually.
|
||||
# argument will raise RecursionError eventually.
|
||||
tuple_arg = (compare_to,)
|
||||
for cnt in range(sys.getrecursionlimit()+5):
|
||||
tuple_arg = (tuple_arg,)
|
||||
|
|
|
@ -68,11 +68,11 @@ class TestRecursion:
|
|||
def test_highly_nested_objects_decoding(self):
|
||||
# test that loading highly-nested objects doesn't segfault when C
|
||||
# accelerations are used. See #12017
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.assertRaises(RecursionError):
|
||||
self.loads('{"a":' * 100000 + '1' + '}' * 100000)
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.assertRaises(RecursionError):
|
||||
self.loads('{"a":' * 100000 + '[1]' + '}' * 100000)
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.assertRaises(RecursionError):
|
||||
self.loads('[' * 100000 + '1' + ']' * 100000)
|
||||
|
||||
def test_highly_nested_objects_encoding(self):
|
||||
|
@ -80,9 +80,9 @@ class TestRecursion:
|
|||
l, d = [], {}
|
||||
for x in range(100000):
|
||||
l, d = [l], {'k':d}
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.assertRaises(RecursionError):
|
||||
self.dumps(l)
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.assertRaises(RecursionError):
|
||||
self.dumps(d)
|
||||
|
||||
def test_endless_recursion(self):
|
||||
|
@ -92,7 +92,7 @@ class TestRecursion:
|
|||
"""If check_circular is False, this will keep adding another list."""
|
||||
return [o]
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
with self.assertRaises(RecursionError):
|
||||
EndlessJSONEncoder(check_circular=False).encode(5j)
|
||||
|
||||
|
||||
|
|
|
@ -353,7 +353,8 @@ class CompatPickleTests(unittest.TestCase):
|
|||
with self.subTest(name):
|
||||
if exc in (BlockingIOError,
|
||||
ResourceWarning,
|
||||
StopAsyncIteration):
|
||||
StopAsyncIteration,
|
||||
RecursionError):
|
||||
continue
|
||||
if exc is not OSError and issubclass(exc, OSError):
|
||||
self.assertEqual(reverse_mapping('builtins', name),
|
||||
|
|
|
@ -228,25 +228,25 @@ class MiscTest(unittest.TestCase):
|
|||
b = UserList()
|
||||
a.append(b)
|
||||
b.append(a)
|
||||
self.assertRaises(RuntimeError, operator.eq, a, b)
|
||||
self.assertRaises(RuntimeError, operator.ne, a, b)
|
||||
self.assertRaises(RuntimeError, operator.lt, a, b)
|
||||
self.assertRaises(RuntimeError, operator.le, a, b)
|
||||
self.assertRaises(RuntimeError, operator.gt, a, b)
|
||||
self.assertRaises(RuntimeError, operator.ge, a, b)
|
||||
self.assertRaises(RecursionError, operator.eq, a, b)
|
||||
self.assertRaises(RecursionError, operator.ne, a, b)
|
||||
self.assertRaises(RecursionError, operator.lt, a, b)
|
||||
self.assertRaises(RecursionError, operator.le, a, b)
|
||||
self.assertRaises(RecursionError, operator.gt, a, b)
|
||||
self.assertRaises(RecursionError, operator.ge, a, b)
|
||||
|
||||
b.append(17)
|
||||
# Even recursive lists of different lengths are different,
|
||||
# but they cannot be ordered
|
||||
self.assertTrue(not (a == b))
|
||||
self.assertTrue(a != b)
|
||||
self.assertRaises(RuntimeError, operator.lt, a, b)
|
||||
self.assertRaises(RuntimeError, operator.le, a, b)
|
||||
self.assertRaises(RuntimeError, operator.gt, a, b)
|
||||
self.assertRaises(RuntimeError, operator.ge, a, b)
|
||||
self.assertRaises(RecursionError, operator.lt, a, b)
|
||||
self.assertRaises(RecursionError, operator.le, a, b)
|
||||
self.assertRaises(RecursionError, operator.gt, a, b)
|
||||
self.assertRaises(RecursionError, operator.ge, a, b)
|
||||
a.append(17)
|
||||
self.assertRaises(RuntimeError, operator.eq, a, b)
|
||||
self.assertRaises(RuntimeError, operator.ne, a, b)
|
||||
self.assertRaises(RecursionError, operator.eq, a, b)
|
||||
self.assertRaises(RecursionError, operator.ne, a, b)
|
||||
a.insert(0, 11)
|
||||
b.insert(0, 12)
|
||||
self.assertTrue(not (a == b))
|
||||
|
|
|
@ -673,7 +673,7 @@ class RunPathTestCase(unittest.TestCase, CodeExecutionMixin):
|
|||
script_name = self._make_test_script(script_dir, mod_name, source)
|
||||
zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name)
|
||||
msg = "recursion depth exceeded"
|
||||
self.assertRaisesRegex(RuntimeError, msg, run_path, zip_name)
|
||||
self.assertRaisesRegex(RecursionError, msg, run_path, zip_name)
|
||||
|
||||
def test_encoding(self):
|
||||
with temp_dir() as script_dir:
|
||||
|
|
|
@ -211,8 +211,8 @@ class SysModuleTest(unittest.TestCase):
|
|||
for i in (50, 1000):
|
||||
# Issue #5392: stack overflow after hitting recursion limit twice
|
||||
sys.setrecursionlimit(i)
|
||||
self.assertRaises(RuntimeError, f)
|
||||
self.assertRaises(RuntimeError, f)
|
||||
self.assertRaises(RecursionError, f)
|
||||
self.assertRaises(RecursionError, f)
|
||||
finally:
|
||||
sys.setrecursionlimit(oldlimit)
|
||||
|
||||
|
@ -225,7 +225,7 @@ class SysModuleTest(unittest.TestCase):
|
|||
def f():
|
||||
try:
|
||||
f()
|
||||
except RuntimeError:
|
||||
except RecursionError:
|
||||
f()
|
||||
|
||||
sys.setrecursionlimit(%d)
|
||||
|
|
|
@ -945,7 +945,7 @@ class ThreadingExceptionTests(BaseTestCase):
|
|||
def outer():
|
||||
try:
|
||||
recurse()
|
||||
except RuntimeError:
|
||||
except RecursionError:
|
||||
pass
|
||||
|
||||
w = threading.Thread(target=outer)
|
||||
|
|
|
@ -30,6 +30,8 @@ Core and Builtins
|
|||
- Issue #24450: Add gi_yieldfrom to generators and cr_await to coroutines.
|
||||
Contributed by Benno Leslie and Yury Selivanov.
|
||||
|
||||
- Issue #19235: Add new RecursionError exception. Patch by Georg Brandl.
|
||||
|
||||
Library
|
||||
-------
|
||||
|
||||
|
|
|
@ -3622,7 +3622,7 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
|
|||
>>> pickle.dumps(1+2j)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
RuntimeError: maximum recursion depth exceeded
|
||||
RecursionError: maximum recursion depth exceeded
|
||||
|
||||
Removing the complex class from copyreg.dispatch_table made the
|
||||
__reduce_ex__() method emit another complex object:
|
||||
|
|
|
@ -500,8 +500,9 @@ pattern_error(Py_ssize_t status)
|
|||
{
|
||||
switch (status) {
|
||||
case SRE_ERROR_RECURSION_LIMIT:
|
||||
/* This error code seems to be unused. */
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
PyExc_RecursionError,
|
||||
"maximum recursion limit exceeded"
|
||||
);
|
||||
break;
|
||||
|
|
|
@ -1231,6 +1231,11 @@ SimpleExtendsException(PyExc_Exception, EOFError,
|
|||
SimpleExtendsException(PyExc_Exception, RuntimeError,
|
||||
"Unspecified run-time error.");
|
||||
|
||||
/*
|
||||
* RecursionError extends RuntimeError
|
||||
*/
|
||||
SimpleExtendsException(PyExc_RuntimeError, RecursionError,
|
||||
"Recursion limit exceeded.");
|
||||
|
||||
/*
|
||||
* NotImplementedError extends RuntimeError
|
||||
|
@ -2380,7 +2385,7 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning,
|
|||
|
||||
|
||||
|
||||
/* Pre-computed RuntimeError instance for when recursion depth is reached.
|
||||
/* Pre-computed RecursionError instance for when recursion depth is reached.
|
||||
Meant to be used when normalizing the exception for exceeding the recursion
|
||||
depth will cause its own infinite recursion.
|
||||
*/
|
||||
|
@ -2484,6 +2489,7 @@ _PyExc_Init(PyObject *bltinmod)
|
|||
PRE_INIT(OSError)
|
||||
PRE_INIT(EOFError)
|
||||
PRE_INIT(RuntimeError)
|
||||
PRE_INIT(RecursionError)
|
||||
PRE_INIT(NotImplementedError)
|
||||
PRE_INIT(NameError)
|
||||
PRE_INIT(UnboundLocalError)
|
||||
|
@ -2560,6 +2566,7 @@ _PyExc_Init(PyObject *bltinmod)
|
|||
#endif
|
||||
POST_INIT(EOFError)
|
||||
POST_INIT(RuntimeError)
|
||||
POST_INIT(RecursionError)
|
||||
POST_INIT(NotImplementedError)
|
||||
POST_INIT(NameError)
|
||||
POST_INIT(UnboundLocalError)
|
||||
|
@ -2643,9 +2650,9 @@ _PyExc_Init(PyObject *bltinmod)
|
|||
preallocate_memerrors();
|
||||
|
||||
if (!PyExc_RecursionErrorInst) {
|
||||
PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL);
|
||||
PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RecursionError, NULL, NULL);
|
||||
if (!PyExc_RecursionErrorInst)
|
||||
Py_FatalError("Cannot pre-allocate RuntimeError instance for "
|
||||
Py_FatalError("Cannot pre-allocate RecursionError instance for "
|
||||
"recursion errors");
|
||||
else {
|
||||
PyBaseExceptionObject *err_inst =
|
||||
|
@ -2654,15 +2661,15 @@ _PyExc_Init(PyObject *bltinmod)
|
|||
PyObject *exc_message;
|
||||
exc_message = PyUnicode_FromString("maximum recursion depth exceeded");
|
||||
if (!exc_message)
|
||||
Py_FatalError("cannot allocate argument for RuntimeError "
|
||||
Py_FatalError("cannot allocate argument for RecursionError "
|
||||
"pre-allocation");
|
||||
args_tuple = PyTuple_Pack(1, exc_message);
|
||||
if (!args_tuple)
|
||||
Py_FatalError("cannot allocate tuple for RuntimeError "
|
||||
Py_FatalError("cannot allocate tuple for RecursionError "
|
||||
"pre-allocation");
|
||||
Py_DECREF(exc_message);
|
||||
if (BaseException_init(err_inst, args_tuple, NULL))
|
||||
Py_FatalError("init of pre-allocated RuntimeError failed");
|
||||
Py_FatalError("init of pre-allocated RecursionError failed");
|
||||
Py_DECREF(args_tuple);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4142,7 +4142,7 @@ reduce_newobj(PyObject *obj, int proto)
|
|||
* were implemented in the same function:
|
||||
* - trying to pickle an object with a custom __reduce__ method that
|
||||
* fell back to object.__reduce__ in certain circumstances led to
|
||||
* infinite recursion at Python level and eventual RuntimeError.
|
||||
* infinite recursion at Python level and eventual RecursionError.
|
||||
* - Pickling objects that lied about their type by overwriting the
|
||||
* __class__ descriptor could lead to infinite recursion at C level
|
||||
* and eventual segfault.
|
||||
|
|
|
@ -737,7 +737,7 @@ _Py_CheckRecursiveCall(const char *where)
|
|||
if (tstate->recursion_depth > recursion_limit) {
|
||||
--tstate->recursion_depth;
|
||||
tstate->overflowed = 1;
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
PyErr_Format(PyExc_RecursionError,
|
||||
"maximum recursion depth exceeded%s",
|
||||
where);
|
||||
return -1;
|
||||
|
|
|
@ -319,7 +319,7 @@ finally:
|
|||
Py_DECREF(*exc);
|
||||
Py_DECREF(*val);
|
||||
/* ... and use the recursion error instead */
|
||||
*exc = PyExc_RuntimeError;
|
||||
*exc = PyExc_RecursionError;
|
||||
*val = PyExc_RecursionErrorInst;
|
||||
Py_INCREF(*exc);
|
||||
Py_INCREF(*val);
|
||||
|
|
|
@ -1135,7 +1135,7 @@ static int
|
|||
symtable_visit_stmt(struct symtable *st, stmt_ty s)
|
||||
{
|
||||
if (++st->recursion_depth > st->recursion_limit) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
PyErr_SetString(PyExc_RecursionError,
|
||||
"maximum recursion depth exceeded during compilation");
|
||||
VISIT_QUIT(st, 0);
|
||||
}
|
||||
|
@ -1357,7 +1357,7 @@ static int
|
|||
symtable_visit_expr(struct symtable *st, expr_ty e)
|
||||
{
|
||||
if (++st->recursion_depth > st->recursion_limit) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
PyErr_SetString(PyExc_RecursionError,
|
||||
"maximum recursion depth exceeded during compilation");
|
||||
VISIT_QUIT(st, 0);
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ def test_cpickle(_cache={}):
|
|||
def test_compiler_recursion():
|
||||
# The compiler uses a scaling factor to support additional levels
|
||||
# of recursion. This is a sanity check of that scaling to ensure
|
||||
# it still raises RuntimeError even at higher recursion limits
|
||||
# it still raises RecursionError even at higher recursion limits
|
||||
compile("()" * (10 * sys.getrecursionlimit()), "<single>", "single")
|
||||
|
||||
def check_limit(n, test_func_name):
|
||||
|
@ -107,7 +107,7 @@ def check_limit(n, test_func_name):
|
|||
# AttributeError can be raised because of the way e.g. PyDict_GetItem()
|
||||
# silences all exceptions and returns NULL, which is usually interpreted
|
||||
# as "missing attribute".
|
||||
except (RuntimeError, AttributeError):
|
||||
except (RecursionError, AttributeError):
|
||||
pass
|
||||
else:
|
||||
print("Yikes!")
|
||||
|
|
Loading…
Reference in New Issue