Skip to content

Commit 83c4801

Browse files
committed
Add PyArg_ParseVectorAndKeywords()
1 parent 8a32aa1 commit 83c4801

File tree

5 files changed

+128
-19
lines changed

5 files changed

+128
-19
lines changed

Doc/c-api/arg.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,17 @@ API Functions
527527
.. versionadded:: next
528528
529529
530+
.. c:function:: int PyArg_ParseVectorAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, const char * const *kwlist, ...)
531+
532+
Parse the parameters of a function that takes both vector and keyword
533+
parameters into local variables (that is, a function using the
534+
:c:macro:`METH_FASTCALL` ``|`` :c:macro:`METH_KEYWORDS` calling convention).
535+
Returns true on success; on failure, it returns false and raises the
536+
appropriate exception.
537+
538+
.. versionadded:: next
539+
540+
530541
.. c:function:: int PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...)
531542
532543
A simpler form of parameter retrieval which does not use a format string to

Doc/whatsnew/3.15.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,8 +1255,9 @@ C API changes
12551255
New features
12561256
------------
12571257

1258-
* Add :c:func:`PyArg_ParseVector` function to parse arguments of functions
1259-
using the :c:macro:`METH_FASTCALL` calling convention.
1258+
* Add :c:func:`PyArg_ParseVector` and :c:func:`PyArg_ParseVectorAndKeywords`
1259+
functions to parse arguments of functions using the :c:macro:`METH_FASTCALL`
1260+
calling convention.
12601261
(Contributed by Victor Stinner in :gh:`144175`.)
12611262

12621263
* Add :c:func:`PySys_GetAttr`, :c:func:`PySys_GetAttrString`,

Include/cpython/modsupport.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ PyAPI_FUNC(int) PyArg_ParseVector(
77
Py_ssize_t nargs,
88
const char *format,
99
...);
10+
PyAPI_FUNC(int) PyArg_ParseVectorAndKeywords(
11+
PyObject *const *args,
12+
Py_ssize_t nargs,
13+
PyObject *kwnames,
14+
const char *format,
15+
PY_CXX_CONST char * const *kwlist,
16+
...);
1017

1118
// A data structure that can be used to run initialization code once in a
1219
// thread-safe manner. The C++11 equivalent is std::call_once.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
Add :c:func:`PyArg_ParseVector` function to parse arguments of functions
2-
using the :c:macro:`METH_FASTCALL` calling convention. Patch by Victor
3-
Stinner.
1+
Add :c:func:`PyArg_ParseVector` and :c:func:`PyArg_ParseVectorAndKeywords`
2+
functions to parse arguments of functions using the :c:macro:`METH_FASTCALL`
3+
calling convention. Patch by Victor Stinner.

Python/getargs.c

Lines changed: 104 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,15 @@ static const char *convertsimple(PyObject *, const char **, va_list *, int,
5757
static Py_ssize_t convertbuffer(PyObject *, const void **p, const char **);
5858
static int getbuffer(PyObject *, Py_buffer *, const char**);
5959

60-
static int vgetargskeywords(PyObject *, PyObject *,
61-
const char *, const char * const *, va_list *, int);
60+
static int
61+
vgetargskeywords(PyObject *args, PyObject *kwargs,
62+
const char *format, const char * const *kwlist,
63+
va_list *p_va, int flags);
64+
static int
65+
vgetargskeywords_impl(PyObject *const *args, Py_ssize_t nargs,
66+
PyObject *kwargs, PyObject *kwnames,
67+
const char *format, const char * const *kwlist,
68+
va_list *p_va, int flags);
6269
static int vgetargskeywordsfast(PyObject *, PyObject *,
6370
struct _PyArg_Parser *, va_list *, int);
6471
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, .
139146
return retval;
140147
}
141148

149+
int
150+
PyArg_ParseVectorAndKeywords(PyObject *const *args, Py_ssize_t nargs,
151+
PyObject *kwnames,
152+
const char *format,
153+
const char * const *kwlist, ...)
154+
{
155+
if ((args == NULL && nargs != 0) ||
156+
(kwnames != NULL && !PyTuple_Check(kwnames)) ||
157+
format == NULL ||
158+
kwlist == NULL)
159+
{
160+
PyErr_BadInternalCall();
161+
return 0;
162+
}
163+
164+
va_list va;
165+
va_start(va, kwlist);
166+
int retval = vgetargskeywords_impl(args, nargs, NULL, kwnames, format,
167+
kwlist, &va, 0);
168+
va_end(va);
169+
return retval;
170+
}
171+
172+
142173
int
143174
PyArg_VaParse(PyObject *args, const char *format, va_list va)
144175
{
@@ -1622,11 +1653,27 @@ PyArg_ValidateKeywordArguments(PyObject *kwargs)
16221653
static PyObject *
16231654
new_kwtuple(const char * const *keywords, int total, int pos);
16241655

1656+
static PyObject*
1657+
find_keyword_str(PyObject *kwnames, PyObject *const *kwstack, const char *key)
1658+
{
1659+
Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames);
1660+
for (Py_ssize_t i = 0; i < nkwargs; i++) {
1661+
PyObject *kwname = PyTuple_GET_ITEM(kwnames, i);
1662+
assert(PyUnicode_Check(kwname));
1663+
if (PyUnicode_EqualToUTF8(kwname, key)) {
1664+
return Py_NewRef(kwstack[i]);
1665+
}
1666+
}
1667+
return NULL;
1668+
}
1669+
16251670
#define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':')
16261671

