https://github.com/python/cpython/commit/64862112b7beb005523e7bca15c59c78c5319e58
commit: 64862112b7beb005523e7bca15c59c78c5319e58
branch: main
author: bkap123 <[email protected]>
committer: encukou <[email protected]>
date: 2026-03-18T13:46:01+01:00
summary:

gh-146075: Prevent crash in `functools.partial()` from malformed `str` subclass 
(GH-146078)

In `partial_vectorcall`, an error returned by `PyDict_Contains` was
considered to be a truthy value. Now, the error is handled
appropriately.

files:
A Misc/NEWS.d/next/Library/2026-03-17-19-30-45.gh-issue-146075.85sCSh.rst
M Lib/test/test_functools.py
M Modules/_functoolsmodule.c

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) {

_______________________________________________
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