[Python-checkins] gh-106084: Remove _PyObject_CallMethod() function (#106159)

vstinner webhook-mailer at python.org
Tue Jun 27 19:34:41 EDT 2023


https://github.com/python/cpython/commit/84caa3324aaefb900895de2f946607cfdbe1be70
commit: 84caa3324aaefb900895de2f946607cfdbe1be70
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2023-06-28T01:34:37+02:00
summary:

gh-106084: Remove _PyObject_CallMethod() function (#106159)

Remove the following private functions from the public C API:

* _Py_CheckFunctionResult()
* _PyObject_CallMethod()
* _PyObject_CallMethodId()
* _PyObject_CallMethodIdNoArgs()
* _PyObject_CallMethodIdObjArgs()
* _PyObject_CallMethodIdOneArg()
* _PyObject_MakeTpCall()
* _PyObject_VectorcallMethodId()
* _PyStack_AsDict()

Move these functions to the internal C API (pycore_call.h).

No longer export the following functions:

* _PyObject_Call()
* _PyObject_CallMethod()
* _PyObject_CallMethodId()
* _PyObject_CallMethodIdObjArgs()
* _PyObject_Call_Prepend()
* _PyObject_FastCallDictTstate()
* _PyStack_AsDict()

The following functions are still exported for stdlib shared
extensions:

* _Py_CheckFunctionResult()
* _PyObject_MakeTpCall()

Mark the following internal functions as extern:

* _PyStack_UnpackDict()
* _PyStack_UnpackDict_Free()
* _PyStack_UnpackDict_FreeNoDecRef()

files:
M Include/cpython/abstract.h
M Include/internal/pycore_call.h
M Modules/_bisectmodule.c
M Modules/_io/iobase.c
M Modules/_io/textio.c
M Modules/arraymodule.c
M Objects/descrobject.c
M Objects/methodobject.c
M Python/pylifecycle.c

diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h
index 5eb22ff61ecdd..2e76ff49a81dd 100644
--- a/Include/cpython/abstract.h
+++ b/Include/cpython/abstract.h
@@ -4,20 +4,6 @@
 
 /* === Object Protocol ================================================== */
 
-/* Convert keyword arguments from the FASTCALL (stack: C array, kwnames: tuple)
-   format to a Python dictionary ("kwargs" dict).
-
-   The type of kwnames keys is not checked. The final function getting
-   arguments is responsible to check if all keys are strings, for example using
-   PyArg_ParseTupleAndKeywords() or PyArg_ValidateKeywordArguments().
-
-   Duplicate keys are merged using the last value. If duplicate keys must raise
-   an exception, the caller is responsible to implement an explicit keys on
-   kwnames. */
-PyAPI_FUNC(PyObject *) _PyStack_AsDict(
-    PyObject *const *values,
-    PyObject *kwnames);
-
 /* Suggested size (number of positional arguments) for arrays of PyObject*
    allocated on a C stack to avoid allocating memory on the heap memory. Such
    array is used to pass positional arguments to call functions of the
@@ -29,32 +15,17 @@ PyAPI_FUNC(PyObject *) _PyStack_AsDict(
    40 bytes on the stack. */
 #define _PY_FASTCALL_SMALL_STACK 5
 
-PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(
-    PyThreadState *tstate,
-    PyObject *callable,
-    PyObject *result,
-    const char *where);
-
 /* === Vectorcall protocol (PEP 590) ============================= */
 
-/* Call callable using tp_call. Arguments are like PyObject_Vectorcall()
-   or PyObject_FastCallDict() (both forms are supported),
-   except that nargs is plainly the number of arguments without flags. */
-PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall(
-    PyThreadState *tstate,
-    PyObject *callable,
-    PyObject *const *args, Py_ssize_t nargs,
-    PyObject *keywords);
-
 // PyVectorcall_NARGS() is exported as a function for the stable ABI.
 // Here (when we are not using the stable ABI), the name is overridden to
 // call a static inline function for best performance.
-#define PyVectorcall_NARGS(n) _PyVectorcall_NARGS(n)
 static inline Py_ssize_t
 _PyVectorcall_NARGS(size_t n)
 {
     return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
 }
+#define PyVectorcall_NARGS(n) _PyVectorcall_NARGS(n)
 
 PyAPI_FUNC(vectorcallfunc) PyVectorcall_Function(PyObject *callable);
 
@@ -90,49 +61,6 @@ PyObject_CallMethodOneArg(PyObject *self, PyObject *name, PyObject *arg)
     return PyObject_VectorcallMethod(name, args, nargsf, _Py_NULL);
 }
 
-PyAPI_FUNC(PyObject *) _PyObject_CallMethod(PyObject *obj,
-                                            PyObject *name,
-                                            const char *format, ...);
-
-/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
-   as the method name. */
-PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,
-                                              _Py_Identifier *name,
-                                              const char *format, ...);
-
-PyAPI_FUNC(PyObject *) _PyObject_CallMethodIdObjArgs(
-    PyObject *obj,
-    _Py_Identifier *name,
-    ...);
-
-static inline PyObject *
-_PyObject_VectorcallMethodId(
-    _Py_Identifier *name, PyObject *const *args,
-    size_t nargsf, PyObject *kwnames)
-{
-    PyObject *oname = _PyUnicode_FromId(name); /* borrowed */
-    if (!oname) {
-        return _Py_NULL;
-    }
-    return PyObject_VectorcallMethod(oname, args, nargsf, kwnames);
-}
-
-static inline PyObject *
-_PyObject_CallMethodIdNoArgs(PyObject *self, _Py_Identifier *name)
-{
-    size_t nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
-    return _PyObject_VectorcallMethodId(name, &self, nargsf, _Py_NULL);
-}
-
-static inline PyObject *
-_PyObject_CallMethodIdOneArg(PyObject *self, _Py_Identifier *name, PyObject *arg)
-{
-    PyObject *args[2] = {self, arg};
-    size_t nargsf = 2 | PY_VECTORCALL_ARGUMENTS_OFFSET;
-    assert(arg != NULL);
-    return _PyObject_VectorcallMethodId(name, args, nargsf, _Py_NULL);
-}
-
 /* Guess the size of object 'o' using len(o) or o.__length_hint__().
    If neither of those return a non-negative value, then return the default
    value.  If one of the calls fails, this function returns -1. */
