bpo-29622: Make AST constructor to accept less than enough number of positional arguments (GH-249)

bpo-29463 added optional "docstring" field to 4 AST types.
While it is optional, it breaks backward compatibility because AST constructor
requires number of positional argument is same to number of fields.

AST types accepts empty arguments, and incomplete keyword arguments.
But it's not big problem because field can be filled after creation, and checked when compiling.
So stop requiring complete set of fields for positional arguments too.
This commit is contained in:
INADA Naoki 2017-02-24 02:48:17 +09:00 committed by GitHub
parent 561ca80cff
commit 4c78c527d2
3 changed files with 34 additions and 42 deletions

View File

@ -373,12 +373,8 @@ class AST_Tests(unittest.TestCase):
self.assertEqual(x.right, 3) self.assertEqual(x.right, 3)
self.assertEqual(x.lineno, 0) self.assertEqual(x.lineno, 0)
# node raises exception when not given enough arguments
self.assertRaises(TypeError, ast.BinOp, 1, 2)
# node raises exception when given too many arguments # node raises exception when given too many arguments
self.assertRaises(TypeError, ast.BinOp, 1, 2, 3, 4) self.assertRaises(TypeError, ast.BinOp, 1, 2, 3, 4)
# node raises exception when not given enough arguments
self.assertRaises(TypeError, ast.BinOp, 1, 2, lineno=0)
# node raises exception when given too many arguments # node raises exception when given too many arguments
self.assertRaises(TypeError, ast.BinOp, 1, 2, 3, 4, lineno=0) self.assertRaises(TypeError, ast.BinOp, 1, 2, 3, 4, lineno=0)

View File

@ -664,29 +664,27 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
if (numfields == -1) if (numfields == -1)
goto cleanup; goto cleanup;
} }
res = 0; /* if no error occurs, this stays 0 to the end */ res = 0; /* if no error occurs, this stays 0 to the end */
if (PyTuple_GET_SIZE(args) > 0) { if (numfields < PyTuple_GET_SIZE(args)) {
if (numfields != PyTuple_GET_SIZE(args)) { PyErr_Format(PyExc_TypeError, "%.400s constructor takes at most "
PyErr_Format(PyExc_TypeError, "%.400s constructor takes %s" "%zd positional argument%s",
"%zd positional argument%s", Py_TYPE(self)->tp_name,
Py_TYPE(self)->tp_name, numfields, numfields == 1 ? "" : "s");
numfields == 0 ? "" : "either 0 or ", res = -1;
numfields, numfields == 1 ? "" : "s"); goto cleanup;
}
for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
/* cannot be reached when fields is NULL */
PyObject *name = PySequence_GetItem(fields, i);
if (!name) {
res = -1; res = -1;
goto cleanup; goto cleanup;
} }
for (i = 0; i < PyTuple_GET_SIZE(args); i++) { res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
/* cannot be reached when fields is NULL */ Py_DECREF(name);
PyObject *name = PySequence_GetItem(fields, i); if (res < 0)
if (!name) { goto cleanup;
res = -1;
goto cleanup;
}
res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
Py_DECREF(name);
if (res < 0)
goto cleanup;
}
} }
if (kw) { if (kw) {
i = 0; /* needed by PyDict_Next */ i = 0; /* needed by PyDict_Next */

View File

@ -551,29 +551,27 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
if (numfields == -1) if (numfields == -1)
goto cleanup; goto cleanup;
} }
res = 0; /* if no error occurs, this stays 0 to the end */ res = 0; /* if no error occurs, this stays 0 to the end */
if (PyTuple_GET_SIZE(args) > 0) { if (numfields < PyTuple_GET_SIZE(args)) {
if (numfields != PyTuple_GET_SIZE(args)) { PyErr_Format(PyExc_TypeError, "%.400s constructor takes at most "
PyErr_Format(PyExc_TypeError, "%.400s constructor takes %s" "%zd positional argument%s",
"%zd positional argument%s", Py_TYPE(self)->tp_name,
Py_TYPE(self)->tp_name, numfields, numfields == 1 ? "" : "s");
numfields == 0 ? "" : "either 0 or ", res = -1;
numfields, numfields == 1 ? "" : "s"); goto cleanup;
}
for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
/* cannot be reached when fields is NULL */
PyObject *name = PySequence_GetItem(fields, i);
if (!name) {
res = -1; res = -1;
goto cleanup; goto cleanup;
} }
for (i = 0; i < PyTuple_GET_SIZE(args); i++) { res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
/* cannot be reached when fields is NULL */ Py_DECREF(name);
PyObject *name = PySequence_GetItem(fields, i); if (res < 0)
if (!name) { goto cleanup;
res = -1;
goto cleanup;
}
res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
Py_DECREF(name);
if (res < 0)
goto cleanup;
}
} }
if (kw) { if (kw) {
i = 0; /* needed by PyDict_Next */ i = 0; /* needed by PyDict_Next */