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]
