Skip to content

Commit d8f3c1e

Browse files
gh-117482: Simplify the Fix For Builtin Types Slot Wrappers (GH-122865)
In gh-121602, I applied a fix to a builtin types initialization bug. That fix made sense in the context of some broader future changes, but introduced a little bit of extra complexity. That fix has turned out to be incomplete for some of the builtin types we haven't been testing. I found that out while improving the tests. A while back, @markshannon suggested a simpler fix that doesn't have that problem, which I've already applied to 3.12 and 3.13. I'm switching to that here. Given the potential long-term benefits of the more complex (but still incomplete) approach, I'll circle back to it in the future, particularly after I've improved the tests so no corner cases slip through the cracks. (This is effectively a "forward-port" of 716c677 from 3.13.)
1 parent b950831 commit d8f3c1e

File tree

5 files changed

+38
-42
lines changed

5 files changed

+38
-42
lines changed

Include/internal/pycore_typeobject.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ struct _types_runtime_state {
3333
struct {
3434
struct {
3535
PyTypeObject *type;
36-
PyTypeObject def;
3736
int64_t interp_count;
3837
} types[_Py_MAX_MANAGED_STATIC_TYPES];
3938
} managed_static;

Lib/test/test_doctest/test_doctest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ def non_Python_modules(): r"""
738738
739739
>>> import builtins
740740
>>> tests = doctest.DocTestFinder().find(builtins)
741-
>>> 830 < len(tests) < 860 # approximate number of objects with docstrings
741+
>>> 750 < len(tests) < 800 # approximate number of objects with docstrings
742742
True
743743
>>> real_tests = [t for t in tests if len(t.examples) > 0]
744744
>>> len(real_tests) # objects that actually have doctests

Lib/test/test_embed.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,6 @@ def test_datetime_reset_strptime(self):
416416
out, err = self.run_embedded_interpreter("test_repeated_init_exec", code)
417417
self.assertEqual(out, '20000101\n' * INIT_LOOPS)
418418

419-
@unittest.skip('inheritance across re-init is currently broken; see gh-117482')
420419
def test_static_types_inherited_slots(self):
421420
script = textwrap.dedent("""
422421
import test.support

Lib/test/test_types.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,9 +2410,6 @@ def test_static_types_inherited_slots(self):
24102410
def collate_results(raw):
24112411
results = {}
24122412
for cls, attr, wrapper in raw:
2413-
# XXX This should not be necessary.
2414-
if cls == repr(bool) and attr in self.NUMERIC_METHODS:
2415-
continue
24162413
key = cls, attr
24172414
assert key not in results, (results, key, wrapper)
24182415
results[key] = wrapper
@@ -2433,14 +2430,7 @@ def collate_results(raw):
24332430
cls, attr = key
24342431
with self.subTest(cls=cls, slotattr=attr):
24352432
actual = interp_results.pop(key)
2436-
# XXX This should not be necessary.
2437-
if cls == "<class 'collections.OrderedDict'>" and attr == '__len__':
2438-
continue
24392433
self.assertEqual(actual, expected)
2440-
# XXX This should not be necessary.
2441-
interp_results = {k: v for k, v in interp_results.items() if k[1] != '__hash__'}
2442-
# XXX This should not be necessary.
2443-
interp_results.pop(("<class 'collections.OrderedDict'>", '__getitem__'), None)
24442434
self.maxDiff = None
24452435
self.assertEqual(interp_results, {})
24462436

Objects/typeobject.c

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,6 @@ 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-
}
326317

327318

328319
PyObject *
@@ -5927,6 +5918,7 @@ fini_static_type(PyInterpreterState *interp, PyTypeObject *type,
59275918

59285919
_PyStaticType_ClearWeakRefs(interp, type);
59295920
managed_static_type_state_clear(interp, type, isbuiltin, final);
5921+
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
59305922
}
59315923

59325924
void
@@ -7939,7 +7931,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
79397931
return 0;
79407932
}
79417933

7942-
static int add_operators(PyTypeObject *, PyTypeObject *);
7934+
static int add_operators(PyTypeObject *type);
79437935
static int add_tp_new_wrapper(PyTypeObject *type);
79447936

79457937
#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
@@ -8104,10 +8096,10 @@ type_dict_set_doc(PyTypeObject *type)
81048096

81058097

