From 769e40bdc72cd33a1d8933ff3dae30ffaa6b1156 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 27 Jan 2026 18:06:32 +0100 Subject: [PATCH 1/8] gh-144175: Add PyArg_ParseVector() function --- Doc/c-api/arg.rst | 11 +++++++++++ Doc/whatsnew/3.15.rst | 4 ++++ Include/cpython/modsupport.h | 6 ++++++ .../2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst | 3 +++ Python/getargs.c | 10 ++++++++++ 5 files changed, 34 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index fd6be6a9b67a03..e36d1d6102cce5 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -516,6 +516,17 @@ API Functions } +.. c:function:: int PyArg_ParseVector(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) + + Parse the parameters of a function that takes only vector parameters into + local variables; function using the :c:macro:`METH_FASTCALL` calling + convention. + Returns true on success; on failure, it returns false and raises the + appropriate exception. + + .. versionadded:: 3.15 + + .. c:function:: int PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...) A simpler form of parameter retrieval which does not use a format string to diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index c412f48606c045..394ac6476f663e 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1255,6 +1255,10 @@ C API changes New features ------------ +* Add :c:func:`PyArg_ParseVector` function to parse arguments of functions + using the :c:macro:`METH_FASTCALL` calling convention. + (Contributed by Victor Stinner in :gh:`144175`.) + * Add :c:func:`PySys_GetAttr`, :c:func:`PySys_GetAttrString`, :c:func:`PySys_GetOptionalAttr`, and :c:func:`PySys_GetOptionalAttrString` functions as replacements for :c:func:`PySys_GetObject`. diff --git a/Include/cpython/modsupport.h b/Include/cpython/modsupport.h index 6134442106474f..956d3837a61142 100644 --- a/Include/cpython/modsupport.h +++ b/Include/cpython/modsupport.h @@ -2,6 +2,12 @@ # error "this header file must not be included directly" #endif +PyAPI_FUNC(int) PyArg_ParseVector( + PyObject *const *args, + Py_ssize_t nargs, + const char *format, + ...); + // A data structure that can be used to run initialization code once in a // thread-safe manner. The C++11 equivalent is std::call_once. typedef struct { diff --git a/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst b/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst new file mode 100644 index 00000000000000..420f80f43cc3ea --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst @@ -0,0 +1,3 @@ +Add :c:func:`PyArg_ParseVector` function to parse arguments of functions +using the :c:macro:`METH_FASTCALL` calling convention. Patch by Victor +Stinner. diff --git a/Python/getargs.c b/Python/getargs.c index c119ca5c35398b..4005439c7e50fe 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -129,6 +129,16 @@ _PyArg_ParseStack(PyObject *const *args, Py_ssize_t nargs, const char *format, . return retval; } +int +PyArg_ParseVector(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) +{ + va_list va; + va_start(va, format); + int retval = vgetargs1_impl(NULL, args, nargs, format, &va, 0); + va_end(va); + return retval; +} + int PyArg_VaParse(PyObject *args, const char *format, va_list va) { From c2aa2b741973d62ae531fa62e18348ad5f3c23f4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 28 Jan 2026 11:24:41 +0100 Subject: [PATCH 2/8] Update Doc/c-api/arg.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/c-api/arg.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index e36d1d6102cce5..72c8f3ffefc5e8 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -524,7 +524,7 @@ API Functions Returns true on success; on failure, it returns false and raises the appropriate exception. - .. versionadded:: 3.15 + .. versionadded:: next .. c:function:: int PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...) From 6e729000eb479fa25c195478a402dfe0c7117ed7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 28 Jan 2026 11:26:05 +0100 Subject: [PATCH 3/8] Rephrase the doc --- Doc/c-api/arg.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 72c8f3ffefc5e8..fcd65310b92329 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -519,8 +519,8 @@ API Functions .. c:function:: int PyArg_ParseVector(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) Parse the parameters of a function that takes only vector parameters into - local variables; function using the :c:macro:`METH_FASTCALL` calling - convention. + local variables (that is, a function using the :c:macro:METH_FASTCALL calling + convention). Returns true on success; on failure, it returns false and raises the appropriate exception. From 8a32aa1866acd24a4a570231266cc059f5076a14 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 28 Jan 2026 12:04:40 +0100 Subject: [PATCH 4/8] doc: fix syntax --- Doc/c-api/arg.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index fcd65310b92329..a9d81b393ad21f 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -519,8 +519,8 @@ API Functions .. c:function:: int PyArg_ParseVector(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) Parse the parameters of a function that takes only vector parameters into - local variables (that is, a function using the :c:macro:METH_FASTCALL calling - convention). + local variables (that is, a function using the :c:macro:`METH_FASTCALL` + calling convention). Returns true on success; on failure, it returns false and raises the appropriate exception. From 83c4801da3fb3ee0034251818f5afa0c84fd554c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 28 Jan 2026 15:55:15 +0100 Subject: [PATCH 5/8] Add PyArg_ParseVectorAndKeywords() --- Doc/c-api/arg.rst | 11 ++ Doc/whatsnew/3.15.rst | 5 +- Include/cpython/modsupport.h | 7 ++ ...-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst | 6 +- Python/getargs.c | 118 +++++++++++++++--- 5 files changed, 128 insertions(+), 19 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index a9d81b393ad21f..6fcf9922e9df68 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -527,6 +527,17 @@ API Functions .. versionadded:: next +.. c:function:: int PyArg_ParseVectorAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, const char * const *kwlist, ...) + + Parse the parameters of a function that takes both vector and keyword + parameters into local variables (that is, a function using the + :c:macro:`METH_FASTCALL` ``|`` :c:macro:`METH_KEYWORDS` calling convention). + Returns true on success; on failure, it returns false and raises the + appropriate exception. + + .. versionadded:: next + + .. c:function:: int PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...) A simpler form of parameter retrieval which does not use a format string to diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 394ac6476f663e..5bf75c903ea9cd 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1255,8 +1255,9 @@ C API changes New features ------------ -* Add :c:func:`PyArg_ParseVector` function to parse arguments of functions - using the :c:macro:`METH_FASTCALL` calling convention. +* Add :c:func:`PyArg_ParseVector` and :c:func:`PyArg_ParseVectorAndKeywords` + functions to parse arguments of functions using the :c:macro:`METH_FASTCALL` + calling convention. (Contributed by Victor Stinner in :gh:`144175`.) * Add :c:func:`PySys_GetAttr`, :c:func:`PySys_GetAttrString`, diff --git a/Include/cpython/modsupport.h b/Include/cpython/modsupport.h index 956d3837a61142..1ed5dd6a1e1ac3 100644 --- a/Include/cpython/modsupport.h +++ b/Include/cpython/modsupport.h @@ -7,6 +7,13 @@ PyAPI_FUNC(int) PyArg_ParseVector( Py_ssize_t nargs, const char *format, ...); +PyAPI_FUNC(int) PyArg_ParseVectorAndKeywords( + PyObject *const *args, + Py_ssize_t nargs, + PyObject *kwnames, + const char *format, + PY_CXX_CONST char * const *kwlist, + ...); // A data structure that can be used to run initialization code once in a // thread-safe manner. The C++11 equivalent is std::call_once. diff --git a/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst b/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst index 420f80f43cc3ea..a9d57d1a74a341 100644 --- a/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst +++ b/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst @@ -1,3 +1,3 @@ -Add :c:func:`PyArg_ParseVector` function to parse arguments of functions -using the :c:macro:`METH_FASTCALL` calling convention. Patch by Victor -Stinner. +Add :c:func:`PyArg_ParseVector` and :c:func:`PyArg_ParseVectorAndKeywords` +functions to parse arguments of functions using the :c:macro:`METH_FASTCALL` +calling convention. Patch by Victor Stinner. diff --git a/Python/getargs.c b/Python/getargs.c index 4005439c7e50fe..e76db366853f49 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -57,8 +57,15 @@ static const char *convertsimple(PyObject *, const char **, va_list *, int, static Py_ssize_t convertbuffer(PyObject *, const void **p, const char **); static int getbuffer(PyObject *, Py_buffer *, const char**); -static int vgetargskeywords(PyObject *, PyObject *, - const char *, const char * const *, va_list *, int); +static int +vgetargskeywords(PyObject *args, PyObject *kwargs, + const char *format, const char * const *kwlist, + va_list *p_va, int flags); +static int +vgetargskeywords_impl(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + const char *format, const char * const *kwlist, + va_list *p_va, int flags); static int vgetargskeywordsfast(PyObject *, PyObject *, struct _PyArg_Parser *, va_list *, int); static int vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, @@ -139,6 +146,30 @@ PyArg_ParseVector(PyObject *const *args, Py_ssize_t nargs, const char *format, . return retval; } +int +PyArg_ParseVectorAndKeywords(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames, + const char *format, + const char * const *kwlist, ...) +{ + if ((args == NULL && nargs != 0) || + (kwnames != NULL && !PyTuple_Check(kwnames)) || + format == NULL || + kwlist == NULL) + { + PyErr_BadInternalCall(); + return 0; + } + + va_list va; + va_start(va, kwlist); + int retval = vgetargskeywords_impl(args, nargs, NULL, kwnames, format, + kwlist, &va, 0); + va_end(va); + return retval; +} + + int PyArg_VaParse(PyObject *args, const char *format, va_list va) { @@ -1622,11 +1653,27 @@ PyArg_ValidateKeywordArguments(PyObject *kwargs) static PyObject * new_kwtuple(const char * const *keywords, int total, int pos); +static PyObject* +find_keyword_str(PyObject *kwnames, PyObject *const *kwstack, const char *key) +{ + Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames); + for (Py_ssize_t i = 0; i < nkwargs; i++) { + PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); + assert(PyUnicode_Check(kwname)); + if (PyUnicode_EqualToUTF8(kwname, key)) { + return Py_NewRef(kwstack[i]); + } + } + return NULL; +} + #define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':') static int -vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, - const char * const *kwlist, va_list *p_va, int flags) +vgetargskeywords_impl(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + const char *format, const char * const *kwlist, + va_list *p_va, int flags) { char msgbuf[512]; int levels[32]; @@ -1635,16 +1682,18 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, int max = INT_MAX; int i, pos, len; int skip = 0; - Py_ssize_t nargs, nkwargs; + Py_ssize_t nkwargs; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelist_t freelist; + PyObject * const *kwstack = NULL; freelist.entries = static_entries; freelist.first_available = 0; freelist.entries_malloced = 0; - assert(args != NULL && PyTuple_Check(args)); + assert(args != NULL || nargs == 0); assert(kwargs == NULL || PyDict_Check(kwargs)); + assert(kwnames == NULL || PyTuple_Check(kwnames)); assert(format != NULL); assert(kwlist != NULL); assert(p_va != NULL); @@ -1682,8 +1731,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, freelist.entries_malloced = 1; } - nargs = PyTuple_GET_SIZE(args); - nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs); + if (kwargs != NULL) { + nkwargs = PyDict_GET_SIZE(kwargs); + } + else if (kwnames != NULL) { + nkwargs = PyTuple_GET_SIZE(kwnames); + kwstack = args + nargs; + } + else { + nkwargs = 0; + } if (nargs + nkwargs > len) { /* Adding "keyword" (when nargs == 0) prevents producing wrong error messages in some special cases (see bpo-31229). */ @@ -1767,11 +1824,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (!skip) { PyObject *current_arg; if (i < nargs) { - current_arg = Py_NewRef(PyTuple_GET_ITEM(args, i)); + current_arg = Py_NewRef(args[i]); } else if (nkwargs && i >= pos) { - if (PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0) { - return cleanreturn(0, &freelist); + if (kwargs != NULL) { + if (PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0) { + return cleanreturn(0, &freelist); + } + } + else { + current_arg = find_keyword_str(kwnames, kwstack, kwlist[i]); } if (current_arg) { --nkwargs; @@ -1856,8 +1918,13 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, /* make sure there are no arguments given by name and position */ for (i = pos; i < nargs; i++) { PyObject *current_arg; - if (PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0) { - return cleanreturn(0, &freelist); + if (kwargs != NULL) { + if (PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0) { + return cleanreturn(0, &freelist); + } + } + else { + current_arg = find_keyword_str(kwnames, kwstack, kwlist[i]); } if (current_arg) { Py_DECREF(current_arg); @@ -1873,7 +1940,20 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, } /* make sure there are no extraneous keyword arguments */ j = 0; - while (PyDict_Next(kwargs, &j, &key, NULL)) { + while (1) { + if (kwargs != NULL) { + if (!PyDict_Next(kwargs, &j, &key, NULL)) { + break; + } + } + else { + if (j >= nkwargs) { + break; + } + key = PyTuple_GET_ITEM(kwnames, j); + j++; + } + int match = 0; if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, @@ -1931,6 +2011,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, return cleanreturn(1, &freelist); } +static int +vgetargskeywords(PyObject *argstuple, PyObject *kwargs, + const char *format, const char * const *kwlist, + va_list *p_va, int flags) +{ + PyObject *const *args = _PyTuple_ITEMS(argstuple); + Py_ssize_t nargs = PyTuple_GET_SIZE(argstuple); + return vgetargskeywords_impl(args, nargs, kwargs, NULL, + format, kwlist, p_va, flags); +} static int scan_keywords(const char * const *keywords, int *ptotal, int *pposonly) From d0cad25c87c318fe37d3569eb040a65bb18bc379 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Mar 2026 22:19:35 +0100 Subject: [PATCH 6/8] Don't use PY_CXX_CONST; add tests --- Doc/c-api/arg.rst | 2 +- Include/cpython/modsupport.h | 2 +- Lib/test/test_capi/test_modsupport.py | 19 ++++++++++++++++++ Modules/_testcapi/modsupport.c | 28 +++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 6fcf9922e9df68..46e968e3c90d65 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -527,7 +527,7 @@ API Functions .. versionadded:: next -.. c:function:: int PyArg_ParseVectorAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, const char * const *kwlist, ...) +.. c:function:: int PyArg_ParseVectorAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, const char *format, const char * const *kwlist, ...) Parse the parameters of a function that takes both vector and keyword parameters into local variables (that is, a function using the diff --git a/Include/cpython/modsupport.h b/Include/cpython/modsupport.h index 1ed5dd6a1e1ac3..5aadac7132cb99 100644 --- a/Include/cpython/modsupport.h +++ b/Include/cpython/modsupport.h @@ -12,7 +12,7 @@ PyAPI_FUNC(int) PyArg_ParseVectorAndKeywords( Py_ssize_t nargs, PyObject *kwnames, const char *format, - PY_CXX_CONST char * const *kwlist, + const char * const *kwlist, ...); // A data structure that can be used to run initialization code once in a diff --git a/Lib/test/test_capi/test_modsupport.py b/Lib/test/test_capi/test_modsupport.py index 1520489f843826..4e7b30bc98b1e5 100644 --- a/Lib/test/test_capi/test_modsupport.py +++ b/Lib/test/test_capi/test_modsupport.py @@ -152,3 +152,22 @@ def test_negative_freethreading(self, modname, minor, build): msg = "only compatible with free-threaded CPython" with self.assertRaisesRegex(ImportError, msg): _testcapi.pyabiinfo_check(modname, 1, minor, ft_flag, build, 0) + + +class TestModsupport(unittest.TestCase): + def test_pyarg_parsevector(self): + func = _testcapi.pyarg_parsevector + self.assertEqual(func(1, 2), (1, 2, 0)) + self.assertEqual(func(1, 2, 3), (1, 2, 3)) + self.assertRaises(TypeError, func, 1) + self.assertRaises(TypeError, func, "str", 2) + + def test_funcandkeywords(self): + func = _testcapi.pyarg_parsevectorandkeywords + self.assertEqual(func(1, 2), (1, 2, 0)) + self.assertEqual(func(1, 2, 3), (1, 2, 3)) + self.assertEqual(func(1, b=2), (1, 2, 0)) + self.assertEqual(func(1, b=2, c=3), (1, 2, 3)) + self.assertRaises(TypeError, func, 1) + self.assertRaises(TypeError, func, "str", 2) + self.assertRaises(TypeError, func, 1, z=2) diff --git a/Modules/_testcapi/modsupport.c b/Modules/_testcapi/modsupport.c index 6746eb9eb1e94a..2af9b1336bf2f0 100644 --- a/Modules/_testcapi/modsupport.c +++ b/Modules/_testcapi/modsupport.c @@ -25,8 +25,36 @@ pyabiinfo_check(PyObject *Py_UNUSED(module), PyObject *args) Py_RETURN_NONE; } +static PyObject * +pyarg_parsevector(PyObject* self, PyObject* const* args, Py_ssize_t nargs) +{ + int a, b, c = 0; + if (!PyArg_ParseVector(args, nargs, "ii|i", &a, &b, &c)) { + return NULL; + } + return Py_BuildValue("iii", a, b, c); +} + +static PyObject * +pyarg_parsevectorandkeywords(PyObject* self, PyObject* const* args, + Py_ssize_t nargs, PyObject* kwnames) +{ + int a, b, c = 0; + const char *kwlist[] = {"a", "b", "c", NULL}; + if (!PyArg_ParseVectorAndKeywords(args, nargs, kwnames, + "ii|i", kwlist, + &a, &b, &c)) { + return NULL; + } + return Py_BuildValue("iii", a, b, c); +} + static PyMethodDef TestMethods[] = { {"pyabiinfo_check", pyabiinfo_check, METH_VARARGS}, + {"pyarg_parsevector", _PyCFunction_CAST(pyarg_parsevector), METH_FASTCALL}, + {"pyarg_parsevectorandkeywords", + _PyCFunction_CAST(pyarg_parsevectorandkeywords), + METH_FASTCALL | METH_KEYWORDS}, {NULL}, }; From 78510f5d0c55a8de84ac47242f5df4150cd2daea Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Mar 2026 22:24:25 +0100 Subject: [PATCH 7/8] Rename to PyArg_ParseArray() --- Doc/c-api/arg.rst | 4 ++-- Include/cpython/modsupport.h | 4 ++-- Lib/test/test_capi/test_modsupport.py | 6 +++--- Modules/_testcapi/modsupport.c | 20 ++++++++++---------- Python/getargs.c | 10 +++++----- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 46e968e3c90d65..c4fe84d4dd3a59 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -516,7 +516,7 @@ API Functions } -.. c:function:: int PyArg_ParseVector(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) +.. c:function:: int PyArg_ParseArray(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) Parse the parameters of a function that takes only vector parameters into local variables (that is, a function using the :c:macro:`METH_FASTCALL` @@ -527,7 +527,7 @@ API Functions .. versionadded:: next -.. c:function:: int PyArg_ParseVectorAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, const char *format, const char * const *kwlist, ...) +.. c:function:: int PyArg_ParseArrayAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, const char *format, const char * const *kwlist, ...) Parse the parameters of a function that takes both vector and keyword parameters into local variables (that is, a function using the diff --git a/Include/cpython/modsupport.h b/Include/cpython/modsupport.h index 5aadac7132cb99..b9f253e06b31c9 100644 --- a/Include/cpython/modsupport.h +++ b/Include/cpython/modsupport.h @@ -2,12 +2,12 @@ # error "this header file must not be included directly" #endif -PyAPI_FUNC(int) PyArg_ParseVector( +PyAPI_FUNC(int) PyArg_ParseArray( PyObject *const *args, Py_ssize_t nargs, const char *format, ...); -PyAPI_FUNC(int) PyArg_ParseVectorAndKeywords( +PyAPI_FUNC(int) PyArg_ParseArrayAndKeywords( PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, diff --git a/Lib/test/test_capi/test_modsupport.py b/Lib/test/test_capi/test_modsupport.py index 4e7b30bc98b1e5..29bebf847aaba2 100644 --- a/Lib/test/test_capi/test_modsupport.py +++ b/Lib/test/test_capi/test_modsupport.py @@ -155,15 +155,15 @@ def test_negative_freethreading(self, modname, minor, build): class TestModsupport(unittest.TestCase): - def test_pyarg_parsevector(self): - func = _testcapi.pyarg_parsevector + def test_pyarg_parsearray(self): + func = _testcapi.pyarg_parsearray self.assertEqual(func(1, 2), (1, 2, 0)) self.assertEqual(func(1, 2, 3), (1, 2, 3)) self.assertRaises(TypeError, func, 1) self.assertRaises(TypeError, func, "str", 2) def test_funcandkeywords(self): - func = _testcapi.pyarg_parsevectorandkeywords + func = _testcapi.pyarg_parsearrayandkeywords self.assertEqual(func(1, 2), (1, 2, 0)) self.assertEqual(func(1, 2, 3), (1, 2, 3)) self.assertEqual(func(1, b=2), (1, 2, 0)) diff --git a/Modules/_testcapi/modsupport.c b/Modules/_testcapi/modsupport.c index 2af9b1336bf2f0..151e4aa19afe11 100644 --- a/Modules/_testcapi/modsupport.c +++ b/Modules/_testcapi/modsupport.c @@ -26,24 +26,24 @@ pyabiinfo_check(PyObject *Py_UNUSED(module), PyObject *args) } static PyObject * -pyarg_parsevector(PyObject* self, PyObject* const* args, Py_ssize_t nargs) +pyarg_parsearray(PyObject* self, PyObject* const* args, Py_ssize_t nargs) { int a, b, c = 0; - if (!PyArg_ParseVector(args, nargs, "ii|i", &a, &b, &c)) { + if (!PyArg_ParseArray(args, nargs, "ii|i", &a, &b, &c)) { return NULL; } return Py_BuildValue("iii", a, b, c); } static PyObject * -pyarg_parsevectorandkeywords(PyObject* self, PyObject* const* args, - Py_ssize_t nargs, PyObject* kwnames) +pyarg_parsearrayandkeywords(PyObject* self, PyObject* const* args, + Py_ssize_t nargs, PyObject* kwnames) { int a, b, c = 0; const char *kwlist[] = {"a", "b", "c", NULL}; - if (!PyArg_ParseVectorAndKeywords(args, nargs, kwnames, - "ii|i", kwlist, - &a, &b, &c)) { + if (!PyArg_ParseArrayAndKeywords(args, nargs, kwnames, + "ii|i", kwlist, + &a, &b, &c)) { return NULL; } return Py_BuildValue("iii", a, b, c); @@ -51,9 +51,9 @@ pyarg_parsevectorandkeywords(PyObject* self, PyObject* const* args, static PyMethodDef TestMethods[] = { {"pyabiinfo_check", pyabiinfo_check, METH_VARARGS}, - {"pyarg_parsevector", _PyCFunction_CAST(pyarg_parsevector), METH_FASTCALL}, - {"pyarg_parsevectorandkeywords", - _PyCFunction_CAST(pyarg_parsevectorandkeywords), + {"pyarg_parsearray", _PyCFunction_CAST(pyarg_parsearray), METH_FASTCALL}, + {"pyarg_parsearrayandkeywords", + _PyCFunction_CAST(pyarg_parsearrayandkeywords), METH_FASTCALL | METH_KEYWORDS}, {NULL}, }; diff --git a/Python/getargs.c b/Python/getargs.c index e76db366853f49..31cd4ad3f652d9 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -137,7 +137,7 @@ _PyArg_ParseStack(PyObject *const *args, Py_ssize_t nargs, const char *format, . } int -PyArg_ParseVector(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) +PyArg_ParseArray(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) { va_list va; va_start(va, format); @@ -147,10 +147,10 @@ PyArg_ParseVector(PyObject *const *args, Py_ssize_t nargs, const char *format, . } int -PyArg_ParseVectorAndKeywords(PyObject *const *args, Py_ssize_t nargs, - PyObject *kwnames, - const char *format, - const char * const *kwlist, ...) +PyArg_ParseArrayAndKeywords(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames, + const char *format, + const char * const *kwlist, ...) { if ((args == NULL && nargs != 0) || (kwnames != NULL && !PyTuple_Check(kwnames)) || From 07dc96899d1d01dacc011906d7eecf6bb211554c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Mar 2026 22:28:30 +0100 Subject: [PATCH 8/8] Replace more references to Vector with Array --- Doc/c-api/arg.rst | 4 ++-- Doc/whatsnew/3.15.rst | 2 +- .../next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index c4fe84d4dd3a59..4a3a6347239c4f 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -518,7 +518,7 @@ API Functions .. c:function:: int PyArg_ParseArray(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) - Parse the parameters of a function that takes only vector parameters into + Parse the parameters of a function that takes only array parameters into local variables (that is, a function using the :c:macro:`METH_FASTCALL` calling convention). Returns true on success; on failure, it returns false and raises the @@ -529,7 +529,7 @@ API Functions .. c:function:: int PyArg_ParseArrayAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, const char *format, const char * const *kwlist, ...) - Parse the parameters of a function that takes both vector and keyword + Parse the parameters of a function that takes both array and keyword parameters into local variables (that is, a function using the :c:macro:`METH_FASTCALL` ``|`` :c:macro:`METH_KEYWORDS` calling convention). Returns true on success; on failure, it returns false and raises the diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 616ab832558206..42b6171c1a83a2 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1607,7 +1607,7 @@ C API changes New features ------------ -* Add :c:func:`PyArg_ParseVector` and :c:func:`PyArg_ParseVectorAndKeywords` +* Add :c:func:`PyArg_ParseArray` and :c:func:`PyArg_ParseArrayAndKeywords` functions to parse arguments of functions using the :c:macro:`METH_FASTCALL` calling convention. (Contributed by Victor Stinner in :gh:`144175`.) diff --git a/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst b/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst index a9d57d1a74a341..da1e489bb3d2e5 100644 --- a/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst +++ b/Misc/NEWS.d/next/C_API/2026-01-27-18-15-15.gh-issue-144175.qHK5Jf.rst @@ -1,3 +1,3 @@ -Add :c:func:`PyArg_ParseVector` and :c:func:`PyArg_ParseVectorAndKeywords` +Add :c:func:`PyArg_ParseArray` and :c:func:`PyArg_ParseArrayAndKeywords` functions to parse arguments of functions using the :c:macro:`METH_FASTCALL` calling convention. Patch by Victor Stinner.