Skip to content

gh-132284: Don't wrap base PyCFunction slots on class creation if not overridden #132329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 17, 2025
Merged
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,10 @@ Other language changes
of HMAC is not available.
(Contributed by Bénédikt Tran in :gh:`99108`.)

* When subclassing from a pure C type, the C slots for the new type are no
longer replaced with a wrapped version on class creation if they are not
explicitly overridden in the subclass.
(Contributed by Tomasz Pytel in :gh:`132329`.)

.. _whatsnew314-pep765:

Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1838,6 +1838,23 @@ class Model(metaclass=ModelBase):
with self.assertRaises(RuntimeWarning):
type("SouthPonies", (Model,), {})

def test_subclass_inherited_slot_update(self):
# gh-132284: Make sure slot update still works after fix.
# Note that after assignment to D.__getitem__ the actual C slot will
# never go back to dict_subscript as it was on class type creation but
# rather be set to slot_mp_subscript, unfortunately there is no way to
# check that here.

class D(dict):
pass

d = D({None: None})
self.assertIs(d[None], None)
D.__getitem__ = lambda self, item: 42
self.assertEqual(d[None], 42)
D.__getitem__ = dict.__getitem__
self.assertIs(d[None], None)

def test_tuple_subclass_as_bases(self):
# gh-132176: it used to crash on using
# tuple subclass for as base classes.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Don't wrap base ``PyCFunction`` slots on class creation if not overridden.
9 changes: 8 additions & 1 deletion Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -11237,7 +11237,14 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
}
else {
use_generic = 1;
generic = p->function;
if (generic == NULL && Py_IS_TYPE(descr, &PyMethodDescr_Type) &&
*ptr == ((PyMethodDescrObject *)descr)->d_method->ml_meth)
{
generic = *ptr;
}
else {
generic = p->function;
}
if (p->function == slot_tp_call) {
/* A generic __call__ is incompatible with vectorcall */
type_clear_flags(type, Py_TPFLAGS_HAVE_VECTORCALL);
Expand Down
Loading