From 66aa3e6272360439a1a200fd30f4e609ccd78bda Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Jul 2024 14:20:14 -0600 Subject: [PATCH 1/2] 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. --- Lib/test/test_types.py | 35 ++++++++++++++++ ...-07-10-15-43-54.gh-issue-117482.5WYaXR.rst | 2 + Objects/typeobject.c | 41 +++++++++++++++---- 3 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-07-10-15-43-54.gh-issue-117482.5WYaXR.rst diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 5ffe4085f09548..ca41c76649ca99 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -9,6 +9,7 @@ import pickle import locale import sys +import textwrap import types import unittest.mock import weakref @@ -2252,5 +2253,39 @@ def coro(): 'close', 'throw'})) +class SubinterpreterTests(unittest.TestCase): + + @classmethod + def setUpClass(cls): + global interpreters + try: + from test.support import interpreters + except ModuleNotFoundError: + raise unittest.SkipTest('subinterpreters required') + + @cpython_only + def test_slot_wrappers(self): + rch, sch = interpreters.create_channel() + + # 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(f''' + text = repr(int.__str__) + sch = interpreters.SendChannel({sch.id}) + sch.send_nowait(text) + ''') + + exec(script) + expected = rch.recv() + + interp = interpreters.create() + interp.run('from test.support import interpreters') + interp.run(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 9d05798e1715b4..1436b225f375a0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -116,6 +116,18 @@ static_builtin_index_clear(PyTypeObject *self) self->tp_subclasses = NULL; } + +/* In 3.13+ this is stored in _PyRuntimeState. */ +static PyTypeObject static_type_defs[_Py_MAX_STATIC_BUILTIN_TYPES]; + +static inline PyTypeObject * +static_builtin_get_def(PyTypeObject *type) +{ + size_t index = static_builtin_index_get(type); + return &static_type_defs[index]; +} + + static inline static_builtin_state * static_builtin_state_get(PyInterpreterState *interp, PyTypeObject *self) { @@ -6982,7 +6994,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) @@ -7147,10 +7159,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) { @@ -7462,7 +7474,7 @@ type_ready_post_checks(PyTypeObject *type) static int -type_ready(PyTypeObject *type, int rerunbuiltin) +type_ready(PyTypeObject *type, PyTypeObject *def, int rerunbuiltin) { _PyObject_ASSERT((PyObject *)type, !is_readying(type)); start_readying(type); @@ -7499,7 +7511,7 @@ type_ready(PyTypeObject *type, int rerunbuiltin) if (type_ready_set_new(type, rerunbuiltin) < 0) { goto error; } - if (type_ready_fill_dict(type) < 0) { + if (type_ready_fill_dict(type, def) < 0) { goto error; } if (!rerunbuiltin) { @@ -7551,7 +7563,7 @@ PyType_Ready(PyTypeObject *type) type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; } - return type_ready(type, 0); + return type_ready(type, NULL, 0); } int @@ -7581,10 +7593,16 @@ _PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self) static_builtin_state_init(interp, self); - int res = type_ready(self, !ismain); + PyTypeObject *def = static_builtin_get_def(self); + if (ismain) { + memcpy(def, self, sizeof(PyTypeObject)); + } + + int res = type_ready(self, def, !ismain); if (res < 0) { static_builtin_state_clear(interp, self); } + return res; } @@ -10108,17 +10126,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); From f7977c54af348bd31bb0c9d62113c34a38f1f6c2 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Jul 2024 15:00:08 -0600 Subject: [PATCH 2/2] Ignore the global variable. --- Tools/c-analyzer/cpython/globals-to-fix.tsv | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index ac60ee4e1416a7..62b5d882965b13 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -305,6 +305,11 @@ Objects/sliceobject.c - _Py_EllipsisObject - Python/instrumentation.c - _PyInstrumentation_DISABLE - Python/instrumentation.c - _PyInstrumentation_MISSING - +##----------------------- +## other + +Objects/typeobject.c - static_type_defs - + ################################## ## global non-objects to fix in core code