diff --git a/.gitattributes b/.gitattributes index 5b81d2cb3c90e9..56855828a462a1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -89,6 +89,7 @@ Lib/test/levenshtein_examples.json generated Lib/test/test_stable_abi_ctypes.py generated Lib/token.py generated Misc/sbom.spdx.json generated +Modules/_testinternalcapi/tpslots_generated.h generated Objects/typeslots.inc generated PC/python3dll.c generated Parser/parser.c generated diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 6e6386bc044dc3..d4f5ec2b881b8c 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -2035,6 +2035,29 @@ gh_119213_getargs_impl(PyObject *module, PyObject *spam) } +#include "_testinternalcapi/tpslots_generated.h" + +static PyObject * +identify_type_slots(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *slots = PyList_New(Py_ARRAY_LENGTH(slotdefs)-1); + if (slots == NULL) { + return NULL; + } + Py_ssize_t i; + const struct pytype_slot *p; + for (i = 0, p = slotdefs; p->slot != NULL; p++, i++) { + PyObject *item = Py_BuildValue("ss", p->slot, p->attr); + if (item == NULL) { + Py_DECREF(slots); + return NULL; + } + PyList_SET_ITEM(slots, i, item); + } + return slots; +} + + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -2129,6 +2152,7 @@ static PyMethodDef module_functions[] = { {"uop_symbols_test", _Py_uop_symbols_test, METH_NOARGS}, #endif GH_119213_GETARGS_METHODDEF + {"identify_type_slots", identify_type_slots, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_testinternalcapi/tpslots_generated.h b/Modules/_testinternalcapi/tpslots_generated.h new file mode 100644 index 00000000000000..76f8df5ef45041 --- /dev/null +++ b/Modules/_testinternalcapi/tpslots_generated.h @@ -0,0 +1,120 @@ + +/* The following is auto-generated by Tools/build/generate_global_objects.py. */ + +struct pytype_slot { + const char *slot; + const char *attr; +}; + +// These are derived from the "slotdefs" array in Objects/typeobject.c. +static const struct pytype_slot slotdefs[] = { + {"tp_getattr", "__getattribute__"}, + {"tp_getattr", "__getattr__"}, + {"tp_setattr", "__setattr__"}, + {"tp_setattr", "__delattr__"}, + {"tp_repr", "__repr__"}, + {"tp_hash", "__hash__"}, + {"tp_call", "__call__"}, + {"tp_str", "__str__"}, + {"tp_getattro", "__getattribute__"}, + {"tp_getattro", "__getattr__"}, + {"tp_setattro", "__setattr__"}, + {"tp_setattro", "__delattr__"}, + {"tp_richcompare", "__lt__"}, + {"tp_richcompare", "__le__"}, + {"tp_richcompare", "__eq__"}, + {"tp_richcompare", "__ne__"}, + {"tp_richcompare", "__gt__"}, + {"tp_richcompare", "__ge__"}, + {"tp_iter", "__iter__"}, + {"tp_iternext", "__next__"}, + {"tp_descr_get", "__get__"}, + {"tp_descr_set", "__set__"}, + {"tp_descr_set", "__delete__"}, + {"tp_init", "__init__"}, + {"tp_new", "__new__"}, + {"tp_finalize", "__del__"}, + + /* buffer */ + {"tp_as_buffer.bf_getbuffer", "__buffer__"}, + {"tp_as_buffer.bf_releasebuffer", "__release_buffer__"}, + + /* async */ + {"tp_as_async.am_await", "__await__"}, + {"tp_as_async.am_aiter", "__aiter__"}, + {"tp_as_async.am_anext", "__anext__"}, + /* Does not have a corresponding slot wrapper: */ + {"tp_as_async.am_send", "NULL"}, + + /* number */ + {"tp_as_number.nb_add", "__add__"}, + {"tp_as_number.nb_add", "__radd__"}, + {"tp_as_number.nb_subtract", "__sub__"}, + {"tp_as_number.nb_subtract", "__rsub__"}, + {"tp_as_number.nb_multiply", "__mul__"}, + {"tp_as_number.nb_multiply", "__rmul__"}, + {"tp_as_number.nb_remainder", "__mod__"}, + {"tp_as_number.nb_remainder", "__rmod__"}, + {"tp_as_number.nb_power", "__pow__"}, + {"tp_as_number.nb_power", "__rpow__"}, + {"tp_as_number.nb_negative", "__neg__"}, + {"tp_as_number.nb_positive", "__pos__"}, + {"tp_as_number.nb_absolute", "__abs__"}, + {"tp_as_number.nb_bool", "__bool__"}, + {"tp_as_number.nb_invert", "__invert__"}, + {"tp_as_number.nb_lshift", "__lshift__"}, + {"tp_as_number.nb_lshift", "__rlshift__"}, + {"tp_as_number.nb_rshift", "__rshift__"}, + {"tp_as_number.nb_rshift", "__rrshift__"}, + {"tp_as_number.nb_and", "__and__"}, + {"tp_as_number.nb_and", "__rand__"}, + {"tp_as_number.nb_xor", "__xor__"}, + {"tp_as_number.nb_xor", "__rxor__"}, + {"tp_as_number.nb_or", "__or__"}, + {"tp_as_number.nb_or", "__ror__"}, + {"tp_as_number.nb_int", "__int__"}, + {"tp_as_number.nb_float", "__float__"}, + {"tp_as_number.nb_inplace_add", "__iadd__"}, + {"tp_as_number.nb_inplace_subtract", "__isub__"}, + {"tp_as_number.nb_inplace_multiply", "__imul__"}, + {"tp_as_number.nb_inplace_remainder", "__imod__"}, + {"tp_as_number.nb_inplace_power", "__ipow__"}, + {"tp_as_number.nb_inplace_lshift", "__ilshift__"}, + {"tp_as_number.nb_inplace_rshift", "__irshift__"}, + {"tp_as_number.nb_inplace_and", "__iand__"}, + {"tp_as_number.nb_inplace_xor", "__ixor__"}, + {"tp_as_number.nb_inplace_or", "__ior__"}, + {"tp_as_number.nb_floor_divide", "__floordiv__"}, + {"tp_as_number.nb_floor_divide", "__rfloordiv__"}, + {"tp_as_number.nb_true_divide", "__truediv__"}, + {"tp_as_number.nb_true_divide", "__rtruediv__"}, + {"tp_as_number.nb_inplace_floor_divide", "__ifloordiv__"}, + {"tp_as_number.nb_inplace_true_divide", "__itruediv__"}, + {"tp_as_number.nb_index", "__index__"}, + {"tp_as_number.nb_matrix_multiply", "__matmul__"}, + {"tp_as_number.nb_matrix_multiply", "__rmatmul__"}, + {"tp_as_number.nb_inplace_matrix_multiply", "__imatmul__"}, + + /* mapping */ + {"tp_as_mapping.mp_length", "__len__"}, + {"tp_as_mapping.mp_subscript", "__getitem__"}, + {"tp_as_mapping.mp_ass_subscript", "__setitem__"}, + {"tp_as_mapping.mp_ass_subscript", "__delitem__"}, + + /* sequence */ + {"tp_as_sequence.sq_length", "__len__"}, + {"tp_as_sequence.sq_concat", "__add__"}, + {"tp_as_sequence.sq_repeat", "__mul__"}, + {"tp_as_sequence.sq_repeat", "__rmul__"}, + {"tp_as_sequence.sq_item", "__getitem__"}, + {"tp_as_sequence.sq_ass_item", "__setitem__"}, + {"tp_as_sequence.sq_ass_item", "__delitem__"}, + {"tp_as_sequence.sq_contains", "__contains__"}, + {"tp_as_sequence.sq_inplace_concat", "__iadd__"}, + {"tp_as_sequence.sq_inplace_repeat", "__imul__"}, + + /* sentinel */ + {NULL} +}; + +/* End auto-generated code */ diff --git a/Tools/build/generate_global_objects.py b/Tools/build/generate_global_objects.py index 882918fafb1edd..e3f6a6fceaafab 100644 --- a/Tools/build/generate_global_objects.py +++ b/Tools/build/generate_global_objects.py @@ -2,6 +2,8 @@ import io import os.path import re +import textwrap + SCRIPT_NAME = 'Tools/build/generate_global_objects.py' __file__ = os.path.abspath(__file__) @@ -175,6 +177,29 @@ def iter_global_strings(): yield varname, string, filename, lno, line +def iter_tp_slots(): + regex = re.compile(r'^ +\w+SLOT\((\w+), (\w+),') + filename = os.path.join(ROOT, 'Objects', 'typeobject.c') + with open(filename, encoding='utf-8') as infile: + for line in infile: + if line.startswith('static pytype_slotdef slotdefs[] = {'): + break + for line in infile: + m = regex.match(line) + if not m: + line = line.strip() + if line == '{NULL}': + break + assert line != '};', (line,) + assert 'SLOT(' not in line, (line,) + continue + attr, slot = m.groups() + yield slot, attr + # Add in slots that aren't in slotdefs. + if slot == 'am_anext': + yield 'am_send', None + + def iter_to_marker(lines, marker): for line in lines: if line.rstrip() == marker: @@ -218,8 +243,15 @@ def block(self, prefix, suffix="", *, continuation=None): @contextlib.contextmanager -def open_for_changes(filename, orig): +def open_for_changes(filename, orig=None): """Like open() but only write to the file if it changed.""" + if orig is None: + try: + with open(filename, encoding='utf-8') as infile: + orig = infile.read() + except FileNotFoundError: + orig = None + outfile = io.StringIO() yield outfile text = outfile.getvalue() @@ -447,6 +479,65 @@ def get_identifiers_and_strings() -> 'tuple[set[str], dict[str, str]]': return identifiers, strings +####################################### +# info about types + +def generate_tp_slot_names(): + filename = os.path.join( + ROOT, 'Modules', '_testinternalcapi', 'tpslots_generated.h') + template = textwrap.dedent(f""" + {START} + + struct pytype_slot {{ + const char *slot; + const char *attr; + }}; + + // These are derived from the "slotdefs" array in Objects/typeobject.c. + static const struct pytype_slot slotdefs[] = {{ + %s + + /* sentinel */ + {{NULL}} + }}; + + {END} + """) + subslots = { + 'bf_': 'tp_as_buffer', + 'am_': 'tp_as_async', + 'nb_': 'tp_as_number', + 'mp_': 'tp_as_mapping', + 'sq_': 'tp_as_sequence', + } + rows = [] + groups = [] + for slot, attr in iter_tp_slots(): + if slot.startswith('tp_'): + group = 'primary' + else: + subslot = slot + slot = subslots[slot[:3]] + group = slot[6:] + slot = f'{slot}.{subslot}' + + if not groups: + groups.append(group) + elif groups[-1] != group: + assert group not in groups, (group, groups) + rows.append('') + rows.append(f' /* {group} */') + groups.append(group) + + if attr is None: + rows.append(' /* Does not have a corresponding slot wrapper: */') + attr = 'NULL' + rows.append(f' {{"{slot}", "{attr}"}},') + text = template % (os.linesep.join(rows).strip()) + with open_for_changes(filename) as outfile: + outfile.write(text) + + ####################################### # the script @@ -458,6 +549,8 @@ def main() -> None: generate_static_strings_initializer(identifiers, strings) generate_global_object_finalizers(generated_immortal_objects) + generate_tp_slot_names() + if __name__ == '__main__': main()