mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
58e8cf2bb6
commit
5250a03133
|
@ -33,6 +33,7 @@ struct _types_runtime_state {
|
||||||
struct {
|
struct {
|
||||||
struct {
|
struct {
|
||||||
PyTypeObject *type;
|
PyTypeObject *type;
|
||||||
|
PyTypeObject def;
|
||||||
int64_t interp_count;
|
int64_t interp_count;
|
||||||
} types[_Py_MAX_MANAGED_STATIC_TYPES];
|
} types[_Py_MAX_MANAGED_STATIC_TYPES];
|
||||||
} managed_static;
|
} managed_static;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import inspect
|
||||||
import pickle
|
import pickle
|
||||||
import locale
|
import locale
|
||||||
import sys
|
import sys
|
||||||
|
import textwrap
|
||||||
import types
|
import types
|
||||||
import unittest.mock
|
import unittest.mock
|
||||||
import weakref
|
import weakref
|
||||||
|
@ -2345,5 +2346,40 @@ class FunctionTests(unittest.TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SubinterpreterTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
global interpreters
|
||||||
|
try:
|
||||||
|
from test.support import interpreters
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
raise unittest.SkipTest('subinterpreters required')
|
||||||
|
import test.support.interpreters.channels
|
||||||
|
|
||||||
|
@cpython_only
|
||||||
|
def test_slot_wrappers(self):
|
||||||
|
rch, sch = interpreters.channels.create()
|
||||||
|
|
||||||
|
# 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('''
|
||||||
|
text = repr(int.__str__)
|
||||||
|
sch.send_nowait(text)
|
||||||
|
''')
|
||||||
|
|
||||||
|
exec(script)
|
||||||
|
expected = rch.recv()
|
||||||
|
|
||||||
|
interp = interpreters.create()
|
||||||
|
interp.exec('from test.support import interpreters')
|
||||||
|
interp.prepare_main(sch=sch)
|
||||||
|
interp.exec(script)
|
||||||
|
results = rch.recv()
|
||||||
|
|
||||||
|
self.assertEqual(results, expected)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Unexpected slot wrappers are no longer created for builtin static types in
|
||||||
|
subinterpreters.
|
|
@ -314,6 +314,16 @@ managed_static_type_state_clear(PyInterpreterState *interp, PyTypeObject *self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyTypeObject *
|
||||||
|
managed_static_type_get_def(PyTypeObject *self, int isbuiltin)
|
||||||
|
{
|
||||||
|
size_t index = managed_static_type_index_get(self);
|
||||||
|
size_t full_index = isbuiltin
|
||||||
|
? index
|
||||||
|
: index + _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES;
|
||||||
|
return &_PyRuntime.types.managed_static.types[full_index].def;
|
||||||
|
}
|
||||||
|
|
||||||
// Also see _PyStaticType_InitBuiltin() and _PyStaticType_FiniBuiltin().
|
// Also see _PyStaticType_InitBuiltin() and _PyStaticType_FiniBuiltin().
|
||||||
|
|
||||||
/* end static builtin helpers */
|
/* end static builtin helpers */
|
||||||
|
@ -5840,7 +5850,6 @@ fini_static_type(PyInterpreterState *interp, PyTypeObject *type,
|
||||||
|
|
||||||
_PyStaticType_ClearWeakRefs(interp, type);
|
_PyStaticType_ClearWeakRefs(interp, type);
|
||||||
managed_static_type_state_clear(interp, type, isbuiltin, final);
|
managed_static_type_state_clear(interp, type, isbuiltin, final);
|
||||||
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -7850,7 +7859,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_operators(PyTypeObject *);
|
static int add_operators(PyTypeObject *, PyTypeObject *);
|
||||||
static int add_tp_new_wrapper(PyTypeObject *type);
|
static int add_tp_new_wrapper(PyTypeObject *type);
|
||||||
|
|
||||||
#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
|
#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
|
||||||
|
@ -8015,10 +8024,10 @@ type_dict_set_doc(PyTypeObject *type)
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
type_ready_fill_dict(PyTypeObject *type)
|
type_ready_fill_dict(PyTypeObject *type, PyTypeObject *def)
|
||||||
{
|
{
|
||||||
/* Add type-specific descriptors to tp_dict */
|
/* Add type-specific descriptors to tp_dict */
|
||||||
if (add_operators(type) < 0) {
|
if (add_operators(type, def) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (type_add_methods(type) < 0) {
|
if (type_add_methods(type) < 0) {
|
||||||
|
@ -8337,7 +8346,7 @@ type_ready_post_checks(PyTypeObject *type)
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
type_ready(PyTypeObject *type, int initial)
|
type_ready(PyTypeObject *type, PyTypeObject *def, int initial)
|
||||||
{
|
{
|
||||||
ASSERT_TYPE_LOCK_HELD();
|
ASSERT_TYPE_LOCK_HELD();
|
||||||
|
|
||||||
|
@ -8376,7 +8385,7 @@ type_ready(PyTypeObject *type, int initial)
|
||||||
if (type_ready_set_new(type, initial) < 0) {
|
if (type_ready_set_new(type, initial) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (type_ready_fill_dict(type) < 0) {
|
if (type_ready_fill_dict(type, def) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (initial) {
|
if (initial) {
|
||||||
|
@ -8433,7 +8442,7 @@ PyType_Ready(PyTypeObject *type)
|
||||||
int res;
|
int res;
|
||||||
BEGIN_TYPE_LOCK();
|
BEGIN_TYPE_LOCK();
|
||||||
if (!(type->tp_flags & Py_TPFLAGS_READY)) {
|
if (!(type->tp_flags & Py_TPFLAGS_READY)) {
|
||||||
res = type_ready(type, 1);
|
res = type_ready(type, NULL, 1);
|
||||||
} else {
|
} else {
|
||||||
res = 0;
|
res = 0;
|
||||||
assert(_PyType_CheckConsistency(type));
|
assert(_PyType_CheckConsistency(type));
|
||||||
|
@ -8469,14 +8478,20 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
|
||||||
|
|
||||||
managed_static_type_state_init(interp, self, isbuiltin, initial);
|
managed_static_type_state_init(interp, self, isbuiltin, initial);
|
||||||
|
|
||||||
|
PyTypeObject *def = managed_static_type_get_def(self, isbuiltin);
|
||||||
|
if (initial) {
|
||||||
|
memcpy(def, self, sizeof(PyTypeObject));
|
||||||
|
}
|
||||||
|
|
||||||
int res;
|
int res;
|
||||||
BEGIN_TYPE_LOCK();
|
BEGIN_TYPE_LOCK();
|
||||||
res = type_ready(self, initial);
|
res = type_ready(self, def, initial);
|
||||||
END_TYPE_LOCK();
|
END_TYPE_LOCK();
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
_PyStaticType_ClearWeakRefs(interp, self);
|
_PyStaticType_ClearWeakRefs(interp, self);
|
||||||
managed_static_type_state_clear(interp, self, isbuiltin, initial);
|
managed_static_type_state_clear(interp, self, isbuiltin, initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11064,17 +11079,22 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
|
||||||
infinite recursion here.) */
|
infinite recursion here.) */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
add_operators(PyTypeObject *type)
|
add_operators(PyTypeObject *type, PyTypeObject *def)
|
||||||
{
|
{
|
||||||
PyObject *dict = lookup_tp_dict(type);
|
PyObject *dict = lookup_tp_dict(type);
|
||||||
pytype_slotdef *p;
|
pytype_slotdef *p;
|
||||||
PyObject *descr;
|
PyObject *descr;
|
||||||
void **ptr;
|
void **ptr;
|
||||||
|
|
||||||
|
assert(def == NULL || (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
|
||||||
|
if (def == NULL) {
|
||||||
|
def = type;
|
||||||
|
}
|
||||||
|
|
||||||
for (p = slotdefs; p->name; p++) {
|
for (p = slotdefs; p->name; p++) {
|
||||||
if (p->wrapper == NULL)
|
if (p->wrapper == NULL)
|
||||||
continue;
|
continue;
|
||||||
ptr = slotptr(type, p->offset);
|
ptr = slotptr(def, p->offset);
|
||||||
if (!ptr || !*ptr)
|
if (!ptr || !*ptr)
|
||||||
continue;
|
continue;
|
||||||
int r = PyDict_Contains(dict, p->name_strobj);
|
int r = PyDict_Contains(dict, p->name_strobj);
|
||||||
|
|
Loading…
Reference in New Issue