]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-132284: Don't wrap base PyCFunction slots on class creation if not overridden...
authorTomasz Pytel <tompytel@gmail.com>
Thu, 17 Apr 2025 17:08:59 +0000 (13:08 -0400)
committerGitHub <noreply@github.com>
Thu, 17 Apr 2025 17:08:59 +0000 (18:08 +0100)
Doc/whatsnew/3.14.rst
Lib/test/test_types.py
Misc/NEWS.d/next/Core_and_Builtins/2025-04-09-20-49-04.gh-issue-132284.TxTNka.rst [new file with mode: 0644]
Objects/typeobject.c

index c50d1669fef84c22cbc07560b181ccd1cf549d7d..7d469e83dc27ad4da211e5fe6338c8e4f0d93f73 100644 (file)
@@ -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:
 
index 87081a6db4ea48021188d007b93cb633f3038e0d..e5d80ee8eb7aca89ef840f175db03de201238dab 100644 (file)
@@ -1838,6 +1838,23 @@ class ClassCreationTests(unittest.TestCase):
         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.
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-09-20-49-04.gh-issue-132284.TxTNka.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-09-20-49-04.gh-issue-132284.TxTNka.rst
new file mode 100644 (file)
index 0000000..b63a75f
--- /dev/null
@@ -0,0 +1 @@
+Don't wrap base ``PyCFunction`` slots on class creation if not overridden.
index f65695360a7483dd79a6e4fa524f81f2c3bee425..982f41fd47f92c45547f053ec907fb3bdda6ab24 100644 (file)
@@ -11233,7 +11233,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);