[Python-checkins] cpython: Issue #29306: Fix usage of Py_EnterRecursiveCall()

victor.stinner python-checkins at python.org
Wed Feb 8 06:25:16 EST 2017


https://hg.python.org/cpython/rev/88ed9d9eabc1
changeset:   106466:88ed9d9eabc1
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Wed Feb 08 12:06:00 2017 +0100
summary:
  Issue #29306: Fix usage of Py_EnterRecursiveCall()

* *PyCFunction_*Call*() functions now call Py_EnterRecursiveCall().
* PyObject_Call() now calls directly _PyFunction_FastCallDict() and
  PyCFunction_Call() to avoid calling Py_EnterRecursiveCall() twice per
  function call

files:
  Objects/abstract.c     |  112 +++++++++++++++-------------
  Objects/methodobject.c |   61 +++++++++------
  2 files changed, 95 insertions(+), 78 deletions(-)


diff --git a/Objects/abstract.c b/Objects/abstract.c
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2239,21 +2239,32 @@
     assert(PyTuple_Check(args));
     assert(kwargs == NULL || PyDict_Check(kwargs));
 
-    call = callable->ob_type->tp_call;
-    if (call == NULL) {
-        PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
-                     callable->ob_type->tp_name);
-        return NULL;
+    if (PyFunction_Check(callable)) {
+        return _PyFunction_FastCallDict(callable,
+                                        &PyTuple_GET_ITEM(args, 0),
+                                        PyTuple_GET_SIZE(args),
+                                        kwargs);
     }
-
-    if (Py_EnterRecursiveCall(" while calling a Python object"))
-        return NULL;
-
-    result = (*call)(callable, args, kwargs);
-
-    Py_LeaveRecursiveCall();
-
-    return _Py_CheckFunctionResult(callable, result, NULL);
+    else if (PyCFunction_Check(callable)) {
+        return PyCFunction_Call(callable, args, kwargs);
+    }
+    else {
+        call = callable->ob_type->tp_call;
+        if (call == NULL) {
+            PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
+                         callable->ob_type->tp_name);
+            return NULL;
+        }
+
+        if (Py_EnterRecursiveCall(" while calling a Python object"))
+            return NULL;
+
+        result = (*call)(callable, args, kwargs);
+
+        Py_LeaveRecursiveCall();
+
+        return _Py_CheckFunctionResult(callable, result, NULL);
+    }
 }
 
 /* Issue #29234: Inlining _PyStack_AsTuple() into callers increases their
@@ -2305,9 +2316,6 @@
 _PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs,
                        PyObject *kwargs)
 {
-    ternaryfunc call;
-    PyObject *result = NULL;
-
     /* _PyObject_FastCallDict() must not be called with an exception set,
        because it can clear it (directly or indirectly) and so the
        caller loses its exception */
@@ -2318,42 +2326,41 @@
     assert(nargs == 0 || args != NULL);
     assert(kwargs == NULL || PyDict_Check(kwargs));
 
-    if (Py_EnterRecursiveCall(" while calling a Python object")) {
-        return NULL;
-    }
-
     if (PyFunction_Check(callable)) {
-        result = _PyFunction_FastCallDict(callable, args, nargs, kwargs);
+        return _PyFunction_FastCallDict(callable, args, nargs, kwargs);
     }
     else if (PyCFunction_Check(callable)) {
-        result = _PyCFunction_FastCallDict(callable, args, nargs, kwargs);
+        return _PyCFunction_FastCallDict(callable, args, nargs, kwargs);
     }
     else {
-        PyObject *tuple;
+        PyObject *argstuple, *result;
+        ternaryfunc call;
 
         /* Slow-path: build a temporary tuple */
         call = callable->ob_type->tp_call;
         if (call == NULL) {
             PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
                          callable->ob_type->tp_name);
-            goto exit;
+            return NULL;
         }
 
-        tuple = _PyStack_AsTuple(args, nargs);
-        if (tuple == NULL) {
-            goto exit;
+        argstuple = _PyStack_AsTuple(args, nargs);
+        if (argstuple == NULL) {
+            return NULL;
         }
 
-        result = (*call)(callable, tuple, kwargs);
-        Py_DECREF(tuple);
-
+        if (Py_EnterRecursiveCall(" while calling a Python object")) {
+            return NULL;
+        }
+
+        result = (*call)(callable, argstuple, kwargs);
+
+        Py_LeaveRecursiveCall();
+
+        Py_DECREF(argstuple);
         result = _Py_CheckFunctionResult(callable, result, NULL);
+        return result;
     }
-
-exit:
-    Py_LeaveRecursiveCall();
-
-    return result;
 }
 
 /* Positional arguments are obj followed by args:
@@ -2506,49 +2513,48 @@
            temporary dictionary for keyword arguments (if any) */
 
         ternaryfunc call;
-        PyObject *argtuple;
+        PyObject *argstuple;
         PyObject *kwdict, *result;
         Py_ssize_t nkwargs;
 
-        result = NULL;
         nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
         assert((nargs == 0 && nkwargs == 0) || stack != NULL);
 
-        if (Py_EnterRecursiveCall(" while calling a Python object")) {
-            return NULL;
-        }
-
         call = callable->ob_type->tp_call;
         if (call == NULL) {
             PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
                          callable->ob_type->tp_name);
-            goto exit;
+            return NULL;
         }
 
