Skip to content

Commit a4ca6e8

Browse files
ashm-devvstinner
andauthored
gh-156762: Fix tp_clear slot signature for operator.methodcaller() (#156769)
Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent fb46c67 commit a4ca6e8

3 files changed

Lines changed: 22 additions & 1 deletion

File tree

Lib/test/test_operator.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import inspect
33
import pickle
44
import sys
5+
import weakref
56
from decimal import Decimal
67
from fractions import Fraction
78

@@ -511,6 +512,21 @@ def return_arguments(self, *args, **kwds):
511512
f = operator.methodcaller('return_arguments', *many_positional_arguments, **many_kw_arguments)
512513
self.assertEqual(f(a), (many_positional_arguments, many_kw_arguments))
513514

515+
def test_methodcaller_cyclic_gc(self):
516+
# gh-156762: Check for undefined behavior on calling methodcaller_clear()
517+
operator = self.module
518+
519+
class C:
520+
pass
521+
522+
c = C()
523+
ref = weakref.ref(c)
524+
c.m = operator.methodcaller('foo', c)
525+
del c
526+
527+
support.gc_collect()
528+
self.assertIsNone(ref())
529+
514530
def test_inplace(self):
515531
operator = self.module
516532
class C(object):
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix undefined behaviour in :class:`operator.methodcaller`: its
2+
:c:member:`~PyTypeObject.tp_clear` slot function returned ``void`` instead of
3+
``int``, so the garbage collector called it through an incompatible function
4+
type. Patched by Shamil Abdulaev.

Modules/_operator.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1740,7 +1740,7 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
17401740
return (PyObject *)mc;
17411741
}
17421742

1743-
static void
1743+
static int
17441744
methodcaller_clear(PyObject *op)
17451745
{
17461746
methodcallerobject *mc = methodcallerobject_CAST(op);
@@ -1749,6 +1749,7 @@ methodcaller_clear(PyObject *op)
17491749
Py_CLEAR(mc->kwds);
17501750
Py_CLEAR(mc->vectorcall_args);
17511751
Py_CLEAR(mc->vectorcall_kwnames);
1752+
return 0;
17521753
}
17531754

17541755
static void

0 commit comments

Comments
 (0)