Browse Source
bpo-36290: Fix keytword collision handling in AST node constructors (GH-12382)
pull/20367/head
Rémi Lapeyre
6 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with
66 additions and
6 deletions
-
Lib/ast.py
-
Lib/test/test_ast.py
-
Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rst
-
Parser/asdl_c.py
-
Python/Python-ast.c
|
|
|
@ -524,6 +524,13 @@ class _ABC(type): |
|
|
|
return type.__instancecheck__(cls, inst) |
|
|
|
|
|
|
|
def _new(cls, *args, **kwargs): |
|
|
|
for key in kwargs: |
|
|
|
if key not in cls._fields: |
|
|
|
# arbitrary keyword arguments are accepted |
|
|
|
continue |
|
|
|
pos = cls._fields.index(key) |
|
|
|
if pos < len(args): |
|
|
|
raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}") |
|
|
|
if cls in _const_types: |
|
|
|
return Constant(*args, **kwargs) |
|
|
|
return Constant.__new__(cls, *args, **kwargs) |
|
|
|
|
|
|
|
@ -402,6 +402,15 @@ class AST_Tests(unittest.TestCase): |
|
|
|
self.assertRaises(TypeError, ast.Num, 1, None, 2) |
|
|
|
self.assertRaises(TypeError, ast.Num, 1, None, 2, lineno=0) |
|
|
|
|
|
|
|
# Arbitrary keyword arguments are supported |
|
|
|
self.assertEqual(ast.Constant(1, foo='bar').foo, 'bar') |
|
|
|
self.assertEqual(ast.Num(1, foo='bar').foo, 'bar') |
|
|
|
|
|
|
|
with self.assertRaisesRegex(TypeError, "Num got multiple values for argument 'n'"): |
|
|
|
ast.Num(1, n=2) |
|
|
|
with self.assertRaisesRegex(TypeError, "Constant got multiple values for argument 'value'"): |
|
|
|
ast.Constant(1, value=2) |
|
|
|
|
|
|
|
self.assertEqual(ast.Num(42).n, 42) |
|
|
|
self.assertEqual(ast.Num(4.25).n, 4.25) |
|
|
|
self.assertEqual(ast.Num(4.25j).n, 4.25j) |
|
|
|
|
|
|
|
@ -0,0 +1,2 @@ |
|
|
|
AST nodes are now raising :exc:`TypeError` on conflicting keyword arguments. |
|
|
|
Patch contributed by Rémi Lapeyre. |
|
|
|
@ -695,8 +695,9 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) |
|
|
|
} |
|
|
|
if (fields) { |
|
|
|
numfields = PySequence_Size(fields); |
|
|
|
if (numfields == -1) |
|
|
|
if (numfields == -1) { |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
res = 0; /* if no error occurs, this stays 0 to the end */ |
|
|
|
@ -717,15 +718,35 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) |
|
|
|
} |
|
|
|
res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i)); |
|
|
|
Py_DECREF(name); |
|
|
|
if (res < 0) |
|
|
|
if (res < 0) { |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
} |
|
|
|
if (kw) { |
|
|
|
i = 0; /* needed by PyDict_Next */ |
|
|
|
while (PyDict_Next(kw, &i, &key, &value)) { |
|
|
|
int contains = PySequence_Contains(fields, key); |
|
|
|
if (contains == -1) { |
|
|
|
res = -1; |
|
|
|
goto cleanup; |
|
|
|
} else if (contains == 1) { |
|
|
|
Py_ssize_t p = PySequence_Index(fields, key); |
|
|
|
if (p == -1) { |
|
|
|
res = -1; |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
if (p < PyTuple_GET_SIZE(args)) { |
|
|
|
PyErr_Format(PyExc_TypeError, |
|
|
|
"%.400s got multiple values for argument '%U'", |
|
|
|
Py_TYPE(self)->tp_name, key); |
|
|
|
res = -1; |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
} |
|
|
|
res = PyObject_SetAttr(self, key, value); |
|
|
|
if (res < 0) |
|
|
|
if (res < 0) { |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
cleanup: |
|
|
|
|
|
|
|
@ -1131,8 +1131,9 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) |
|
|
|
} |
|
|
|
if (fields) { |
|
|
|
numfields = PySequence_Size(fields); |
|
|
|
if (numfields == -1) |
|
|
|
if (numfields == -1) { |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
res = 0; /* if no error occurs, this stays 0 to the end */ |
|
|
|
@ -1153,15 +1154,35 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) |
|
|
|
} |
|
|
|
res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i)); |
|
|
|
Py_DECREF(name); |
|
|
|
if (res < 0) |
|
|
|
if (res < 0) { |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
} |
|
|
|
if (kw) { |
|
|
|
i = 0; /* needed by PyDict_Next */ |
|
|
|
while (PyDict_Next(kw, &i, &key, &value)) { |
|
|
|
int contains = PySequence_Contains(fields, key); |
|
|
|
if (contains == -1) { |
|
|
|
res = -1; |
|
|
|
goto cleanup; |
|
|
|
} else if (contains == 1) { |
|
|
|
Py_ssize_t p = PySequence_Index(fields, key); |
|
|
|
if (p == -1) { |
|
|
|
res = -1; |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
if (p < PyTuple_GET_SIZE(args)) { |
|
|
|
PyErr_Format(PyExc_TypeError, |
|
|
|
"%.400s got multiple values for argument '%U'", |
|
|
|
Py_TYPE(self)->tp_name, key); |
|
|
|
res = -1; |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
} |
|
|
|
res = PyObject_SetAttr(self, key, value); |
|
|
|
if (res < 0) |
|
|
|
if (res < 0) { |
|
|
|
goto cleanup; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
cleanup: |
|
|
|
|