diff --git a/Include/internal/pycore_call.h b/Include/internal/pycore_call.h
index 5d9342b562b00..3caf504a96aa3 100644
--- a/Include/internal/pycore_call.h
+++ b/Include/internal/pycore_call.h
@@ -10,29 +10,112 @@ extern "C" {
 
 #include "pycore_pystate.h"       // _PyThreadState_GET()
 
-PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend(
+// Export for shared stdlib extensions like the math extension,
+// function used via inlined _PyObject_VectorcallTstate() function.
+PyAPI_FUNC(PyObject*) _Py_CheckFunctionResult(
+    PyThreadState *tstate,
+    PyObject *callable,
+    PyObject *result,
+    const char *where);
+
+/* Convert keyword arguments from the FASTCALL (stack: C array, kwnames: tuple)
+   format to a Python dictionary ("kwargs" dict).
+
+   The type of kwnames keys is not checked. The final function getting
+   arguments is responsible to check if all keys are strings, for example using
+   PyArg_ParseTupleAndKeywords() or PyArg_ValidateKeywordArguments().
+
+   Duplicate keys are merged using the last value. If duplicate keys must raise
+   an exception, the caller is responsible to implement an explicit keys on
+   kwnames. */
+extern PyObject* _PyStack_AsDict(PyObject *const *values, PyObject *kwnames);
+
+extern PyObject* _PyObject_Call_Prepend(
     PyThreadState *tstate,
     PyObject *callable,
     PyObject *obj,
     PyObject *args,
     PyObject *kwargs);
 
-PyAPI_FUNC(PyObject *) _PyObject_FastCallDictTstate(
+extern PyObject* _PyObject_FastCallDictTstate(
     PyThreadState *tstate,
     PyObject *callable,
     PyObject *const *args,
     size_t nargsf,
     PyObject *kwargs);
 
-PyAPI_FUNC(PyObject *) _PyObject_Call(
+extern PyObject* _PyObject_Call(
     PyThreadState *tstate,
     PyObject *callable,
     PyObject *args,
     PyObject *kwargs);
 
 extern PyObject * _PyObject_CallMethodFormat(
-        PyThreadState *tstate, PyObject *callable, const char *format, ...);
+    PyThreadState *tstate,
+    PyObject *callable,
+    const char *format,
+    ...);
 
+// Export for shared stdlib extensions like the array extension
+PyAPI_FUNC(PyObject*) _PyObject_CallMethod(
+    PyObject *obj,
+    PyObject *name,
+    const char *format, ...);
+
+/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
+   as the method name. */
+extern PyObject* _PyObject_CallMethodId(
+    PyObject *obj,
+    _Py_Identifier *name,
+    const char *format, ...);
+
+extern PyObject* _PyObject_CallMethodIdObjArgs(
+    PyObject *obj,
+    _Py_Identifier *name,
+    ...);
+
+static inline PyObject *
+_PyObject_VectorcallMethodId(
+    _Py_Identifier *name, PyObject *const *args,
+    size_t nargsf, PyObject *kwnames)
+{
+    PyObject *oname = _PyUnicode_FromId(name); /* borrowed */
+    if (!oname) {
+        return _Py_NULL;
+    }
+    return PyObject_VectorcallMethod(oname, args, nargsf, kwnames);
+}
+
+static inline PyObject *
+_PyObject_CallMethodIdNoArgs(PyObject *self, _Py_Identifier *name)
+{
+    size_t nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
+    return _PyObject_VectorcallMethodId(name, &self, nargsf, _Py_NULL);
+}
+
+static inline PyObject *
+_PyObject_CallMethodIdOneArg(PyObject *self, _Py_Identifier *name, PyObject *arg)
+{
+    PyObject *args[2] = {self, arg};
+    size_t nargsf = 2 | PY_VECTORCALL_ARGUMENTS_OFFSET;
+    assert(arg != NULL);
+    return _PyObject_VectorcallMethodId(name, args, nargsf, _Py_NULL);
+}
+
+
+/* === Vectorcall protocol (PEP 590) ============================= */
+
+// Call callable using tp_call. Arguments are like PyObject_Vectorcall()
+// or PyObject_FastCallDict() (both forms are supported),
+// except that nargs is plainly the number of arguments without flags.
+//
+// Export for shared stdlib extensions like the math extension,
+// function used via inlined _PyObject_VectorcallTstate() function.
+PyAPI_FUNC(PyObject*) _PyObject_MakeTpCall(
+    PyThreadState *tstate,
+    PyObject *callable,
+    PyObject *const *args, Py_ssize_t nargs,
+    PyObject *keywords);
 
 // Static inline variant of public PyVectorcall_Function().
 static inline vectorcallfunc
@@ -110,22 +193,26 @@ _PyObject_CallNoArgs(PyObject *func) {
 
 
 static inline PyObject *
-_PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const *args, Py_ssize_t nargs)
+_PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func,
+                         PyObject *const *args, Py_ssize_t nargs)
 {
     EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_API, func);
     return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL);
 }
 
-PyObject *const *
+extern PyObject *const *
 _PyStack_UnpackDict(PyThreadState *tstate,
     PyObject *const *args, Py_ssize_t nargs,
     PyObject *kwargs, PyObject **p_kwnames);
 
-void
-_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
+extern void _PyStack_UnpackDict_Free(
+    PyObject *const *stack,
+    Py_ssize_t nargs,
     PyObject *kwnames);
 
-void _PyStack_UnpackDict_FreeNoDecRef(PyObject *const *stack, PyObject *kwnames);
+extern void _PyStack_UnpackDict_FreeNoDecRef(
+    PyObject *const *stack,
+    PyObject *kwnames);
 
 #ifdef __cplusplus
 }
diff --git a/Modules/_bisectmodule.c b/Modules/_bisectmodule.c
index 0773bbd191931..60f4dc69dc05d 100644
--- a/Modules/_bisectmodule.c
+++ b/Modules/_bisectmodule.c
@@ -3,8 +3,13 @@
 Converted to C by Dmitry Vasiliev (dima at hlabs.spb.ru).
 */
 
+#ifndef Py_BUILD_CORE_BUILTIN
+#  define Py_BUILD_CORE_MODULE 1
+#endif
+
 #define PY_SSIZE_T_CLEAN
 #include "Python.h"
