Skip to content

Commit 5250a03

Browse files
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.
1 parent 58e8cf2 commit 5250a03

File tree

4 files changed

+69
-10
lines changed

4 files changed

+69
-10
lines changed

Include/internal/pycore_typeobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct _types_runtime_state {
3333
struct {
3434
struct {
3535
PyTypeObject *type;
36+
PyTypeObject def;
3637
int64_t interp_count;
3738
} types[_Py_MAX_MANAGED_STATIC_TYPES];
3839
} managed_static;

Lib/test/test_types.py

+36
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import pickle
1111
import locale
1212
import sys
13+
import textwrap
1314
import types
1415
import unittest.mock
1516
import weakref
@@ -2345,5 +2346,40 @@ def ex(a, /, b, *, c):
23452346
)
23462347

23472348

2349+
class SubinterpreterTests(unittest.TestCase):
2350+
2351+
@classmethod
2352+
def setUpClass(cls):
2353+
global interpreters
2354+
try:
2355+
from test.support import interpreters
2356+
except ModuleNotFoundError:
2357+
raise unittest.SkipTest('subinterpreters required')
2358+
import test.support.interpreters.channels
2359+
2360+
@cpython_only
2361+
def test_slot_wrappers(self):
2362+
rch, sch = interpreters.channels.create()
2363+
2364+
# For now it's sufficient to check int.__str__.
2365+
# See https://github.com/python/cpython/issues/117482
2366+
# and https://github.com/python/cpython/pull/117660.
2367+
script = textwrap.dedent('''
2368+
text = repr(int.__str__)
2369+
sch.send_nowait(text)
2370+
''')
2371+
2372+
exec(script)
2373+
expected = rch.recv()
2374+
2375+
interp = interpreters.create()
2376+
interp.exec('from test.support import interpreters')
2377+
interp.prepare_main(sch=sch)
2378+
interp.exec(script)
2379+
results = rch.recv()
2380+
2381+
self.assertEqual(results, expected)
2382+
2383+
23482384
if __name__ == '__main__':
23492385
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Unexpected slot wrappers are no longer created for builtin static types in
2+
subinterpreters.

Objects/typeobject.c

+30-10
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,16 @@ managed_static_type_state_clear(PyInterpreterState *interp, PyTypeObject *self,
314314
}
315315
}
316316

317+
static PyTypeObject *
318+
managed_static_type_get_def(PyTypeObject *self, int isbuiltin)
319+
{
320+
size_t index = managed_static_type_index_get(self);
321+
size_t full_index = isbuiltin
322+
? index
323+
: index + _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES;
324+
return &_PyRuntime.types.managed_static.types[full_index].def;
325+
}
326+
317327
// Also see _PyStaticType_InitBuiltin() and _PyStaticType_FiniBuiltin().
318328

319329
/* end static builtin helpers */
@@ -5840,7 +5850,6 @@ fini_static_type(PyInterpreterState *interp, PyTypeObject *type,
58405850

58415851
_PyStaticType_ClearWeakRefs(interp, type);
58425852
managed_static_type_state_clear(interp, type, isbuiltin, final);
5843-
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
58445853
}
58455854

58465855
void
@@ -7850,7 +7859,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
78507859
return 0;
78517860
}
78527861

7853-
static int add_operators(PyTypeObject *);
7862+
static int add_operators(PyTypeObject *, PyTypeObject *);
78547863
static int add_tp_new_wrapper(PyTypeObject *type);
78557864

78567865
#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
@@ -8015,10 +8024,10 @@ type_dict_set_doc(PyTypeObject *type)
80158024

80168025

80178026
static int
8018-
type_ready_fill_dict(PyTypeObject *type)
8027+
type_ready_fill_dict(PyTypeObject *type, PyTypeObject *def)
80198028
{
80208029
/* Add type-specific descriptors to tp_dict */
8021-
if (add_operators(type) < 0) {
8030+
if (add_operators(type, def) < 0) {
80228031
return -1;
80238032
}
80248033
if (type_add_methods(type) < 0) {
@@ -8337,7 +8346,7 @@ type_ready_post_checks(PyTypeObject *type)
83378346

83388347

83398348
static int
8340-
type_ready(PyTypeObject *type, int initial)
8349+
type_ready(PyTypeObject *type, PyTypeObject *def, int initial)
83418350
{
83428351
ASSERT_TYPE_LOCK_HELD();
83438352

@@ -8376,7 +8385,7 @@ type_ready(PyTypeObject *type, int initial)
83768385
if (type_ready_set_new(type, initial) < 0) {
83778386
goto error;
83788387
}
8379-
if (type_ready_fill_dict(type) < 0) {
8388+
if (type_ready_fill_dict(type, def) < 0) {
83808389
goto error;
83818390
}
83828391
if (initial) {
@@ -8433,7 +8442,7 @@ PyType_Ready(PyTypeObject *type)
84338442
int res;
84348443
BEGIN_TYPE_LOCK();
84358444
if (!(type->tp_flags & Py_TPFLAGS_READY)) {
8436-
res = type_ready(type, 1);
8445+
res = type_ready(type, NULL, 1);
84378446
} else {
84388447
res = 0;
84398448
assert(_PyType_CheckConsistency(type));
@@ -8469,14 +8478,20 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
84698478

84708479
managed_static_type_state_init(interp, self, isbuiltin, initial);
84718480

8481+
PyTypeObject *def = managed_static_type_get_def(self, isbuiltin);
8482+
if (initial) {
8483+
memcpy(def, self, sizeof(PyTypeObject));
8484+
}
8485+
84728486
int res;
84738487
BEGIN_TYPE_LOCK();
8474-
res = type_ready(self, initial);
8488+
res = type_ready(self, def, initial);
84758489
END_TYPE_LOCK();
84768490
if (res < 0) {
84778491
_PyStaticType_ClearWeakRefs(interp, self);
84788492
managed_static_type_state_clear(interp, self, isbuiltin, initial);
84798493
}
8494+
84808495
return res;
84818496
}
84828497

@@ -11064,17 +11079,22 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
1106411079
infinite recursion here.) */
1106511080

1106611081
static int
11067-
add_operators(PyTypeObject *type)
11082+
add_operators(PyTypeObject *type, PyTypeObject *def)
1106811083
{
1106911084
PyObject *dict = lookup_tp_dict(type);
1107011085
pytype_slotdef *p;
1107111086
PyObject *descr;
1107211087
void **ptr;
1107311088

11089+
assert(def == NULL || (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
11090+
if (def == NULL) {
11091+
def = type;
11092+
}
11093+
1107411094
for (p = slotdefs; p->name; p++) {
1107511095
if (p->wrapper == NULL)
1107611096
continue;
11077-
ptr = slotptr(type, p->offset);
11097+
ptr = slotptr(def, p->offset);
1107811098
if (!ptr || !*ptr)
1107911099
continue;
1108011100
int r = PyDict_Contains(dict, p->name_strobj);

0 commit comments

Comments
 (0)