]> granicus.if.org Git - python/commitdiff
Fix for an obscure bug introduced by revs 46806 and 46808, with a test.
authorArmin Rigo <arigo@tunes.org>
Wed, 21 Jun 2006 21:58:50 +0000 (21:58 +0000)
committerArmin Rigo <arigo@tunes.org>
Wed, 21 Jun 2006 21:58:50 +0000 (21:58 +0000)
The problem of checking too eagerly for recursive calls is the
following: if a RuntimeError is caused by recursion, and if code needs
to normalize it immediately (as in the 2nd test), then
PyErr_NormalizeException() needs a call to the RuntimeError class to
instantiate it, and this hits the recursion limit again...  causing
PyErr_NormalizeException() to never finish.

Moved this particular recursion check to slot_tp_call(), which is not
involved in instantiating built-in exceptions.

Backport candidate.

Lib/test/test_exceptions.py
Objects/abstract.c
Objects/typeobject.c

index 45f518899055a3696a107a2f95696927a2a0d0e8..ec8895c7471867f167386fe03aba8ee9f190e828 100644 (file)
@@ -305,6 +305,18 @@ class ExceptionTests(unittest.TestCase):
         x = DerivedException(fancy_arg=42)
         self.assertEquals(x.fancy_arg, 42)
 
+    def testInfiniteRecursion(self):
+        def f():
+            return f()
+        self.assertRaises(RuntimeError, f)
+
+        def g():
+            try:
+                return g()
+            except ValueError:
+                return -1
+        self.assertRaises(RuntimeError, g)
+
 def test_main():
     run_unittest(ExceptionTests)
 
index d16660bde1af5e6777b6674d9525583dbc7d8671..638e41787d07acd39e8cd365c4ef4a9dc6c7d634 100644 (file)
@@ -1796,17 +1796,7 @@ PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw)
         ternaryfunc call;
 
        if ((call = func->ob_type->tp_call) != NULL) {
-               PyObject *result = NULL;
-               /* slot_tp_call() will be called and ends up calling
-                  PyObject_Call() if the object returned for __call__ has
-                  __call__ itself defined upon it.  This can be an infinite
-                  recursion if you set __call__ in a class to an instance of
-                  it. */
-               if (Py_EnterRecursiveCall(" in __call__")) {
-                   return NULL;
-               }
-               result = (*call)(func, arg, kw);
-               Py_LeaveRecursiveCall();
+               PyObject *result = (*call)(func, arg, kw);
                if (result == NULL && !PyErr_Occurred())
                        PyErr_SetString(
                                PyExc_SystemError,
index 439676f751c4c5b239277a9464bda1da63afe153..760ef95858d2b5469f1f9a26445633f920ccb6dc 100644 (file)
@@ -4590,7 +4590,16 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
 
        if (meth == NULL)
                return NULL;
+
+       /* PyObject_Call() will end up calling slot_tp_call() again if
+          the object returned for __call__ has __call__ itself defined
+          upon it.  This can be an infinite recursion if you set
+          __call__ in a class to an instance of it. */
+       if (Py_EnterRecursiveCall(" in __call__"))
+               return NULL;
        res = PyObject_Call(meth, args, kwds);
+       Py_LeaveRecursiveCall();
+
        Py_DECREF(meth);
        return res;
 }