https://github.com/python/cpython/commit/5250a031332eb9499d5fc190d7287642e5a144b9
commit: 5250a031332eb9499d5fc190d7287642e5a144b9
branch: main
author: Eric Snow <[email protected]>
committer: ericsnowcurrently <[email protected]>
date: 2024-07-11T20:20:14Z
summary:

gh-117482: Fix Builtin Types Slot Wrappers (gh-121602)

When builtin static types are initialized for a subinterpreter, various "tp" 
slots have already been inherited (for the main interpreter).  This was 
interfering with the logic in add_operators() (in Objects/typeobject.c), 
causing a wrapper to get created when it shouldn't.  This change fixes that by 
preserving the original data from the static type struct and checking that.

files:
A Misc/NEWS.d/next/Core and 
Builtins/2024-07-10-15-43-54.gh-issue-117482.5WYaXR.rst
M Include/internal/pycore_typeobject.h
M Lib/test/test_types.py
M Objects/typeobject.c

diff --git a/Include/internal/pycore_typeobject.h 
b/Include/internal/pycore_typeobject.h
index 32bd19d968b917..df6bfef715dd34 100644
--- a/Include/internal/pycore_typeobject.h
+++ b/Include/internal/pycore_typeobject.h
@@ -33,6 +33,7 @@ struct _types_runtime_state {
     struct {
         struct {
             PyTypeObject *type;
+            PyTypeObject def;
             int64_t interp_count;
         } types[_Py_MAX_MANAGED_STATIC_TYPES];
     } managed_static;
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index fbca198aab5180..38a98828085e2f 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -10,6 +10,7 @@
 import pickle
 import locale
 import sys
+import textwrap
 import types
 import unittest.mock
 import weakref
@@ -2345,5 +2346,40 @@ def ex(a, /, b, *, c):
             )
 
 
+class SubinterpreterTests(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        global interpreters
+        try:
+            from test.support import interpreters
+        except ModuleNotFoundError:
+            raise unittest.SkipTest('subinterpreters required')
+        import test.support.interpreters.channels
+
+    @cpython_only
+    def test_slot_wrappers(self):
+        rch, sch = interpreters.channels.create()
+
+        # For now it's sufficient to check int.__str__.
+        # See https://github.com/python/cpython/issues/117482
+        # and https://github.com/python/cpython/pull/117660.
+        script = textwrap.dedent('''
+            text = repr(int.__str__)
+            sch.send_nowait(text)
+            ''')
+
+        exec(script)
+        expected = rch.recv()
+
+        interp = interpreters.create()
+        interp.exec('from test.support import interpreters')
+        interp.prepare_main(sch=sch)
+        interp.exec(script)
+        results = rch.recv()
+
+        self.assertEqual(results, expected)
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core and 
Builtins/2024-07-10-15-43-54.gh-issue-117482.5WYaXR.rst b/Misc/NEWS.d/next/Core 
and Builtins/2024-07-10-15-43-54.gh-issue-117482.5WYaXR.rst
new file mode 100644
index 00000000000000..ec1e7327b77f19
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and 
Builtins/2024-07-10-15-43-54.gh-issue-117482.5WYaXR.rst 
@@ -0,0 +1,2 @@
+Unexpected slot wrappers are no longer created for builtin static types in
+subinterpreters.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 587632cecfba9d..7d01b680605a38 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -314,6 +314,16 @@ managed_static_type_state_clear(PyInterpreterState 
*interp, PyTypeObject *self,
     }
 }
 
+static PyTypeObject *
+managed_static_type_get_def(PyTypeObject *self, int isbuiltin)
+{
+    size_t index = managed_static_type_index_get(self);
+    size_t full_index = isbuiltin
+        ? index
+        : index + _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES;
+    return &_PyRuntime.types.managed_static.types[full_index].def;
+}
+
 // Also see _PyStaticType_InitBuiltin() and _PyStaticType_FiniBuiltin().
 
 /* end static builtin helpers */
@@ -5840,7 +5850,6 @@ fini_static_type(PyInterpreterState *interp, PyTypeObject 
*type,
 
     _PyStaticType_ClearWeakRefs(interp, type);
     managed_static_type_state_clear(interp, type, isbuiltin, final);
-    /* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
 }
 
 void
@@ -7850,7 +7859,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
     return 0;
 }
 
-static int add_operators(PyTypeObject *);
+static int add_operators(PyTypeObject *, PyTypeObject *);
 static int add_tp_new_wrapper(PyTypeObject *type);
 
 #define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
@@ -8015,10 +8024,10 @@ type_dict_set_doc(PyTypeObject *type)
 
 
 static int
-type_ready_fill_dict(PyTypeObject *type)
+type_ready_fill_dict(PyTypeObject *type, PyTypeObject *def)
 {
     /* Add type-specific descriptors to tp_dict */
-    if (add_operators(type) < 0) {
+    if (add_operators(type, def) < 0) {
         return -1;
     }
     if (type_add_methods(type) < 0) {
@@ -8337,7 +8346,7 @@ type_ready_post_checks(PyTypeObject *type)
 
 
 static int
-type_ready(PyTypeObject *type, int initial)
+type_ready(PyTypeObject *type, PyTypeObject *def, int initial)
 {
     ASSERT_TYPE_LOCK_HELD();
 
@@ -8376,7 +8385,7 @@ type_ready(PyTypeObject *type, int initial)
     if (type_ready_set_new(type, initial) < 0) {
         goto error;
     }
-    if (type_ready_fill_dict(type) < 0) {
+    if (type_ready_fill_dict(type, def) < 0) {
         goto error;
     }
     if (initial) {
@@ -8433,7 +8442,7 @@ PyType_Ready(PyTypeObject *type)
     int res;
     BEGIN_TYPE_LOCK();
     if (!(type->tp_flags & Py_TPFLAGS_READY)) {
-        res = type_ready(type, 1);
+        res = type_ready(type, NULL, 1);
     } else {
         res = 0;
         assert(_PyType_CheckConsistency(type));
@@ -8469,14 +8478,20 @@ init_static_type(PyInterpreterState *interp, 
PyTypeObject *self,
 
     managed_static_type_state_init(interp, self, isbuiltin, initial);
 
+    PyTypeObject *def = managed_static_type_get_def(self, isbuiltin);
+    if (initial) {
+        memcpy(def, self, sizeof(PyTypeObject));
+    }
+
     int res;
     BEGIN_TYPE_LOCK();
-    res = type_ready(self, initial);
+    res = type_ready(self, def, initial);
     END_TYPE_LOCK();
     if (res < 0) {
         _PyStaticType_ClearWeakRefs(interp, self);
         managed_static_type_state_clear(interp, self, isbuiltin, initial);
     }
+
     return res;
 }
 
@@ -11064,17 +11079,22 @@ recurse_down_subclasses(PyTypeObject *type, PyObject 
*attr_name,
    infinite recursion here.) */
 
 static int
-add_operators(PyTypeObject *type)
+add_operators(PyTypeObject *type, PyTypeObject *def)
 {
     PyObject *dict = lookup_tp_dict(type);
     pytype_slotdef *p;
     PyObject *descr;
     void **ptr;
 
+    assert(def == NULL || (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
+    if (def == NULL) {
+        def = type;
+    }
+
     for (p = slotdefs; p->name; p++) {
         if (p->wrapper == NULL)
             continue;
-        ptr = slotptr(type, p->offset);
+        ptr = slotptr(def, p->offset);
         if (!ptr || !*ptr)
             continue;
         int r = PyDict_Contains(dict, p->name_strobj);

_______________________________________________
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