mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +00:00
bpo-37540: vectorcall: keyword names must be strings (GH-14682)
The fact that keyword names are strings is now part of the vectorcall and `METH_FASTCALL` protocols. The biggest concrete change is that `_PyStack_UnpackDict` now checks that and raises `TypeError` if not. CC @markshannon @vstinner https://bugs.python.org/issue37540
This commit is contained in:
parent
f3cb68f2e4
commit
0567786d26
10 changed files with 43 additions and 46 deletions
|
@ -322,8 +322,7 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
|
|||
assert(nargs >= 0);
|
||||
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
|
||||
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
|
||||
/* kwnames must only contains str strings, no subclass, and all keys must
|
||||
be unique */
|
||||
/* kwnames must only contain strings and all keys must be unique */
|
||||
|
||||
if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
|
||||
(co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
|
||||
|
@ -943,12 +942,12 @@ _PyStack_AsDict(PyObject *const *values, PyObject *kwnames)
|
|||
vector; return NULL with exception set on error. Return the keyword names
|
||||
tuple in *p_kwnames.
|
||||
|
||||
This also checks that all keyword names are strings. If not, a TypeError is
|
||||
raised.
|
||||
|
||||
The newly allocated argument vector supports PY_VECTORCALL_ARGUMENTS_OFFSET.
|
||||
|
||||
When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames)
|
||||
|
||||
The type of keyword keys is not checked, these checks should be done
|
||||
later (ex: _PyArg_ParseStackAndKeywords). */
|
||||
When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames) */
|
||||
static PyObject *const *
|
||||
_PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
|
||||
PyObject **p_kwnames)
|
||||
|
@ -994,7 +993,9 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
|
|||
called in the performance critical hot code. */
|
||||
Py_ssize_t pos = 0, i = 0;
|
||||
PyObject *key, *value;
|
||||
unsigned long keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS;
|
||||
while (PyDict_Next(kwargs, &pos, &key, &value)) {
|
||||
keys_are_strings &= Py_TYPE(key)->tp_flags;
|
||||
Py_INCREF(key);
|
||||
Py_INCREF(value);
|
||||
PyTuple_SET_ITEM(kwnames, i, key);
|
||||
|
@ -1002,6 +1003,18 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
|
|||
i++;
|
||||
}
|
||||
|
||||
/* keys_are_strings has the value Py_TPFLAGS_UNICODE_SUBCLASS if that
|
||||
* flag is set for all keys. Otherwise, keys_are_strings equals 0.
|
||||
* We do this check once at the end instead of inside the loop above
|
||||
* because it simplifies the deallocation in the failing case.
|
||||
* It happens to also make the loop above slightly more efficient. */
|
||||
if (!keys_are_strings) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"keywords must be strings");
|
||||
_PyStack_UnpackDict_Free(stack, nargs, kwnames);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*p_kwnames = kwnames;
|
||||
return stack;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue