https://github.com/python/cpython/commit/7ae9a479cf1681503e896a3094cfc9122f05cb2e
commit: 7ae9a479cf1681503e896a3094cfc9122f05cb2e
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: vstinner <[email protected]>
date: 2026-01-26T12:14:39Z
summary:

[3.14] gh-144169: Fix three crashes in AST objects with non-str kwargs 
(GH-144178) (#144227)

gh-144169: Fix three crashes in AST objects with non-str kwargs (GH-144178)
(cherry picked from commit 639c1ad4f1ef5c2409a62fa8ed16e6aa3a6f9ab8)

Co-authored-by: Jelle Zijlstra <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-01-23-06-43-21.gh-issue-144169.LFy9yi.rst
M Lib/test/test_ast/test_ast.py
M Parser/asdl_c.py
M Python/Python-ast.c

diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py
index 5b4045ff4201e3..ef12ce0ce37de8 100644
--- a/Lib/test/test_ast/test_ast.py
+++ b/Lib/test/test_ast/test_ast.py
@@ -1419,6 +1419,13 @@ def test_replace_reject_unknown_instance_fields(self):
         self.assertIs(node.ctx, context)
         self.assertRaises(AttributeError, getattr, node, 'unknown')
 
+    def test_replace_non_str_kwarg(self):
+        node = ast.Name(id="x")
+        errmsg = "got an unexpected keyword argument <object object"
+        with self.assertRaisesRegex(TypeError, errmsg):
+            node.__replace__(**{object(): "y"})
+
+
 class ASTHelpers_Test(unittest.TestCase):
     maxDiff = None
 
@@ -3281,6 +3288,27 @@ class _AllFieldTypes(ast.AST):
         self.assertIs(obj.a, None)
         self.assertEqual(obj.b, [])
 
+    def test_non_str_kwarg(self):
+        warn_msg = "got an unexpected keyword argument <object object"
+        with (
+            self.assertRaises(TypeError),
+            self.assertWarnsRegex(DeprecationWarning, warn_msg),
+        ):
+            ast.Name(**{object(): 'y'})
+
+        class FakeStr:
+            def __init__(self, value):
+                self.value = value
+
+            def __hash__(self):
+                return hash(self.value)
+
+            def __eq__(self, other):
+                return isinstance(other, str) and self.value == other
+
+        with self.assertRaisesRegex(TypeError, "got multiple values for 
argument"):
+            ast.Name("x", **{FakeStr('id'): 'y'})
+
 
 @support.cpython_only
 class ModuleStateTests(unittest.TestCase):
diff --git 
a/Misc/NEWS.d/next/Library/2026-01-23-06-43-21.gh-issue-144169.LFy9yi.rst 
b/Misc/NEWS.d/next/Library/2026-01-23-06-43-21.gh-issue-144169.LFy9yi.rst
new file mode 100644
index 00000000000000..e2ef3d7c051d14
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-01-23-06-43-21.gh-issue-144169.LFy9yi.rst
@@ -0,0 +1,2 @@
+Fix three crashes when non-string keyword arguments are supplied to objects
+in the :mod:`ast` module.
diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py
index 3e252cbc4883d1..5ad20d49fa4b31 100755
--- a/Parser/asdl_c.py
+++ b/Parser/asdl_c.py
@@ -942,7 +942,7 @@ def visitModule(self, mod):
                 }
                 if (p == 0) {
                     PyErr_Format(PyExc_TypeError,
-                        "%.400s got multiple values for argument '%U'",
+                        "%.400s got multiple values for argument %R",
                         Py_TYPE(self)->tp_name, key);
                     res = -1;
                     goto cleanup;
@@ -965,7 +965,7 @@ def visitModule(self, mod):
                 else if (contains == 0) {
                     if (PyErr_WarnFormat(
                         PyExc_DeprecationWarning, 1,
-                        "%.400s.__init__ got an unexpected keyword argument 
'%U'. "
+                        "%.400s.__init__ got an unexpected keyword argument 
%R. "
                         "Support for arbitrary keyword arguments is deprecated 
"
                         "and will be removed in Python 3.15.",
                         Py_TYPE(self)->tp_name, key
@@ -1207,7 +1207,7 @@ def visitModule(self, mod):
             if (rc == 0) {
                 PyErr_Format(PyExc_TypeError,
                              "%.400s.__replace__ got an unexpected keyword "
-                             "argument '%U'.", Py_TYPE(self)->tp_name, key);
+                             "argument %R.", Py_TYPE(self)->tp_name, key);
                 Py_DECREF(expecting);
                 return -1;
             }
diff --git a/Python/Python-ast.c b/Python/Python-ast.c
index 79608dee9bfac2..1cc88dc179e120 100644
--- a/Python/Python-ast.c
+++ b/Python/Python-ast.c
@@ -5226,7 +5226,7 @@ ast_type_init(PyObject *self, PyObject *args, PyObject 
*kw)
                 }
                 if (p == 0) {
                     PyErr_Format(PyExc_TypeError,
-                        "%.400s got multiple values for argument '%U'",
+                        "%.400s got multiple values for argument %R",
                         Py_TYPE(self)->tp_name, key);
                     res = -1;
                     goto cleanup;
@@ -5249,7 +5249,7 @@ ast_type_init(PyObject *self, PyObject *args, PyObject 
*kw)
                 else if (contains == 0) {
                     if (PyErr_WarnFormat(
                         PyExc_DeprecationWarning, 1,
-                        "%.400s.__init__ got an unexpected keyword argument 
'%U'. "
+                        "%.400s.__init__ got an unexpected keyword argument 
%R. "
                         "Support for arbitrary keyword arguments is deprecated 
"
                         "and will be removed in Python 3.15.",
                         Py_TYPE(self)->tp_name, key
@@ -5491,7 +5491,7 @@ ast_type_replace_check(PyObject *self,
             if (rc == 0) {
                 PyErr_Format(PyExc_TypeError,
                              "%.400s.__replace__ got an unexpected keyword "
-                             "argument '%U'.", Py_TYPE(self)->tp_name, key);
+                             "argument %R.", Py_TYPE(self)->tp_name, key);
                 Py_DECREF(expecting);
                 return -1;
             }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to