in ext/Python/getargs.cpp [1422:1602]
static int vGetArgsKeywordsFastImpl(PyObject* const* args, Py_ssize_t nargs,
PyObject* keywords, PyObject* kwnames,
struct _PyArg_Parser* parser, va_list* p_va,
int flags) {
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
freelist_t freelist;
freelist.entries = static_entries;
freelist.first_available = 0;
freelist.entries_malloced = 0;
DCHECK(keywords == nullptr || PyDict_Check(keywords),
"keywords must be null or a dict");
DCHECK(kwnames == nullptr || PyTuple_Check(kwnames),
"kwnames must be null or a tuple");
DCHECK(keywords == nullptr || kwnames == nullptr,
"both keywords and kwnames cannot be non-null");
DCHECK(parser != nullptr, "parser must not be null");
DCHECK(p_va != nullptr, "p_va must not be null");
int keyword_count = 0;
if (!parserInit(parser, &keyword_count)) {
return false;
}
int pos = parser->pos;
int len = pos + keyword_count;
if (len > STATIC_FREELIST_ENTRIES) {
freelist.entries = PyMem_NEW(freelistentry_t, len);
if (freelist.entries == nullptr) {
PyErr_NoMemory();
return 0;
}
freelist.entries_malloced = 1;
}
Py_ssize_t num_keywords = 0;
PyObject* const* kwstack = nullptr;
if (keywords != nullptr) {
num_keywords = PyDict_Size(keywords);
} else if (kwnames != nullptr) {
num_keywords = PyTuple_GET_SIZE(kwnames);
kwstack = args + nargs;
}
if (nargs + num_keywords > len) {
PyErr_Format(PyExc_TypeError,
"%s%s takes at most %d argument%s (%zd given)",
(parser->fname == nullptr) ? "function" : parser->fname,
(parser->fname == nullptr) ? "" : "()", len,
(len == 1) ? "" : "s", nargs + num_keywords);
return cleanreturn(0, &freelist);
}
if (parser->max < nargs) {
PyErr_Format(
PyExc_TypeError, "Function takes %s %d positional arguments (%d given)",
(parser->min != INT_MAX) ? "at most" : "exactly", parser->max, nargs);
return cleanreturn(0, &freelist);
}
// convert tuple args and keyword args in same loop, using kwtuple to drive
// process
const char* format = parser->format;
for (int i = 0; i < len; i++) {
const char* keyword = i >= pos ? parser->keywords[i] : nullptr;
if (*format == '|') {
format++;
}
if (*format == '$') {
format++;
}
DCHECK(!IS_END_OF_FORMAT(*format), "end of format was reached");
PyObject* current_arg = nullptr;
if (num_keywords && i >= pos) {
if (keywords != nullptr) {
current_arg = PyDict_GetItemString(keywords, keyword);
if (current_arg == nullptr && PyErr_Occurred()) {
return cleanreturn(0, &freelist);
}
} else {
current_arg = findKeyword(kwnames, kwstack, keyword);
}
}
if (current_arg != nullptr) {
--num_keywords;
if (i < nargs) {
// arg present in tuple and in dict
PyErr_Format(PyExc_TypeError,
"Argument given by name ('%s') "
"and position (%d)",
keyword, i + 1);
return cleanreturn(0, &freelist);
}
} else if (i < nargs) {
current_arg = args[i];
}
if (current_arg != nullptr) {
char msgbuf[512];
int levels[32];
const char* msg = convertitem(current_arg, &format, p_va, flags, levels,
msgbuf, sizeof(msgbuf), &freelist);
if (msg) {
seterror(i + 1, msg, levels, parser->fname, parser->custom_msg);
return cleanreturn(0, &freelist);
}
continue;
}
if (i < parser->min) {
// Less arguments than required
if (i < pos) {
PyErr_Format(
PyExc_TypeError,
"Function takes %s %d positional arguments"
" (%d given)",
(Py_MIN(pos, parser->min) < parser->max) ? "at least" : "exactly",
Py_MIN(pos, parser->min), nargs);
} else {
PyErr_Format(PyExc_TypeError,
"Required argument "
"'%s' (pos %d) not found",
keyword, i + 1);
}
return cleanreturn(0, &freelist);
}
// current code reports success when all required args fulfilled and no
// keyword args left, with no further validation.
if (!num_keywords) {
return cleanreturn(1, &freelist);
}
// We are into optional args, skip through to any remaining keyword args
const char* message = skipitem(&format, p_va, flags);
DCHECK(message == nullptr, "message was not null");
}
DCHECK(IS_END_OF_FORMAT(*format) || (*format == '|') || (*format == '$'),
"expected end of format, '|', '$'");
// make sure there are no extraneous keyword arguments
if (num_keywords > 0) {
if (keywords != nullptr) {
PyObject *key, *value;
Py_ssize_t pos1 = 0;
while (PyDict_Next(keywords, &pos1, &key, &value)) {
if (!PyUnicode_Check(key)) {
Thread::current()->raiseWithFmt(LayoutId::kTypeError,
"keywords must be strings");
return cleanreturn(0, &freelist);
}
if (!isValidKeyword(parser, keyword_count, key)) {
PyErr_Format(PyExc_TypeError,
"'%s' is an invalid keyword "
"argument for this function",
key);
return cleanreturn(0, &freelist);
}
}
} else {
Py_ssize_t num_kwargs = PyTuple_GET_SIZE(kwnames);
for (Py_ssize_t j = 0; j < num_kwargs; j++) {
PyObject* key = PyTuple_GET_ITEM(kwnames, j);
if (!PyUnicode_Check(key)) {
Thread::current()->raiseWithFmt(LayoutId::kTypeError,
"keywords must be strings");
return cleanreturn(0, &freelist);
}
if (!isValidKeyword(parser, keyword_count, key)) {
PyErr_Format(PyExc_TypeError,
"'%U' is an invalid keyword "
"argument for this function",
key);
return cleanreturn(0, &freelist);
}
}
}
}
return cleanreturn(1, &freelist);
}