+#include "pycore_call.h"          // _PyObject_CallMethod()
 
 /*[clinic input]
 module _bisect
diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c
index f98e75ce2d1ed..5cd679c68b128 100644
--- a/Modules/_io/iobase.c
+++ b/Modules/_io/iobase.c
@@ -10,8 +10,9 @@
 
 #define PY_SSIZE_T_CLEAN
 #include "Python.h"
+#include "pycore_call.h"          // _PyObject_CallMethod()
 #include "pycore_long.h"          // _PyLong_GetOne()
-#include "pycore_object.h"
+#include "pycore_object.h"        // _PyType_HasFeature()
 #include <stddef.h>               // offsetof()
 #include "_iomodule.h"
 
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 0ea7458df559b..e6b1306c2f3f0 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -8,10 +8,11 @@
 
 #define PY_SSIZE_T_CLEAN
 #include "Python.h"
+#include "pycore_call.h"          // _PyObject_CallMethod()
 #include "pycore_interp.h"        // PyInterpreterState.fs_codec
 #include "pycore_long.h"          // _PyLong_GetZero()
 #include "pycore_fileutils.h"     // _Py_GetLocaleEncoding()
-#include "pycore_object.h"
+#include "pycore_object.h"        // _PyObject_GC_UNTRACK()
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
 #include "structmember.h"         // PyMemberDef
 #include "_iomodule.h"
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index 8132689c66c0a..15f7766a8dd28 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -9,6 +9,7 @@
 
 #define PY_SSIZE_T_CLEAN
 #include "Python.h"
+#include "pycore_call.h"          // _PyObject_CallMethod()
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
 #include "pycore_bytesobject.h"   // _PyBytes_Repeat
 #include "structmember.h"         // PyMemberDef
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index 89bac99bb9d63..a81490285eed4 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -2,6 +2,7 @@
 
 #include "Python.h"
 #include "pycore_abstract.h"      // _PyObject_RealIsSubclass()
+#include "pycore_call.h"          // _PyStack_AsDict()
 #include "pycore_ceval.h"         // _Py_EnterRecursiveCallTstate()
 #include "pycore_object.h"        // _PyObject_GC_UNTRACK()
 #include "pycore_pystate.h"       // _PyThreadState_GET()
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 51752dec3dd08..fe081992d51fd 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -2,6 +2,7 @@
 /* Method object implementation */
 
 #include "Python.h"
+#include "pycore_call.h"          // _Py_CheckFunctionResult()
 #include "pycore_ceval.h"         // _Py_EnterRecursiveCallTstate()
 #include "pycore_object.h"
 #include "pycore_pyerrors.h"
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 6117f3a19a550..1df35ef427867 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -2,10 +2,11 @@
 
 #include "Python.h"
 
+#include "pycore_call.h"          // _PyObject_CallMethod()
 #include "pycore_ceval.h"         // _PyEval_FiniGIL()
 #include "pycore_context.h"       // _PyContext_Init()
-#include "pycore_exceptions.h"    // _PyExc_InitTypes()
 #include "pycore_dict.h"          // _PyDict_Fini()
+#include "pycore_exceptions.h"    // _PyExc_InitTypes()
 #include "pycore_fileutils.h"     // _Py_ResetForceASCII()
 #include "pycore_floatobject.h"   // _PyFloat_InitTypes()
 #include "pycore_genobject.h"     // _PyAsyncGen_Fini()



More information about the Python-checkins mailing list