Skip to content

Commit 0ec761a

Browse files
[3.12] gh-117482: Fix Builtin Types Slot Wrappers (gh-121632)
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 <[email protected]>
1 parent 5492f84 commit 0ec761a

File tree

4 files changed

+74
-9
lines changed

4 files changed

+74
-9
lines changed

Lib/test/test_types.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import pickle
1010
import locale
1111
import sys
12+
import textwrap
1213
import types
1314
import unittest.mock
1415
import weakref
@@ -2252,5 +2253,39 @@ def coro():
22522253
'close', 'throw'}))
22532254

22542255

2256+
class SubinterpreterTests(unittest.TestCase):
2257+
2258+
@classmethod
2259+
def setUpClass(cls):
2260+
global interpreters
2261+
try:
2262+
from test.support import interpreters
2263+
except ModuleNotFoundError:
2264+
raise unittest.SkipTest('subinterpreters required')
2265+
2266+
@cpython_only
2267+
def test_slot_wrappers(self):
2268+
rch, sch = interpreters.create_channel()
2269+
2270+
# For now it's sufficient to check int.__str__.
2271+
# See https://github.com/python/cpython/issues/117482
2272+
# and https://github.com/python/cpython/pull/117660.
2273+
script = textwrap.dedent(f'''
2274+
text = repr(int.__str__)
2275+
sch = interpreters.SendChannel({sch.id})
2276+
sch.send_nowait(text)
2277+
''')
2278+
2279+
exec(script)
2280+
expected = rch.recv()
2281+
2282+
interp = interpreters.create()
2283+
interp.run('from test.support import interpreters')
2284+
interp.run(script)
2285+
results = rch.recv()
2286+
2287+
self.assertEqual(results, expected)
2288+
2289+
22552290
if __name__ == '__main__':
22562291
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: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ static_builtin_index_clear(PyTypeObject *self)
116116
self->tp_subclasses = NULL;
117117
}
118118

119+
120+
/* In 3.13+ this is stored in _PyRuntimeState. */
121+
static PyTypeObject static_type_defs[_Py_MAX_STATIC_BUILTIN_TYPES];
122+
123+
static inline PyTypeObject *
124+
static_builtin_get_def(PyTypeObject *type)
125+
{
126+
size_t index = static_builtin_index_get(type);
127+
return &static_type_defs[index];
128+
}
129+
130+
119131
static inline static_builtin_state *
120132
static_builtin_state_get(PyInterpreterState *interp, PyTypeObject *self)
121133
{
@@ -6982,7 +6994,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
69826994
return 0;
69836995
}
69846996

6985-
static int add_operators(PyTypeObject *);
6997+
static int add_operators(PyTypeObject *, PyTypeObject *);
69866998
static int add_tp_new_wrapper(PyTypeObject *type);
69876999

69887000
#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
@@ -7147,10 +7159,10 @@ type_dict_set_doc(PyTypeObject *type)
71477159

71487160

71497161
static int
7150-
type_ready_fill_dict(PyTypeObject *type)
7162+
type_ready_fill_dict(PyTypeObject *type, PyTypeObject *def)
71517163
{
71527164
/* Add type-specific descriptors to tp_dict */
7153-
if (add_operators(type) < 0) {
7165+
if (add_operators(type, def) < 0) {
71547166
return -1;
71557167
}
71567168
if (type_add_methods(type) < 0) {
@@ -7462,7 +7474,7 @@ type_ready_post_checks(PyTypeObject *type)
74627474

74637475

74647476
static int
7465-
type_ready(PyTypeObject *type, int rerunbuiltin)
7477+
type_ready(PyTypeObject *type, PyTypeObject *def, int rerunbuiltin)
74667478
{
74677479
_PyObject_ASSERT((PyObject *)type, !is_readying(type));
74687480
start_readying(type);
@@ -7499,7 +7511,7 @@ type_ready(PyTypeObject *type, int rerunbuiltin)
74997511
if (type_ready_set_new(type, rerunbuiltin) < 0) {
75007512
goto error;
75017513
}
7502-
if (type_ready_fill_dict(type) < 0) {
7514+
if (type_ready_fill_dict(type, def) < 0) {
75037515
goto error;
75047516
}
75057517
if (!rerunbuiltin) {
@@ -7551,7 +7563,7 @@ PyType_Ready(PyTypeObject *type)
75517563
type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
75527564
}
75537565

7554-
return type_ready(type, 0);
7566+
return type_ready(type, NULL, 0);
75557567
}
75567568

75577569
int
@@ -7581,10 +7593,16 @@ _PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self)
75817593

75827594
static_builtin_state_init(interp, self);
75837595

7584-
int res = type_ready(self, !ismain);
7596+
PyTypeObject *def = static_builtin_get_def(self);
7597+
if (ismain) {
7598+
memcpy(def, self, sizeof(PyTypeObject));
7599+
}
7600+
7601+
int res = type_ready(self, def, !ismain);
75857602
if (res < 0) {
75867603
static_builtin_state_clear(interp, self);
75877604
}
7605+
75887606
return res;
75897607
}
75907608

@@ -10108,17 +10126,22 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
1010810126
infinite recursion here.) */
1010910127

1011010128
static int
10111-
add_operators(PyTypeObject *type)
10129+
add_operators(PyTypeObject *type, PyTypeObject *def)
1011210130
{
1011310131
PyObject *dict = lookup_tp_dict(type);
1011410132
pytype_slotdef *p;
1011510133
PyObject *descr;
1011610134
void **ptr;
1011710135

10136+
assert(def == NULL || (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
10137+
if (def == NULL) {
10138+
def = type;
10139+
}
10140+
1011810141
for (p = slotdefs; p->name; p++) {
1011910142
if (p->wrapper == NULL)
1012010143
continue;
10121-
ptr = slotptr(type, p->offset);
10144+
ptr = slotptr(def, p->offset);
1012210145
if (!ptr || !*ptr)
1012310146
continue;
1012410147
int r = PyDict_Contains(dict, p->name_strobj);

Tools/c-analyzer/cpython/globals-to-fix.tsv

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ Objects/sliceobject.c - _Py_EllipsisObject -
305305
Python/instrumentation.c - _PyInstrumentation_DISABLE -
306306
Python/instrumentation.c - _PyInstrumentation_MISSING -
307307

308+
##-----------------------
309+
## other
310+
311+
Objects/typeobject.c - static_type_defs -
312+
308313

309314
##################################
310315
## global non-objects to fix in core code

0 commit comments

Comments
 (0)