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]