https://github.com/python/cpython/commit/a4ca6e8d4b5d20b18e0f0eece32bd30968db7470
commit: a4ca6e8d4b5d20b18e0f0eece32bd30968db7470
branch: main
author: Shamil <[email protected]>
committer: vstinner <[email protected]>
date: 2026-09-13T18:57:17Z
summary:

gh-156762: Fix tp_clear slot signature for operator.methodcaller() (#156769)

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

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-43-24.gh-issue-156762.Kbf2mo.rst
M Lib/test/test_operator.py
M Modules/_operator.c

diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py
index 1f89986c777ced8..68c8aadeb50823b 100644
--- a/Lib/test/test_operator.py
+++ b/Lib/test/test_operator.py
@@ -2,6 +2,7 @@
 import inspect
 import pickle
 import sys
+import weakref
 from decimal import Decimal
 from fractions import Fraction
 
@@ -511,6 +512,21 @@ def return_arguments(self, *args, **kwds):
         f = operator.methodcaller('return_arguments', 
*many_positional_arguments, **many_kw_arguments)
         self.assertEqual(f(a), (many_positional_arguments, many_kw_arguments))
 
+    def test_methodcaller_cyclic_gc(self):
+        # gh-156762: Check for undefined behavior on calling 
methodcaller_clear()
+        operator = self.module
+
+        class C:
+            pass
+
+        c = C()
+        ref = weakref.ref(c)
+        c.m = operator.methodcaller('foo', c)
+        del c
+
+        support.gc_collect()
+        self.assertIsNone(ref())
+
     def test_inplace(self):
         operator = self.module
         class C(object):
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-43-24.gh-issue-156762.Kbf2mo.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-43-24.gh-issue-156762.Kbf2mo.rst
new file mode 100644
index 000000000000000..f6f9bb038f4909d
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-13-10-43-24.gh-issue-156762.Kbf2mo.rst
@@ -0,0 +1,4 @@
+Fix undefined behaviour in :class:`operator.methodcaller`: its
+:c:member:`~PyTypeObject.tp_clear` slot function returned ``void`` instead of
+``int``, so the garbage collector called it through an incompatible function
+type. Patched by Shamil Abdulaev.
diff --git a/Modules/_operator.c b/Modules/_operator.c
index 417403dc4c10c11..a0843971efe13e6 100644
--- a/Modules/_operator.c
+++ b/Modules/_operator.c
@@ -1740,7 +1740,7 @@ methodcaller_new(PyTypeObject *type, PyObject *args, 
PyObject *kwds)
     return (PyObject *)mc;
 }
 
-static void
+static int
 methodcaller_clear(PyObject *op)
 {
     methodcallerobject *mc = methodcallerobject_CAST(op);
@@ -1749,6 +1749,7 @@ methodcaller_clear(PyObject *op)
     Py_CLEAR(mc->kwds);
     Py_CLEAR(mc->vectorcall_args);
     Py_CLEAR(mc->vectorcall_kwnames);
+    return 0;
 }
 
 static void

_______________________________________________
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