Skip to content

Commit cbd71fa

Browse files
author
Rémi Lapeyre
committed
Fix kwargs handling in ast node constructors
1 parent 6f78987 commit cbd71fa

File tree

4 files changed

+84
-6
lines changed

4 files changed

+84
-6
lines changed

Lib/ast.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,14 @@ def __instancecheck__(cls, inst):
443443
return type.__instancecheck__(cls, inst)
444444

445445
def _new(cls, *args, **kwargs):
446+
for key in kwargs:
447+
try:
448+
pos = cls._fields.index(key)
449+
if pos < len(args):
450+
raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}")
451+
except ValueError:
452+
# arbitrary keywork arguments are accepted
453+
pass
446454
if cls in _const_types:
447455
return Constant(*args, **kwargs)
448456
return Constant.__new__(cls, *args, **kwargs)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
_ast.ast_type_init now raises TypeError on conflicting keyword arguments.
2+
Patch contributed by Rémi Lapeyre.

Parser/asdl_c.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,7 @@ def visitModule(self, mod):
625625
self.emit("""
626626
_Py_IDENTIFIER(_fields);
627627
_Py_IDENTIFIER(_attributes);
628+
_Py_IDENTIFIER(index);
628629
629630
typedef struct {
630631
PyObject_HEAD
@@ -660,13 +661,15 @@ def visitModule(self, mod):
660661
Py_ssize_t i, numfields = 0;
661662
int res = -1;
662663
PyObject *key, *value, *fields;
664+
PyObject *index = NULL;
663665
if (_PyObject_LookupAttrId((PyObject*)Py_TYPE(self), &PyId__fields, &fields) < 0) {
664666
goto cleanup;
665667
}
666668
if (fields) {
667669
numfields = PySequence_Size(fields);
668-
if (numfields == -1)
670+
if (numfields == -1) {
669671
goto cleanup;
672+
}
670673
}
671674
672675
res = 0; /* if no error occurs, this stays 0 to the end */
@@ -687,19 +690,50 @@ def visitModule(self, mod):
687690
}
688691
res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
689692
Py_DECREF(name);
690-
if (res < 0)
693+
if (res < 0) {
691694
goto cleanup;
695+
}
692696
}
693697
if (kw) {
694698
i = 0; /* needed by PyDict_Next */
699+
if (_PyObject_LookupAttrId(fields, &PyId_index, &index) < 0) {
700+
goto cleanup;
701+
}
695702
while (PyDict_Next(kw, &i, &key, &value)) {
703+
PyObject* pos = _PyObject_FastCallDict(index, &key, 1, NULL);
704+
PyObject* err = PyErr_Occurred();
705+
if (pos == NULL || err != NULL) {
706+
// arbitrary keyword arguments are accepted
707+
if (!PyErr_GivenExceptionMatches(err, PyExc_ValueError)) {
708+
res = -1;
709+
goto cleanup;
710+
}
711+
PyErr_Clear();
712+
}
713+
else {
714+
Py_ssize_t p = PyNumber_AsSsize_t(pos, NULL);
715+
Py_DECREF(pos);
716+
if (p == -1 && PyErr_Occurred()) {
717+
res = -1;
718+
goto cleanup;
719+
}
720+
if (p < PyTuple_GET_SIZE(args)) {
721+
PyErr_Format(PyExc_TypeError,
722+
"%.400s got multiple values for argument '%U'",
723+
Py_TYPE(self)->tp_name, key);
724+
res = -1;
725+
goto cleanup;
726+
}
727+
}
696728
res = PyObject_SetAttr(self, key, value);
697-
if (res < 0)
729+
if (res < 0) {
698730
goto cleanup;
731+
}
699732
}
700733
}
701734
cleanup:
702735
Py_XDECREF(fields);
736+
Py_XDECREF(index);
703737
return res;
704738
}
705739

Python/Python-ast.c

Lines changed: 37 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)