From 4c78c527d215c37472145152cb0e95f196cdddc9 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 24 Feb 2017 02:48:17 +0900 Subject: [PATCH] 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. --- Lib/test/test_ast.py | 4 ---- Parser/asdl_c.py | 36 +++++++++++++++++------------------- Python/Python-ast.c | 36 +++++++++++++++++------------------- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index ac79989796d..366269fe405 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -373,12 +373,8 @@ class AST_Tests(unittest.TestCase): self.assertEqual(x.right, 3) 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 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 self.assertRaises(TypeError, ast.BinOp, 1, 2, 3, 4, lineno=0) diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 444cdea341d..096f5f8c749 100644 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -664,29 +664,27 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) if (numfields == -1) goto cleanup; } + res = 0; /* if no error occurs, this stays 0 to the end */ - if (PyTuple_GET_SIZE(args) > 0) { - if (numfields != PyTuple_GET_SIZE(args)) { - PyErr_Format(PyExc_TypeError, "%.400s constructor takes %s" - "%zd positional argument%s", - Py_TYPE(self)->tp_name, - numfields == 0 ? "" : "either 0 or ", - numfields, numfields == 1 ? "" : "s"); + if (numfields < PyTuple_GET_SIZE(args)) { + PyErr_Format(PyExc_TypeError, "%.400s constructor takes at most " + "%zd positional argument%s", + Py_TYPE(self)->tp_name, + numfields, numfields == 1 ? "" : "s"); + res = -1; + 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; 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; - goto cleanup; - } - res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i)); - Py_DECREF(name); - if (res < 0) - goto cleanup; - } + res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i)); + Py_DECREF(name); + if (res < 0) + goto cleanup; } if (kw) { i = 0; /* needed by PyDict_Next */ diff --git a/Python/Python-ast.c b/Python/Python-ast.c index a6a49f78b1e..2759b2fe9c4 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -551,29 +551,27 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) if (numfields == -1) goto cleanup; } + res = 0; /* if no error occurs, this stays 0 to the end */ - if (PyTuple_GET_SIZE(args) > 0) { - if (numfields != PyTuple_GET_SIZE(args)) { - PyErr_Format(PyExc_TypeError, "%.400s constructor takes %s" - "%zd positional argument%s", - Py_TYPE(self)->tp_name, - numfields == 0 ? "" : "either 0 or ", - numfields, numfields == 1 ? "" : "s"); + if (numfields < PyTuple_GET_SIZE(args)) { + PyErr_Format(PyExc_TypeError, "%.400s constructor takes at most " + "%zd positional argument%s", + Py_TYPE(self)->tp_name, + numfields, numfields == 1 ? "" : "s"); + res = -1; + 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; 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; - goto cleanup; - } - res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i)); - Py_DECREF(name); - if (res < 0) - goto cleanup; - } + res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i)); + Py_DECREF(name); + if (res < 0) + goto cleanup; } if (kw) { i = 0; /* needed by PyDict_Next */