Skip to content

Commit

Permalink
pythongh-117482: Simplify the Fix For Builtin Types Slot Wrappers (py…
Browse files Browse the repository at this point in the history
…thonGH-122865)

In pythongh-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.)
  • Loading branch information
ericsnowcurrently authored Sep 9, 2024
1 parent b950831 commit d8f3c1e
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 42 deletions.
1 change: 0 additions & 1 deletion Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ struct _types_runtime_state {
struct {
struct {
PyTypeObject *type;
PyTypeObject def;
int64_t interp_count;
} types[_Py_MAX_MANAGED_STATIC_TYPES];
} managed_static;
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_doctest/test_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ def non_Python_modules(): r"""
>>> import builtins
>>> tests = doctest.DocTestFinder().find(builtins)
>>> 830 < len(tests) < 860 # approximate number of objects with docstrings
>>> 750 < len(tests) < 800 # approximate number of objects with docstrings
True
>>> real_tests = [t for t in tests if len(t.examples) > 0]
>>> len(real_tests) # objects that actually have doctests
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@ def test_datetime_reset_strptime(self):
out, err = self.run_embedded_interpreter("test_repeated_init_exec", code)
self.assertEqual(out, '20000101\n' * INIT_LOOPS)

@unittest.skip('inheritance across re-init is currently broken; see gh-117482')
def test_static_types_inherited_slots(self):
script = textwrap.dedent("""
import test.support
Expand Down
10 changes: 0 additions & 10 deletions Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2410,9 +2410,6 @@ def test_static_types_inherited_slots(self):
def collate_results(raw):
results = {}
for cls, attr, wrapper in raw:
# XXX This should not be necessary.
if cls == repr(bool) and attr in self.NUMERIC_METHODS:
continue
key = cls, attr
assert key not in results, (results, key, wrapper)
results[key] = wrapper
Expand All @@ -2433,14 +2430,7 @@ def collate_results(raw):
cls, attr = key
with self.subTest(cls=cls, slotattr=attr):
actual = interp_results.pop(key)
# XXX This should not be necessary.
if cls == "<class 'collections.OrderedDict'>" and attr == '__len__':
continue
self.assertEqual(actual, expected)
# XXX This should not be necessary.
interp_results = {k: v for k, v in interp_results.items() if k[1] != '__hash__'}
# XXX This should not be necessary.
interp_results.pop(("<class 'collections.OrderedDict'>", '__getitem__'), None)
self.maxDiff = None
self.assertEqual(interp_results, {})

Expand Down
66 changes: 37 additions & 29 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,15 +314,6 @@ 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;
}


PyObject *
Expand Down Expand Up @@ -5927,6 +5918,7 @@ fini_static_type(PyInterpreterState *interp, PyTypeObject *type,

_PyStaticType_ClearWeakRefs(interp, type);
managed_static_type_state_clear(interp, type, isbuiltin, final);
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
}

void
Expand Down Expand Up @@ -7939,7 +7931,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
return 0;
}

static int add_operators(PyTypeObject *, PyTypeObject *);
static int add_operators(PyTypeObject *type);
static int add_tp_new_wrapper(PyTypeObject *type);

#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
Expand Down Expand Up @@ -8104,10 +8096,10 @@ type_dict_set_doc(PyTypeObject *type)


static int
type_ready_fill_dict(PyTypeObject *type, PyTypeObject *def)
type_ready_fill_dict(PyTypeObject *type)
{
/* Add type-specific descriptors to tp_dict */
if (add_operators(type, def) < 0) {
if (add_operators(type) < 0) {
return -1;
}
if (type_add_methods(type) < 0) {
Expand Down Expand Up @@ -8426,7 +8418,7 @@ type_ready_post_checks(PyTypeObject *type)


static int
type_ready(PyTypeObject *type, PyTypeObject *def, int initial)
type_ready(PyTypeObject *type, int initial)
{
ASSERT_TYPE_LOCK_HELD();

Expand Down Expand Up @@ -8465,7 +8457,7 @@ type_ready(PyTypeObject *type, PyTypeObject *def, int initial)
if (type_ready_set_new(type, initial) < 0) {
goto error;
}
if (type_ready_fill_dict(type, def) < 0) {
if (type_ready_fill_dict(type) < 0) {
goto error;
}
if (initial) {
Expand Down Expand Up @@ -8522,7 +8514,7 @@ PyType_Ready(PyTypeObject *type)
int res;
BEGIN_TYPE_LOCK();
if (!(type->tp_flags & Py_TPFLAGS_READY)) {
res = type_ready(type, NULL, 1);
res = type_ready(type, 1);
} else {
res = 0;
assert(_PyType_CheckConsistency(type));
Expand Down Expand Up @@ -8558,20 +8550,14 @@ init_static_type(PyInterpreterState *interp, PyTypeObject *self,

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;
BEGIN_TYPE_LOCK();
res = type_ready(self, def, initial);
res = type_ready(self, initial);
END_TYPE_LOCK();
if (res < 0) {
_PyStaticType_ClearWeakRefs(interp, self);
managed_static_type_state_clear(interp, self, isbuiltin, initial);
}

return res;
}

Expand Down Expand Up @@ -11182,6 +11168,26 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
return 0;
}

static int
slot_inherited(PyTypeObject *type, pytype_slotdef *slotdef, void **slot)
{
void **slot_base = slotptr(type->tp_base, slotdef->offset);
if (slot_base == NULL || *slot != *slot_base) {
return 0;
}

/* Some slots are inherited in pairs. */
if (slot == (void *)&type->tp_hash) {
return (type->tp_richcompare == type->tp_base->tp_richcompare);
}
else if (slot == (void *)&type->tp_richcompare) {
return (type->tp_hash == type->tp_base->tp_hash);
}

/* It must be inherited (see type_ready_inherit()). */
return 1;
}

/* This function is called by PyType_Ready() to populate the type's
dictionary with method descriptors for function slots. For each
function slot (like tp_repr) that's defined in the type, one or more
Expand Down Expand Up @@ -11213,24 +11219,26 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
infinite recursion here.) */

static int
add_operators(PyTypeObject *type, PyTypeObject *def)
add_operators(PyTypeObject *type)
{
PyObject *dict = lookup_tp_dict(type);
pytype_slotdef *p;
PyObject *descr;
void **ptr;

assert(def == NULL || (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
if (def == NULL) {
def = type;
}

for (p = slotdefs; p->name; p++) {
if (p->wrapper == NULL)
continue;
ptr = slotptr(def, p->offset);
ptr = slotptr(type, p->offset);
if (!ptr || !*ptr)
continue;
/* Also ignore when the type slot has been inherited. */
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN
&& type->tp_base != NULL
&& slot_inherited(type, p, ptr))
{
continue;
}
int r = PyDict_Contains(dict, p->name_strobj);
if (r > 0)
continue;
Expand Down

0 comments on commit d8f3c1e

Please sign in to comment.