diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index dda42cb33072c3..efa85b564f7cdf 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -565,7 +565,19 @@ def __repr__(self): g_partial = functools.partial(func, trigger, None, None, None, None, arg=None) self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), EvilObject, None, None, None, None, arg=None)") + def test_str_subclass_error(self): + class BadStr(str): + def __eq__(self, other): + raise RuntimeError + def __hash__(self): + return str.__hash__(self) + + def f(**kwargs): + return kwargs + p = functools.partial(f, poison="") + with self.assertRaises(RuntimeError): + result = p(**{BadStr("poison"): "new_value"}) @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialC(TestPartial, unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-03-17-19-30-45.gh-issue-146075.85sCSh.rst b/Misc/NEWS.d/next/Library/2026-03-17-19-30-45.gh-issue-146075.85sCSh.rst new file mode 100644 index 00000000000000..792ea3ad668690 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-17-19-30-45.gh-issue-146075.85sCSh.rst @@ -0,0 +1 @@ +Errors when calling :func:`functools.partial` with a malformed keyword will no longer crash the interpreter. diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 723080ede1d9ae..576494e846ca0c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -457,7 +457,11 @@ partial_vectorcall(PyObject *self, PyObject *const *args, for (Py_ssize_t i = 0; i < nkwds; ++i) { key = PyTuple_GET_ITEM(kwnames, i); val = args[nargs + i]; - if (PyDict_Contains(pto->kw, key)) { + int contains = PyDict_Contains(pto->kw, key); + if (contains < 0) { + goto error; + } + else if (contains == 1) { if (pto_kw_merged == NULL) { pto_kw_merged = PyDict_Copy(pto->kw); if (pto_kw_merged == NULL) {