-        argtuple = _PyStack_AsTuple(stack, nargs);
-        if (argtuple == NULL) {
-            goto exit;
+        argstuple = _PyStack_AsTuple(stack, nargs);
+        if (argstuple == NULL) {
+            return NULL;
         }
 
         if (nkwargs > 0) {
             kwdict = _PyStack_AsDict(stack + nargs, kwnames);
             if (kwdict == NULL) {
-                Py_DECREF(argtuple);
-                goto exit;
+                Py_DECREF(argstuple);
+                return NULL;
             }
         }
         else {
             kwdict = NULL;
         }
 
-        result = (*call)(callable, argtuple, kwdict);
-        Py_DECREF(argtuple);
+        if (Py_EnterRecursiveCall(" while calling a Python object")) {
+            return NULL;
+        }
+
+        result = (*call)(callable, argstuple, kwdict);
+
+        Py_LeaveRecursiveCall();
+
+        Py_DECREF(argstuple);
         Py_XDECREF(kwdict);
 
         result = _Py_CheckFunctionResult(callable, result, NULL);
-
-    exit:
-        Py_LeaveRecursiveCall();
         return result;
     }
 }
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -90,11 +90,6 @@
 _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, PyObject **args,
                              Py_ssize_t nargs, PyObject *kwargs)
 {
-    PyCFunction meth;
-    PyObject *result;
-    int flags;
-    PyObject *argstuple;
-
     /* _PyMethodDef_RawFastCallDict() must not be called with an exception set,
        because it can clear it (directly or indirectly) and so the
        caller loses its exception */
@@ -105,18 +100,23 @@
     assert(nargs == 0 || args != NULL);
     assert(kwargs == NULL || PyDict_Check(kwargs));
 
-    meth = method->ml_meth;
-    flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
+    PyCFunction meth = method->ml_meth;
+    int flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
+    PyObject *result = NULL;
+
+    if (Py_EnterRecursiveCall(" while calling a Python object")) {
+        return NULL;
+    }
 
     switch (flags)
     {
     case METH_NOARGS:
-         if (nargs != 0) {
+        if (nargs != 0) {
             PyErr_Format(PyExc_TypeError,
                 "%.200s() takes no arguments (%zd given)",
                 method->ml_name, nargs);
-            return NULL;
-         }
+            goto exit;
+        }
 
         if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) {
             goto no_keyword_error;
@@ -130,7 +130,7 @@
             PyErr_Format(PyExc_TypeError,
                 "%.200s() takes exactly one argument (%zd given)",
                 method->ml_name, nargs);
-            return NULL;
+            goto exit;
         }
 
         if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) {
@@ -148,10 +148,11 @@
         /* fall through next case */
 
     case METH_VARARGS | METH_KEYWORDS:
+    {
         /* Slow-path: create a temporary tuple for positional arguments */
-        argstuple = _PyStack_AsTuple(args, nargs);
+        PyObject *argstuple = _PyStack_AsTuple(args, nargs);
         if (argstuple == NULL) {
-            return NULL;
+            goto exit;
         }
 
         if (flags & METH_KEYWORDS) {
@@ -162,6 +163,7 @@
         }
         Py_DECREF(argstuple);
         break;
+    }
 
     case METH_FASTCALL:
     {
@@ -170,7 +172,7 @@
         _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth;
 
         if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) {
-            return NULL;
+            goto exit;
         }
 
         result = (*fastmeth) (self, stack, nargs, kwnames);
@@ -185,17 +187,19 @@
         PyErr_SetString(PyExc_SystemError,
                         "Bad call flags in _PyMethodDef_RawFastCallDict. "
                         "METH_OLDARGS is no longer supported!");
-        return NULL;
+        goto exit;
     }
 
-    return result;
+    goto exit;
 
 no_keyword_error:
     PyErr_Format(PyExc_TypeError,
                  "%.200s() takes no keyword arguments",
                  method->ml_name, nargs);
 
-    return NULL;
+exit:
+    Py_LeaveRecursiveCall();
+    return result;
 }
 
 PyObject *
@@ -232,7 +236,11 @@
     PyCFunction meth = method->ml_meth;
     int flags = method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
     Py_ssize_t nkwargs = kwnames == NULL ? 0 : PyTuple_Size(kwnames);
-    PyObject *result;
+    PyObject *result = NULL;
+
+    if (Py_EnterRecursiveCall(" while calling a Python object")) {
+        return NULL;
+    }
 
     switch (flags)
     {
@@ -241,7 +249,7 @@
             PyErr_Format(PyExc_TypeError,
                 "%.200s() takes no arguments (%zd given)",
                 method->ml_name, nargs);
-            return NULL;
+            goto exit;
         }
 
         if (nkwargs) {
@@ -256,7 +264,7 @@
             PyErr_Format(PyExc_TypeError,
                 "%.200s() takes exactly one argument (%zd given)",
                 method->ml_name, nargs);
-            return NULL;
+            goto exit;
         }
 
         if (nkwargs) {
@@ -284,7 +292,7 @@
 
         argtuple = _PyStack_AsTuple(args, nargs);
         if (argtuple == NULL) {
-            return NULL;
+            goto exit;
         }
 
         if (flags & METH_KEYWORDS) {
@@ -294,7 +302,7 @@
                 kwdict = _PyStack_AsDict(args + nargs, kwnames);
                 if (kwdict == NULL) {
                     Py_DECREF(argtuple);
-                    return NULL;
+                    goto exit;
                 }
             }
             else {
@@ -315,16 +323,19 @@
         PyErr_SetString(PyExc_SystemError,
                         "Bad call flags in _PyCFunction_FastCallKeywords. "
                         "METH_OLDARGS is no longer supported!");
-        return NULL;
+        goto exit;
     }
 
-    return result;
+    goto exit;
 
 no_keyword_error:
     PyErr_Format(PyExc_TypeError,
                  "%.200s() takes no keyword arguments",
                  method->ml_name);
-    return NULL;
+
+exit:
+    Py_LeaveRecursiveCall();
+    return result;
 }
 
 PyObject *

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list