16271672
static int
1628-
vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format,
1629-
const char * const *kwlist, va_list *p_va, int flags)
1673+
vgetargskeywords_impl(PyObject *const *args, Py_ssize_t nargs,
1674+
PyObject *kwargs, PyObject *kwnames,
1675+
const char *format, const char * const *kwlist,
1676+
va_list *p_va, int flags)
16301677
{
16311678
char msgbuf[512];
16321679
int levels[32];
@@ -1635,16 +1682,18 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format,
16351682
int max = INT_MAX;
16361683
int i, pos, len;
16371684
int skip = 0;
1638-
Py_ssize_t nargs, nkwargs;
1685+
Py_ssize_t nkwargs;
16391686
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
16401687
freelist_t freelist;
1688+
PyObject * const *kwstack = NULL;
16411689

16421690
freelist.entries = static_entries;
16431691
freelist.first_available = 0;
16441692
freelist.entries_malloced = 0;
16451693

1646-
assert(args != NULL && PyTuple_Check(args));
1694+
assert(args != NULL || nargs == 0);
16471695
assert(kwargs == NULL || PyDict_Check(kwargs));
1696+
assert(kwnames == NULL || PyTuple_Check(kwnames));
16481697
assert(format != NULL);
16491698
assert(kwlist != NULL);
16501699
assert(p_va != NULL);
@@ -1682,8 +1731,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format,
16821731
freelist.entries_malloced = 1;
16831732
}
16841733

1685-
nargs = PyTuple_GET_SIZE(args);
1686-
nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs);
1734+
if (kwargs != NULL) {
1735+
nkwargs = PyDict_GET_SIZE(kwargs);
1736+
}
1737+
else if (kwnames != NULL) {
1738+
nkwargs = PyTuple_GET_SIZE(kwnames);
1739+
kwstack = args + nargs;
1740+
}
1741+
else {
1742+
nkwargs = 0;
1743+
}
16871744
if (nargs + nkwargs > len) {
16881745
/* Adding "keyword" (when nargs == 0) prevents producing wrong error
16891746
messages in some special cases (see bpo-31229). */
@@ -1767,11 +1824,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format,
17671824
if (!skip) {
17681825
PyObject *current_arg;
17691826
if (i < nargs) {
1770-
current_arg = Py_NewRef(PyTuple_GET_ITEM(args, i));
1827+
current_arg = Py_NewRef(args[i]);
17711828
}
17721829
else if (nkwargs && i >= pos) {
1773-
if (PyDict_GetItemStringRef(kwargs, kwlist[i], &current_arg) < 0) {
1774-
return cleanreturn(0, &freelist);
1830+
if (kwargs != NULL) {
1831+
if (PyDict_GetItemStringRef(kwargs, kwlist[i], &current_arg) < 0) {
1832+
return cleanreturn(0, &freelist);
1833+
}
1834+
}
1835+
else {
1836+
current_arg = find_keyword_str(kwnames, kwstack, kwlist[i]);
17751837
}
17761838
if (current_arg) {
17771839
--nkwargs;
@@ -1856,8 +1918,13 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format,
18561918
/* make sure there are no arguments given by name and position */
18571919
for (i = pos; i < nargs; i++) {
18581920
PyObject *current_arg;
1859-
if (PyDict_GetItemStringRef(kwargs, kwlist[i], &current_arg) < 0) {
1860-
return cleanreturn(0, &freelist);
1921+
if (kwargs != NULL) {
1922+
if (PyDict_GetItemStringRef(kwargs, kwlist[i], &current_arg) < 0) {
1923+
return cleanreturn(0, &freelist);
1924+
}
1925+
}
1926+
else {
1927+
current_arg = find_keyword_str(kwnames, kwstack, kwlist[i]);
18611928
}
18621929
if (current_arg) {
18631930
Py_DECREF(current_arg);
@@ -1873,7 +1940,20 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format,
18731940
}
18741941
/* make sure there are no extraneous keyword arguments */
18751942
j = 0;
1876-
while (PyDict_Next(kwargs, &j, &key, NULL)) {
1943+
while (1) {
1944+
if (kwargs != NULL) {
1945+
if (!PyDict_Next(kwargs, &j, &key, NULL)) {
1946+
break;
1947+
}
1948+
}
1949+
else {
1950+
if (j >= nkwargs) {
1951+
break;
1952+
}
1953+
key = PyTuple_GET_ITEM(kwnames, j);
1954+
j++;
1955+
}
1956+
18771957
int match = 0;
18781958
if (!PyUnicode_Check(key)) {
18791959
PyErr_SetString(PyExc_TypeError,
@@ -1931,6 +2011,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format,
19312011
return cleanreturn(1, &freelist);
19322012
}
19332013

2014+
static int
2015+
vgetargskeywords(PyObject *argstuple, PyObject *kwargs,
2016+
const char *format, const char * const *kwlist,
2017+
va_list *p_va, int flags)
2018+
{
2019+
PyObject *const *args = _PyTuple_ITEMS(argstuple);
2020+
Py_ssize_t nargs = PyTuple_GET_SIZE(argstuple);
2021+
return vgetargskeywords_impl(args, nargs, kwargs, NULL,
2022+
format, kwlist, p_va, flags);
2023+
}
19342024

19352025
static int
19362026
scan_keywords(const char * const *keywords, int *ptotal, int *pposonly)

0 commit comments

Comments
 (0)