Skip to content

Commit f488fb4

Browse files
committed
Issue python#19235: Add new RecursionError exception. Patch by Georg Brandl.
1 parent 27be130 commit f488fb4

31 files changed

+101
-69
lines changed

Doc/c-api/exceptions.rst

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -683,12 +683,12 @@ recursion depth automatically).
683683
sets a :exc:`MemoryError` and returns a nonzero value.
684684
685685
The function then checks if the recursion limit is reached. If this is the
686-
case, a :exc:`RuntimeError` is set and a nonzero value is returned.
686+
case, a :exc:`RecursionError` is set and a nonzero value is returned.
687687
Otherwise, zero is returned.
688688
689689
*where* should be a string such as ``" in instance check"`` to be
690-
concatenated to the :exc:`RuntimeError` message caused by the recursion depth
691-
limit.
690+
concatenated to the :exc:`RecursionError` message caused by the recursion
691+
depth limit.
692692
693693
.. c:function:: void Py_LeaveRecursiveCall()
694694
@@ -800,6 +800,8 @@ the variables:
800800
+-----------------------------------------+---------------------------------+----------+
801801
| :c:data:`PyExc_ProcessLookupError` | :exc:`ProcessLookupError` | |
802802
+-----------------------------------------+---------------------------------+----------+
803+
| :c:data:`PyExc_RecursionError` | :exc:`RecursionError` | |
804+
+-----------------------------------------+---------------------------------+----------+
803805
| :c:data:`PyExc_ReferenceError` | :exc:`ReferenceError` | \(2) |
804806
+-----------------------------------------+---------------------------------+----------+
805807
| :c:data:`PyExc_RuntimeError` | :exc:`RuntimeError` | |
@@ -829,6 +831,9 @@ the variables:
829831
:c:data:`PyExc_PermissionError`, :c:data:`PyExc_ProcessLookupError`
830832
and :c:data:`PyExc_TimeoutError` were introduced following :pep:`3151`.
831833
834+
.. versionadded:: 3.5
835+
:c:data:`PyExc_RecursionError`.
836+
832837
833838
These are compatibility aliases to :c:data:`PyExc_OSError`:
834839
@@ -877,6 +882,7 @@ These are compatibility aliases to :c:data:`PyExc_OSError`:
877882
single: PyExc_OverflowError
878883
single: PyExc_PermissionError
879884
single: PyExc_ProcessLookupError
885+
single: PyExc_RecursionError
880886
single: PyExc_ReferenceError
881887
single: PyExc_RuntimeError
882888
single: PyExc_SyntaxError

Doc/library/exceptions.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,16 @@ The following exceptions are the exceptions that are usually raised.
282282
handling in C, most floating point operations are not checked.
283283

284284

285+
.. exception:: RecursionError
286+
287+
This exception is derived from :exc:`RuntimeError`. It is raised when the
288+
interpreter detects that the maximum recursion depth (see
289+
:func:`sys.getrecursionlimit`) is exceeded.
290+
291+
.. versionadded:: 3.5
292+
Previously, a plain :exc:`RuntimeError` was raised.
293+
294+
285295
.. exception:: ReferenceError
286296

287297
This exception is raised when a weak reference proxy, created by the

Doc/library/pickle.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ The following types can be pickled:
425425
Attempts to pickle unpicklable objects will raise the :exc:`PicklingError`
426426
exception; when this happens, an unspecified number of bytes may have already
427427
been written to the underlying file. Trying to pickle a highly recursive data
428-
structure may exceed the maximum recursion depth, a :exc:`RuntimeError` will be
428+
structure may exceed the maximum recursion depth, a :exc:`RecursionError` will be
429429
raised in this case. You can carefully raise this limit with
430430
:func:`sys.setrecursionlimit`.
431431

Doc/whatsnew/3.5.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ New built-in features:
8787
* Generators have new ``gi_yieldfrom`` attribute, which returns the
8888
object being iterated by ``yield from`` expressions. (Contributed
8989
by Benno Leslie and Yury Selivanov in :issue:`24450`.)
90+
* New :exc:`RecursionError` exception. (Contributed by Georg Brandl
91+
in :issue:`19235`.)
9092

9193
Implementation improvements:
9294

Include/ceval.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ PyAPI_FUNC(int) Py_MakePendingCalls(void);
4848
4949
In Python 3.0, this protection has two levels:
5050
* normal anti-recursion protection is triggered when the recursion level
51-
exceeds the current recursion limit. It raises a RuntimeError, and sets
51+
exceeds the current recursion limit. It raises a RecursionError, and sets
5252
the "overflowed" flag in the thread state structure. This flag
5353
temporarily *disables* the normal protection; this allows cleanup code
5454
to potentially outgrow the recursion limit while processing the
55-
RuntimeError.
55+
RecursionError.
5656
* "last chance" anti-recursion protection is triggered when the recursion
5757
level exceeds "current recursion limit + 50". By construction, this
5858
protection can only be triggered when the "overflowed" flag is set. It
5959
means the cleanup code has itself gone into an infinite loop, or the
60-
RuntimeError has been mistakingly ignored. When this protection is
60+
RecursionError has been mistakingly ignored. When this protection is
6161
triggered, the interpreter aborts with a Fatal Error.
6262
6363
In addition, the "overflowed" flag is automatically reset when the

Include/pyerrors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ PyAPI_DATA(PyObject *) PyExc_MemoryError;
167167
PyAPI_DATA(PyObject *) PyExc_NameError;
168168
PyAPI_DATA(PyObject *) PyExc_OverflowError;
169169
PyAPI_DATA(PyObject *) PyExc_RuntimeError;
170+
PyAPI_DATA(PyObject *) PyExc_RecursionError;
170171
PyAPI_DATA(PyObject *) PyExc_NotImplementedError;
171172
PyAPI_DATA(PyObject *) PyExc_SyntaxError;
172173
PyAPI_DATA(PyObject *) PyExc_IndentationError;

Lib/ctypes/test/test_as_parameter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ class A(object):
194194

195195
a = A()
196196
a._as_parameter_ = a
197-
with self.assertRaises(RuntimeError):
197+
with self.assertRaises(RecursionError):
198198
c_int.from_param(a)
199199

200200

Lib/test/exception_hierarchy.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ BaseException
3939
+-- ReferenceError
4040
+-- RuntimeError
4141
| +-- NotImplementedError
42+
| +-- RecursionError
4243
+-- SyntaxError
4344
| +-- IndentationError
4445
| +-- TabError

Lib/test/list_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_repr(self):
5656
l0 = []
5757
for i in range(sys.getrecursionlimit() + 100):
5858
l0 = [l0]
59-
self.assertRaises(RuntimeError, repr, l0)
59+
self.assertRaises(RecursionError, repr, l0)
6060

6161
def test_print(self):
6262
d = self.type2test(range(200))

Lib/test/test_class.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,10 +500,10 @@ class A:
500500

501501
try:
502502
a() # This should not segfault
503-
except RuntimeError:
503+
except RecursionError:
504504
pass
505505
else:
506-
self.fail("Failed to raise RuntimeError")
506+
self.fail("Failed to raise RecursionError")
507507

508508
def testForExceptionsRaisedInInstanceGetattr2(self):
509509
# Tests for exceptions raised in instance_getattr2().

0 commit comments

Comments
 (0)