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