]> granicus.if.org Git - python/commitdiff
Issue #19235: Add new RecursionError exception. Patch by Georg Brandl.
authorYury Selivanov <yselivanov@sprymix.com>
Fri, 3 Jul 2015 05:04:23 +0000 (01:04 -0400)
committerYury Selivanov <yselivanov@sprymix.com>
Fri, 3 Jul 2015 05:04:23 +0000 (01:04 -0400)
31 files changed:
Doc/c-api/exceptions.rst
Doc/library/exceptions.rst
Doc/library/pickle.rst
Doc/whatsnew/3.5.rst
Include/ceval.h
Include/pyerrors.h
Lib/ctypes/test/test_as_parameter.py
Lib/test/exception_hierarchy.txt
Lib/test/list_tests.py
Lib/test/test_class.py
Lib/test/test_compile.py
Lib/test/test_copy.py
Lib/test/test_descr.py
Lib/test/test_dictviews.py
Lib/test/test_exceptions.py
Lib/test/test_isinstance.py
Lib/test/test_json/test_recursion.py
Lib/test/test_pickle.py
Lib/test/test_richcmp.py
Lib/test/test_runpy.py
Lib/test/test_sys.py
Lib/test/test_threading.py
Misc/NEWS
Modules/_pickle.c
Modules/_sre.c
Objects/exceptions.c
Objects/typeobject.c
Python/ceval.c
Python/errors.c
Python/symtable.c
Tools/scripts/find_recursionlimit.py

index 6ae723b8d9c0ccbb1a4985dd2aeec8ed10656d15..3fd69ba80fa3aacfba151312dcf819d2d7972878 100644 (file)
@@ -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
index 1a9d0299d5fdbf36f81f81b9e8e1c97af96fc5ff..0a422b238d151dce5994be411489012c0d5a1309 100644 (file)
@@ -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
index 4ce4d345af4590d13da447a6a1866ccbf2215a76..e34f2b39936c993763a9d3b7558a04fb5484acc5 100644 (file)
@@ -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`.
 
index bfefb2fe0d3280b0e2c3e007723c4046a28d9f58..b73c80df3d4a927bf0ba318b7317e76df0909458 100644 (file)
@@ -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:
 
index 2472ae6492ffb424800edcf0004053c1ab6486a2..eb1ee43497cf02a5276a1fc36c9f149f665c8c59 100644 (file)
@@ -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
index a0198659a1aa292f82ce283edc693853aeb14dd4..35aedb7349207003ab899a55ef742a4492e54c4f 100644 (file)
@@ -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;
index 948b4632154d0dcd12ca097a9e4d1f1377d26d8b..2a3484bec01b99ac614ddb5e199ee4b9364b200a 100644 (file)
@@ -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)
 
 
index 6632826afaaf977ca7081e01b1f4989f2618a145..05137654de62610b67ceed1bbbb88a1f7a5474d8 100644 (file)
@@ -39,6 +39,7 @@ BaseException
       +-- ReferenceError
       +-- RuntimeError
       |    +-- NotImplementedError
+      |    +-- RecursionError
       +-- SyntaxError
       |    +-- IndentationError
       |         +-- TabError
index 906933796d23551afa3193eafe831962478c978e..1adfc75b77a0e90091bb49db065f0d0ec4b8a229 100644 (file)
@@ -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))
index 6036e3603224cf9a79fb73b21c60def233abc87d..4d554a397b4a51b307d60aee4baf5aa4a24ff979 100644 (file)
@@ -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().
index f5e45766728389f82545c61ed22e12b30a40a0e3..db821be031c9cfc8c5179a27cfeacb6f06d85ce4 100644 (file)
@@ -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", "()")
index 4c197467e9c52712f5bd717209cf447c815e96aa..b9eadddbf2682f2ed70bb908d9c81e981cb65d70 100644 (file)
@@ -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)
index 80a526d78ccce0f455168a9c2cba908ab4657c3b..0ef1a31e0f449742651ce3db3c98110c2502f3a7 100644 (file)
@@ -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):
index 280353a3a2b1fd27fd7cde1cb9d113a4812e4ab9..8d33801ca094b6ae0fbf806218392e06003392a2 100644 (file)
@@ -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__":
index 80d4f1aeb5a0fec7836338cb31294a7fb04e4dfa..3bfb582cb6947e269e7d6d796e54a915aba6b3f9 100644 (file)
@@ -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):
index e087ac0bad503d2c66c0c3c9645b2659f7366537..e63d59b346a2acda5935459014dc6f1b382f5e42 100644 (file)
@@ -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,)
index 1a76254d01b6acffe49bc557a7e06a4727dd7d52..877dc448b14c1539c7d668ab7e39fccf04e1bb94 100644 (file)
@@ -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)
 
 
index a9853c1dab656f89cb37773e4820dd539f73b7f2..ba92de94c5911d386b85d3de8125266ade84c519 100644 (file)
@@ -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),
index 94185a4aabdfbe0fdec6ff2473b837bc0daaa2fe..1582caad9742b1f06e8d86f5404730133715bdf6 100644 (file)
@@ -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))
index 5a799bdcf77f7518ccfac7a695f055d671871c4a..4bae949d21f50754c89cf6acafc9eef4a41fcc58 100644 (file)
@@ -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:
index 494a53bf24de2244a4c82621c2d6c7d8ac310548..83549bc5376b8000a9141b7ddf733a1495cb1c86 100644 (file)
@@ -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)
index ddafba24b13e1a53f62932c5ba9a0ade8d05840a..3b11bf65080b6709def9819d229e0dd5def92ef2 100644 (file)
@@ -945,7 +945,7 @@ class ThreadingExceptionTests(BaseTestCase):
             def outer():
                 try:
                     recurse()
-                except RuntimeError:
+                except RecursionError:
                     pass
 
             w = threading.Thread(target=outer)
index a1563df5e26375652eea998c61e1e3a70d566bbd..75e717f8115bd0c82290746ef845e2ca329d7a95 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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
 -------
 
index 44f840d19980538034ed7c2003b94c270217275d..3ad9a97641073edc213229eadbb2c93bdee5fb38 100644 (file)
@@ -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:
index 4016a4533e0bccf6a7658bbccad20d8a5c3a958f..4f47393aed93f7cecd5abf3405a6e55b870ceffd 100644 (file)
@@ -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;
index d494995ddefd1ba20372dc815ec67ddbda421c1b..a27599781071c4b3fb94085ba5c11efef9bf557d 100644 (file)
@@ -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);
         }
     }
index 82c8710824d9f52698e7aeb3753043dfff143384..1beed724587e0c9af75cab10d4bfcd10a493f0f8 100644 (file)
@@ -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.
index 12df0fe3fc576bc10bdb73045a39bc705a861d66..e68ae33bfd712f82074a3a34d4d49b9962df027e 100644 (file)
@@ -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;
index 1172c590474d5e33022fd7c4c3acc762f19ab4ea..aed2bdc12d6b1734b58c8e8f86cc96ccae3b84c2 100644 (file)
@@ -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);
index 3677e597482b378f9631e7b77b13d20ebee4845d..354b7996786808b87e0de3d454e22ec12247c53d 100644 (file)
@@ -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);
     }
index 11711464214777cdb8a260d2fff835b2b0733128..b2842a62efdbd682ce5f6a4f913d19d9823c32aa 100755 (executable)
@@ -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!")