[Python-checkins] (no subject)

Rémi Lapeyre webhook-mailer at python.org
Sun May 24 17:13:01 EDT 2020




To: python-checkins at python.org
Subject: bpo-36290: Fix keytword collision handling in AST node constructors
 (GH-12382)
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0

https://github.com/python/cpython/commit/c73914a562580ae72048876cb42ed8e76e2c=
83f9
commit: c73914a562580ae72048876cb42ed8e76e2c83f9
branch: master
author: R=C3=A9mi Lapeyre <remi.lapeyre at lenstra.fr>
committer: GitHub <noreply at github.com>
date: 2020-05-24T22:12:57+01:00
summary:

bpo-36290: Fix keytword collision handling in AST node constructors (GH-12382)

files:
A Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rst
M Lib/ast.py
M Lib/test/test_ast.py
M Parser/asdl_c.py
M Python/Python-ast.c

diff --git a/Lib/ast.py b/Lib/ast.py
index 52e51b4858774..6a5b39e270b9b 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -524,6 +524,13 @@ def __instancecheck__(cls, inst):
         return type.__instancecheck__(cls, inst)
=20
 def _new(cls, *args, **kwargs):
+    for key in kwargs:
+        if key not in cls._fields:
+            # arbitrary keyword arguments are accepted
+            continue
+        pos =3D cls._fields.index(key)
+        if pos < len(args):
+            raise TypeError(f"{cls.__name__} got multiple values for argumen=
t {key!r}")
     if cls in _const_types:
         return Constant(*args, **kwargs)
     return Constant.__new__(cls, *args, **kwargs)
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index e55d10badc37e..3e9c8b55cdff4 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -402,6 +402,15 @@ def test_classattrs(self):
         self.assertRaises(TypeError, ast.Num, 1, None, 2)
         self.assertRaises(TypeError, ast.Num, 1, None, 2, lineno=3D0)
=20
+        # Arbitrary keyword arguments are supported
+        self.assertEqual(ast.Constant(1, foo=3D'bar').foo, 'bar')
+        self.assertEqual(ast.Num(1, foo=3D'bar').foo, 'bar')
+
+        with self.assertRaisesRegex(TypeError, "Num got multiple values for =
argument 'n'"):
+            ast.Num(1, n=3D2)
+        with self.assertRaisesRegex(TypeError, "Constant got multiple values=
 for argument 'value'"):
+            ast.Constant(1, value=3D2)
+
         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)
diff --git a/Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rs=
t b/Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rst
new file mode 100644
index 0000000000000..a9afe62b0c46e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-03-17-19-01-53.bpo-36290.7VXo_K.rst
@@ -0,0 +1,2 @@
+AST nodes are now raising :exc:`TypeError` on conflicting keyword arguments.
+Patch contributed by R=C3=A9mi Lapeyre.
diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py
index 6d572755e68e8..f8729cd170b10 100755
--- a/Parser/asdl_c.py
+++ b/Parser/asdl_c.py
@@ -695,8 +695,9 @@ def visitModule(self, mod):
     }
     if (fields) {
         numfields =3D PySequence_Size(fields);
-        if (numfields =3D=3D -1)
+        if (numfields =3D=3D -1) {
             goto cleanup;
+        }
     }
=20
     res =3D 0; /* if no error occurs, this stays 0 to the end */
@@ -717,15 +718,35 @@ def visitModule(self, mod):
         }
         res =3D PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
         Py_DECREF(name);
-        if (res < 0)
+        if (res < 0) {
             goto cleanup;
+        }
     }
     if (kw) {
         i =3D 0;  /* needed by PyDict_Next */
         while (PyDict_Next(kw, &i, &key, &value)) {
+            int contains =3D PySequence_Contains(fields, key);
+            if (contains =3D=3D -1) {
+                res =3D -1;
+                goto cleanup;
+            } else if (contains =3D=3D 1) {
+                Py_ssize_t p =3D PySequence_Index(fields, key);
+                if (p =3D=3D -1) {
+                    res =3D -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 =3D -1;
+                    goto cleanup;
+                }
+            }
             res =3D PyObject_SetAttr(self, key, value);
-            if (res < 0)
+            if (res < 0) {
                 goto cleanup;
+            }
         }
     }
   cleanup:
diff --git a/Python/Python-ast.c b/Python/Python-ast.c
index f34b1450c66ef..d2edf74c81216 100644
--- a/Python/Python-ast.c
+++ b/Python/Python-ast.c
@@ -1131,8 +1131,9 @@ ast_type_init(PyObject *self, PyObject *args, PyObject =
*kw)
     }
     if (fields) {
         numfields =3D PySequence_Size(fields);
-        if (numfields =3D=3D -1)
+        if (numfields =3D=3D -1) {
             goto cleanup;
+        }
     }
=20
     res =3D 0; /* if no error occurs, this stays 0 to the end */
@@ -1153,15 +1154,35 @@ ast_type_init(PyObject *self, PyObject *args, PyObjec=
t *kw)
         }
         res =3D PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
         Py_DECREF(name);
-        if (res < 0)
+        if (res < 0) {
             goto cleanup;
+        }
     }
     if (kw) {
         i =3D 0;  /* needed by PyDict_Next */
         while (PyDict_Next(kw, &i, &key, &value)) {
+            int contains =3D PySequence_Contains(fields, key);
+            if (contains =3D=3D -1) {
+                res =3D -1;
+                goto cleanup;
+            } else if (contains =3D=3D 1) {
+                Py_ssize_t p =3D PySequence_Index(fields, key);
+                if (p =3D=3D -1) {
+                    res =3D -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 =3D -1;
+                    goto cleanup;
+                }
+            }
             res =3D PyObject_SetAttr(self, key, value);
-            if (res < 0)
+            if (res < 0) {
                 goto cleanup;
+            }
         }
     }
   cleanup:



More information about the Python-checkins mailing list