81068098
static int
8107-
type_ready_fill_dict(PyTypeObject *type, PyTypeObject *def)
8099+
type_ready_fill_dict(PyTypeObject *type)
81088100
{
81098101
/* Add type-specific descriptors to tp_dict */
8110-
if (add_operators(type, def) < 0) {
8102+
if (add_operators(type) < 0) {
81118103
return -1;
81128104
}
81138105
if (type_add_methods(type) < 0) {
@@ -8426,7 +8418,7 @@ type_ready_post_checks(PyTypeObject *type)
84268418

84278419

84288420
static int
8429-
type_ready(PyTypeObject *type, PyTypeObject *def, int initial)
8421+
type_ready(PyTypeObject *type, int initial)
84308422
{
84318423
ASSERT_TYPE_LOCK_HELD();
84328424

@@ -8465,7 +8457,7 @@ type_ready(PyTypeObject *type, PyTypeObject *def, int initial)
84658457
if (type_ready_set_new(type, initial) < 0) {
84668458
goto error;
84678459
}
8468-
if (type_ready_fill_dict(type, def) < 0) {
8460+
if (type_ready_fill_dict(type) < 0) {
84698461
goto error;
84708462
}
84718463
if (initial) {
@@ -8522,7 +8514,7 @@ PyType_Ready(PyTypeObject *type)
85228514
int res;
85238515
BEGIN_TYPE_LOCK();
85248516
if (!(type->tp_flags & Py_TPFLAGS_READY)) {
8525-
res = type_ready(type, NULL, 1);
8517+
res = type_ready(type, 1);
85268518
} else {
85278519
res = 0;
85288520
assert(_PyType_CheckConsistency(type));
@@ -8558,20 +8550,14 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,
85588550

85598551
managed_static_type_state_init(interp, self, isbuiltin, initial);
85608552

8561-
PyTypeObject *def = managed_static_type_get_def(self, isbuiltin);
8562-
if (initial) {
8563-
memcpy(def, self, sizeof(PyTypeObject));
8564-
}
8565-
85668553
int res;
85678554
BEGIN_TYPE_LOCK();
8568-
res = type_ready(self, def, initial);
8555+
res = type_ready(self, initial);
85698556
END_TYPE_LOCK();
85708557
if (res < 0) {
85718558
_PyStaticType_ClearWeakRefs(interp, self);
85728559
managed_static_type_state_clear(interp, self, isbuiltin, initial);
85738560
}
8574-
85758561
return res;
85768562
}
85778563

@@ -11182,6 +11168,26 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
1118211168
return 0;
1118311169
}
1118411170

11171+
static int
11172+
slot_inherited(PyTypeObject *type, pytype_slotdef *slotdef, void **slot)
11173+
{
11174+
void **slot_base = slotptr(type->tp_base, slotdef->offset);
11175+
if (slot_base == NULL || *slot != *slot_base) {
11176+
return 0;
11177+
}
11178+
11179+
/* Some slots are inherited in pairs. */
11180+
if (slot == (void *)&type->tp_hash) {
11181+
return (type->tp_richcompare == type->tp_base->tp_richcompare);
11182+
}
11183+
else if (slot == (void *)&type->tp_richcompare) {
11184+
return (type->tp_hash == type->tp_base->tp_hash);
11185+
}
11186+
11187+
/* It must be inherited (see type_ready_inherit()). */
11188+
return 1;
11189+
}
11190+
1118511191
/* This function is called by PyType_Ready() to populate the type's
1118611192
dictionary with method descriptors for function slots. For each
1118711193
function slot (like tp_repr) that's defined in the type, one or more
@@ -11213,24 +11219,26 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
1121311219
infinite recursion here.) */
1121411220

1121511221
static int
11216-
add_operators(PyTypeObject *type, PyTypeObject *def)
11222+
add_operators(PyTypeObject *type)
1121711223
{
1121811224
PyObject *dict = lookup_tp_dict(type);
1121911225
pytype_slotdef *p;
1122011226
PyObject *descr;
1122111227
void **ptr;
1122211228

11223-
assert(def == NULL || (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
11224-
if (def == NULL) {
11225-
def = type;
11226-
}
11227-
1122811229
for (p = slotdefs; p->name; p++) {
1122911230
if (p->wrapper == NULL)
1123011231
continue;
11231-
ptr = slotptr(def, p->offset);
11232+
ptr = slotptr(type, p->offset);
1123211233
if (!ptr || !*ptr)
1123311234
continue;
11235+
/* Also ignore when the type slot has been inherited. */
11236+
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN
11237+
&& type->tp_base != NULL
11238+
&& slot_inherited(type, p, ptr))
11239+
{
11240+
continue;
11241+
}
1123411242
int r = PyDict_Contains(dict, p->name_strobj);
1123511243
if (r > 0)
1123611244
continue;

0 commit comments

Comments
 (0)