Skip to content

Commit c6dbfbb

Browse files
[3.13] gh-117482: Fix Builtin Types Slot Wrappers (gh-121630)
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>
1 parent 38c4028 commit c6dbfbb

File tree

4 files changed

+69
-10
lines changed

4 files changed

+69
-10
lines changed

Include/internal/pycore_typeobject.h

Lines changed: 1 addition & 0 deletions
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

Lines changed: 36 additions & 0 deletions
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()
Lines changed: 2 additions & 0 deletions
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

Lines changed: 30 additions & 10 deletions
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 */
@@ -5668,7 +5678,6 @@ fini_static_type(PyInterpreterState *interp, PyTypeObject *type,
56685678

56695679
_PyStaticType_ClearWeakRefs(interp, type);
56705680
managed_static_type_state_clear(interp, type, isbuiltin, final);
5671-
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
56725681
}
56735682

56745683
void
@@ -7671,7 +7680,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
76717680
return 0;
76727681
}
76737682

7674-
static int add_operators(PyTypeObject *);
7683+
static int add_operators(PyTypeObject *, PyTypeObject *);
76757684
static int add_tp_new_wrapper(PyTypeObject *type);
76767685

76777686
#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
@@ -7836,10 +7845,10 @@ type_dict_set_doc(PyTypeObject *type)
78367845

78377846

78387847
static int
7839-
type_ready_fill_dict(PyTypeObject *type)
7848+
type_ready_fill_dict(PyTypeObject *type, PyTypeObject *def)
78407849
{
78417850
/* Add type-specific descriptors to tp_dict */
7842-
if (add_operators(type) < 0) {
7851+
if (add_operators(type, def) < 0) {
78437852
return -1;
78447853
}
78457854
if (type_add_methods(type) < 0) {
@@ -8158,7 +8167,7 @@ type_ready_post_checks(PyTypeObject *type)
81588167

81598168

81608169
static int
8161-
type_ready(PyTypeObject *type, int initial)
8170+
type_ready(PyTypeObject *type, PyTypeObject *def, int initial)
81628171
{
81638172
ASSERT_TYPE_LOCK_HELD();
81648173

@@ -8197,7 +8206,7 @@ type_ready(PyTypeObject *type, int initial)
81978206
if (type_ready_set_new(type, initial) < 0) {
81988207
goto error;
81998208
}
8200-
if (type_ready_fill_dict(type) < 0) {
8209+
if (type_ready_fill_dict(type, def) < 0) {
82018210
goto error;
82028211
}
82038212
if (initial) {
@@ -8254,7 +8263,7 @@ PyType_Ready(PyTypeObject *type)
82548263
int res;
82558264
BEGIN_TYPE_LOCK();
82568265
if (!(type->tp_flags & Py_TPFLAGS_READY)) {
8257-
res = type_ready(type, 1);
8266+
res = type_ready(type, NULL, 1);
82588267
} else {
82598268
res = 0;
82608269
assert(_PyType_CheckConsistency(type));
@@ -8290,14 +8299,20 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
82908299

82918300
managed_static_type_state_init(interp, self, isbuiltin, initial);
82928301

8302+
PyTypeObject *def = managed_static_type_get_def(self, isbuiltin);
8303+
if (initial) {
8304+
memcpy(def, self, sizeof(PyTypeObject));
8305+
}
8306+
82938307
int res;
82948308
BEGIN_TYPE_LOCK();
8295-
res = type_ready(self, initial);
8309+
res = type_ready(self, def, initial);
82968310
END_TYPE_LOCK();
82978311
if (res < 0) {
82988312
_PyStaticType_ClearWeakRefs(interp, self);
82998313
managed_static_type_state_clear(interp, self, isbuiltin, initial);
83008314
}
8315+
83018316
return res;
83028317
}
83038318

@@ -10885,17 +10900,22 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
1088510900
infinite recursion here.) */
1088610901

1088710902
static int
10888-
add_operators(PyTypeObject *type)
10903+
add_operators(PyTypeObject *type, PyTypeObject *def)
1088910904
{
1089010905
PyObject *dict = lookup_tp_dict(type);
1089110906
pytype_slotdef *p;
1089210907
PyObject *descr;
1089310908
void **ptr;
1089410909

10910+
assert(def == NULL || (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
10911+
if (def == NULL) {
10912+
def = type;
10913+
}
10914+
1089510915
for (p = slotdefs; p->name; p++) {
1089610916
if (p->wrapper == NULL)
1089710917
continue;
10898-
ptr = slotptr(type, p->offset);
10918+
ptr = slotptr(def, p->offset);
1089910919
if (!ptr || !*ptr)
1090010920
continue;
1090110921
int r = PyDict_Contains(dict, p->name_strobj);

0 commit comments

Comments
 (0)