From bf8e82f976b37856c7d35cdf88a238cb6f57fe65 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 23 Jul 2019 12:39:51 +0200 Subject: [3.8] bpo-36974: separate vectorcall functions for each calling convention (GH-13781) (#14782) --- Include/descrobject.h | 3 - Include/methodobject.h | 5 - Lib/test/test_call.py | 2 + Lib/test/test_gdb.py | 8 +- .../2019-06-11-12-59-38.bpo-36974.bVYmSA.rst | 2 + Objects/call.c | 21 +- Objects/descrobject.c | 246 ++++++++++++++++----- Objects/methodobject.c | 165 +++++++++++++- Python/ceval.c | 16 +- Tools/gdb/libpython.py | 5 +- 10 files changed, 372 insertions(+), 101 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst diff --git a/Include/descrobject.h b/Include/descrobject.h index d711485..ead269d 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -91,9 +91,6 @@ PyAPI_FUNC(PyObject *) PyDescr_NewMember(PyTypeObject *, PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *, struct PyGetSetDef *); #ifndef Py_LIMITED_API - -PyAPI_FUNC(PyObject *) _PyMethodDescr_Vectorcall( - PyObject *descrobj, PyObject *const *args, size_t nargsf, PyObject *kwnames); PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *, struct wrapperbase *, void *); #define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL) diff --git a/Include/methodobject.h b/Include/methodobject.h index e92adde..ba3b887 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -46,11 +46,6 @@ PyAPI_FUNC(PyObject *) _PyCFunction_FastCallDict(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs); - -PyAPI_FUNC(PyObject *) _PyCFunction_Vectorcall(PyObject *func, - PyObject *const *stack, - size_t nargsf, - PyObject *kwnames); #endif struct PyMethodDef { diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index b252ca1..0bff7de 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -586,6 +586,8 @@ class TestPEP590(unittest.TestCase): return super().__call__(*args) calls += [ + (dict.update, ({},), {"key":True}, None), + ({}.update, ({},), {"key":True}, None), (MethodDescriptorHeap(), (0,), {}, True), (MethodDescriptorOverridden(), (0,), {}, 'new'), (MethodDescriptorSuper(), (0,), {}, True), diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 1c5e18b..e07d327 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -850,10 +850,10 @@ id(42) # called, so test a variety of calling conventions. for py_name, py_args, c_name, expected_frame_number in ( ('gmtime', '', 'time_gmtime', 1), # METH_VARARGS - ('len', '[]', 'builtin_len', 2), # METH_O - ('locals', '', 'builtin_locals', 2), # METH_NOARGS - ('iter', '[]', 'builtin_iter', 2), # METH_FASTCALL - ('sorted', '[]', 'builtin_sorted', 2), # METH_FASTCALL|METH_KEYWORDS + ('len', '[]', 'builtin_len', 1), # METH_O + ('locals', '', 'builtin_locals', 1), # METH_NOARGS + ('iter', '[]', 'builtin_iter', 1), # METH_FASTCALL + ('sorted', '[]', 'builtin_sorted', 1), # METH_FASTCALL|METH_KEYWORDS ): with self.subTest(c_name): cmd = ('from time import gmtime\n' # (not always needed) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst new file mode 100644 index 0000000..6080ef3 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst @@ -0,0 +1,2 @@ +Implemented separate vectorcall functions for every calling convention of +builtin functions and methods. This improves performance for calls. diff --git a/Objects/call.c b/Objects/call.c index 1f41f5c..c663898 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -206,7 +206,7 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs) Py_DECREF(kwnames); } - return result; + return _Py_CheckFunctionResult(callable, result, NULL); } @@ -723,25 +723,6 @@ exit: } -PyObject * -_PyCFunction_Vectorcall(PyObject *func, - PyObject *const *args, size_t nargsf, - PyObject *kwnames) -{ - PyObject *result; - - assert(func != NULL); - assert(PyCFunction_Check(func)); - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - - result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml, - PyCFunction_GET_SELF(func), - args, nargs, kwnames); - result = _Py_CheckFunctionResult(func, result, NULL); - return result; -} - - static PyObject * cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs) { diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 6c95a87..dbb44de 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -226,80 +226,199 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) return -1; } -static PyObject * -methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs) -{ - Py_ssize_t nargs; - PyObject *self, *result; - /* Make sure that the first argument is acceptable as 'self' */ - assert(PyTuple_Check(args)); - nargs = PyTuple_GET_SIZE(args); +/* Vectorcall functions for each of the PyMethodDescr calling conventions. + * + * First, common helpers + */ +static const char * +get_name(PyObject *func) { + assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); + return ((PyMethodDescrObject *)func)->d_method->ml_name; +} + +typedef void (*funcptr)(void); + +static inline int +method_check_args(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + assert(!PyErr_Occurred()); + assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); if (nargs < 1) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' of '%.100s' " + "descriptor '%.200s' of '%.100s' " "object needs an argument", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name); - return NULL; + get_name(func), PyDescr_TYPE(func)->tp_name); + return -1; } - self = PyTuple_GET_ITEM(args, 0); + PyObject *self = args[0]; if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), - (PyObject *)PyDescr_TYPE(descr))) { + (PyObject *)PyDescr_TYPE(func))) + { PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%.100s' objects " + "descriptor '%.200s' for '%.100s' objects " "doesn't apply to a '%.100s' object", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name, - self->ob_type->tp_name); + get_name(func), PyDescr_TYPE(func)->tp_name, + Py_TYPE(self)->tp_name); + return -1; + } + if (kwnames && PyTuple_GET_SIZE(kwnames)) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", get_name(func)); + return -1; + } + return 0; +} + +static inline funcptr +method_enter_call(PyObject *func) +{ + if (Py_EnterRecursiveCall(" while calling a Python object")) { return NULL; } + return (funcptr)((PyMethodDescrObject *)func)->d_method->ml_meth; +} - result = _PyMethodDef_RawFastCallDict(descr->d_method, self, - &_PyTuple_ITEMS(args)[1], nargs - 1, - kwargs); - result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL); +/* Now the actual vectorcall functions */ +static PyObject * +method_vectorcall_VARARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, kwnames)) { + return NULL; + } + PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); + if (argstuple == NULL) { + return NULL; + } + PyCFunction meth = (PyCFunction)method_enter_call(func); + if (meth == NULL) { + Py_DECREF(argstuple); + return NULL; + } + PyObject *result = meth(args[0], argstuple); + Py_DECREF(argstuple); + Py_LeaveRecursiveCall(); return result; } -// same to methoddescr_call(), but use FASTCALL convention. -PyObject * -_PyMethodDescr_Vectorcall(PyObject *descrobj, - PyObject *const *args, size_t nargsf, - PyObject *kwnames) +static PyObject * +method_vectorcall_VARARGS_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - assert(Py_TYPE(descrobj) == &PyMethodDescr_Type); - PyMethodDescrObject *descr = (PyMethodDescrObject *)descrobj; - PyObject *self, *result; + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, NULL)) { + return NULL; + } + PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); + if (argstuple == NULL) { + return NULL; + } + PyObject *result = NULL; + /* Create a temporary dict for keyword arguments */ + PyObject *kwdict = NULL; + if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) > 0) { + kwdict = _PyStack_AsDict(args + nargs, kwnames); + if (kwdict == NULL) { + goto exit; + } + } + PyCFunctionWithKeywords meth = (PyCFunctionWithKeywords) + method_enter_call(func); + if (meth == NULL) { + goto exit; + } + result = meth(args[0], argstuple, kwdict); + Py_LeaveRecursiveCall(); +exit: + Py_DECREF(argstuple); + Py_XDECREF(kwdict); + return result; +} +static PyObject * +method_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - /* Make sure that the first argument is acceptable as 'self' */ - if (nargs < 1) { - PyErr_Format(PyExc_TypeError, - "descriptor '%V' of '%.100s' " - "object needs an argument", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name); + if (method_check_args(func, args, nargs, kwnames)) { return NULL; } - self = args[0]; - if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), - (PyObject *)PyDescr_TYPE(descr))) { + _PyCFunctionFast meth = (_PyCFunctionFast) + method_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(args[0], args+1, nargs-1); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +method_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, NULL)) { + return NULL; + } + _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) + method_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(args[0], args+1, nargs-1, kwnames); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +method_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, kwnames)) { + return NULL; + } + if (nargs != 1) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%.100s' objects " - "doesn't apply to a '%.100s' object", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name, - self->ob_type->tp_name); + "%.200s() takes no arguments (%zd given)", get_name(func), nargs-1); return NULL; } + PyCFunction meth = (PyCFunction)method_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(args[0], NULL); + Py_LeaveRecursiveCall(); + return result; +} - result = _PyMethodDef_RawFastCallKeywords(descr->d_method, self, - args+1, nargs-1, kwnames); - result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL); +static PyObject * +method_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (method_check_args(func, args, nargs, kwnames)) { + return NULL; + } + if (nargs != 2) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + get_name(func), nargs-1); + return NULL; + } + PyCFunction meth = (PyCFunction)method_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(args[0], args[1]); + Py_LeaveRecursiveCall(); return result; } + static PyObject * classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) @@ -552,7 +671,7 @@ PyTypeObject PyMethodDescr_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)methoddescr_call, /* tp_call */ + PyVectorcall_Call, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ @@ -750,13 +869,40 @@ descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name) PyObject * PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method) { + /* Figure out correct vectorcall function to use */ + vectorcallfunc vectorcall; + switch (method->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS)) + { + case METH_VARARGS: + vectorcall = method_vectorcall_VARARGS; + break; + case METH_VARARGS | METH_KEYWORDS: + vectorcall = method_vectorcall_VARARGS_KEYWORDS; + break; + case METH_FASTCALL: + vectorcall = method_vectorcall_FASTCALL; + break; + case METH_FASTCALL | METH_KEYWORDS: + vectorcall = method_vectorcall_FASTCALL_KEYWORDS; + break; + case METH_NOARGS: + vectorcall = method_vectorcall_NOARGS; + break; + case METH_O: + vectorcall = method_vectorcall_O; + break; + default: + PyErr_SetString(PyExc_SystemError, "bad call flags"); + return NULL; + } + PyMethodDescrObject *descr; descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type, type, method->ml_name); if (descr != NULL) { descr->d_method = method; - descr->vectorcall = _PyMethodDescr_Vectorcall; + descr->vectorcall = vectorcall; } return (PyObject *)descr; } diff --git a/Objects/methodobject.c b/Objects/methodobject.c index c3bc018..3494f11 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -19,6 +19,17 @@ static int numfree = 0; /* undefine macro trampoline to PyCFunction_NewEx */ #undef PyCFunction_New +/* Forward declarations */ +static PyObject * cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); + + PyObject * PyCFunction_New(PyMethodDef *ml, PyObject *self) { @@ -28,6 +39,33 @@ PyCFunction_New(PyMethodDef *ml, PyObject *self) PyObject * PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) { + /* Figure out correct vectorcall function to use */ + vectorcallfunc vectorcall; + switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS)) + { + case METH_VARARGS: + case METH_VARARGS | METH_KEYWORDS: + /* For METH_VARARGS functions, it's more efficient to use tp_call + * instead of vectorcall. */ + vectorcall = NULL; + break; + case METH_FASTCALL: + vectorcall = cfunction_vectorcall_FASTCALL; + break; + case METH_FASTCALL | METH_KEYWORDS: + vectorcall = cfunction_vectorcall_FASTCALL_KEYWORDS; + break; + case METH_NOARGS: + vectorcall = cfunction_vectorcall_NOARGS; + break; + case METH_O: + vectorcall = cfunction_vectorcall_O; + break; + default: + PyErr_SetString(PyExc_SystemError, "bad call flags"); + return NULL; + } + PyCFunctionObject *op; op = free_list; if (op != NULL) { @@ -46,14 +84,7 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) op->m_self = self; Py_XINCREF(module); op->m_module = module; - if (ml->ml_flags & METH_VARARGS) { - /* For METH_VARARGS functions, it's more efficient to use tp_call - * instead of vectorcall. */ - op->vectorcall = NULL; - } - else { - op->vectorcall = _PyCFunction_Vectorcall; - } + op->vectorcall = vectorcall; _PyObject_GC_TRACK(op); return (PyObject *)op; } @@ -333,3 +364,121 @@ _PyCFunction_DebugMallocStats(FILE *out) "free PyCFunctionObject", numfree, sizeof(PyCFunctionObject)); } + + +/* Vectorcall functions for each of the PyCFunction calling conventions, + * except for METH_VARARGS (possibly combined with METH_KEYWORDS) which + * doesn't use vectorcall. + * + * First, common helpers + */ +static const char * +get_name(PyObject *func) +{ + assert(PyCFunction_Check(func)); + PyMethodDef *method = ((PyCFunctionObject *)func)->m_ml; + return method->ml_name; +} + +typedef void (*funcptr)(void); + +static inline int +cfunction_check_kwargs(PyObject *func, PyObject *kwnames) +{ + assert(!PyErr_Occurred()); + assert(PyCFunction_Check(func)); + if (kwnames && PyTuple_GET_SIZE(kwnames)) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", get_name(func)); + return -1; + } + return 0; +} + +static inline funcptr +cfunction_enter_call(PyObject *func) +{ + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } + return (funcptr)PyCFunction_GET_FUNCTION(func); +} + +/* Now the actual vectorcall functions */ +static PyObject * +cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFast meth = (_PyCFunctionFast) + cfunction_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) + cfunction_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs, kwnames); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (nargs != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", get_name(func), nargs); + return NULL; + } + PyCFunction meth = (PyCFunction)cfunction_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), NULL); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (nargs != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + get_name(func), nargs); + return NULL; + } + PyCFunction meth = (PyCFunction)cfunction_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), args[0]); + Py_LeaveRecursiveCall(); + return result; +} diff --git a/Python/ceval.c b/Python/ceval.c index eddcc8d..546a426 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4943,7 +4943,7 @@ trace_call_function(PyThreadState *tstate, { PyObject *x; if (PyCFunction_Check(func)) { - C_TRACE(x, _PyCFunction_Vectorcall(func, args, nargs, kwnames)); + C_TRACE(x, _PyObject_Vectorcall(func, args, nargs, kwnames)); return x; } else if (Py_TYPE(func) == &PyMethodDescr_Type && nargs > 0) { @@ -4959,9 +4959,9 @@ trace_call_function(PyThreadState *tstate, if (func == NULL) { return NULL; } - C_TRACE(x, _PyCFunction_Vectorcall(func, - args+1, nargs-1, - kwnames)); + C_TRACE(x, _PyObject_Vectorcall(func, + args+1, nargs-1, + kwnames)); Py_DECREF(func); return x; } @@ -5023,10 +5023,10 @@ do_call_core(PyThreadState *tstate, PyObject *func, PyObject *callargs, PyObject return NULL; } - C_TRACE(result, _PyCFunction_FastCallDict(func, - &_PyTuple_ITEMS(callargs)[1], - nargs - 1, - kwdict)); + C_TRACE(result, _PyObject_FastCallDict(func, + &_PyTuple_ITEMS(callargs)[1], + nargs - 1, + kwdict)); Py_DECREF(func); return result; } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 93f720a..e40f79b 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1563,9 +1563,8 @@ class Frame(object): if not caller: return False - if caller in ('_PyCFunction_FastCallDict', - '_PyCFunction_Vectorcall', - 'cfunction_call_varargs'): + if (caller.startswith('cfunction_vectorcall_') or + caller == 'cfunction_call_varargs'): arg_name = 'func' # Within that frame: # "func" is the local containing the PyObject* of the -- cgit v0.12