Skip to content

Commit

Permalink
[3.12] gh-117482: Fix Builtin Types Slot Wrappers (gh-121632)
Browse files Browse the repository at this point in the history
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.

(cherry picked from commit 5250a03, AKA gh-121602)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
  • Loading branch information
ericsnowcurrently authored Jul 11, 2024
1 parent 5492f84 commit 0ec761a
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 9 deletions.
35 changes: 35 additions & 0 deletions Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import pickle
import locale
import sys
import textwrap
import types
import unittest.mock
import weakref
Expand Down Expand Up @@ -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()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Unexpected slot wrappers are no longer created for builtin static types in
subinterpreters.
41 changes: 32 additions & 9 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions Tools/c-analyzer/cpython/globals-to-fix.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 0ec761a

Please sign in to comment.