From 3b13a93d70186542b81b569acf27e3e1693e432c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 15 Jan 2026 16:00:32 +0100 Subject: [PATCH 01/36] Add header for internal slot iterator API --- Include/internal/pycore_slots.h | 136 +++++++++++++++++++++++++++++ Include/pytypedefs.h | 1 + Makefile.pre.in | 1 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 + 5 files changed, 142 insertions(+) create mode 100644 Include/internal/pycore_slots.h diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h new file mode 100644 index 00000000000000..285b0918d25510 --- /dev/null +++ b/Include/internal/pycore_slots.h @@ -0,0 +1,136 @@ +#ifndef _Py_PYCORE_SLOTS_H +#define _Py_PYCORE_SLOTS_H + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#include + +#define _PySlot_TYPE_VOID 1 +#define _PySlot_TYPE_FUNC 2 +#define _PySlot_TYPE_PTR 3 +#define _PySlot_TYPE_SIZE 4 +#define _PySlot_TYPE_INT64 5 +#define _PySlot_TYPE_UINT64 6 + +#define _PySlot_KIND_TYPE 1 +#define _PySlot_KIND_MOD 2 +#define _PySlot_KIND_COMPAT 0x10 +#define _PySlot_KIND_SLOT 0x20 + +#define _PySlot_NULL_DEPRECATED 0 +#define _PySlot_NULL_REJECT 1 +#define _PySlot_NULL_ALLOW 2 + +/* Internal information about a slot */ +typedef struct _PySlot_Info { + const char *name; + uint8_t dtype; /* _PySlot_TYPE_* */ + uint8_t kind; /* _PySlot_KIND */ + uint8_t null_handling; // for pointers (incl. functions) + bool is_subslots :1; + bool is_name :1; + bool reject_duplicates :1; + bool deprecate_duplicates :1; + union { + struct { + /* For type slots (_PySlot_KIND_TYPE): + * Most slots corresponds to members in PyTypeObject & similar + * structs. + * Each entry has two offsets, "slot_offset" and "subslot_offset". + * If is subslot_offset is -1, slot_offset is an offset within the + * PyTypeObject struct. + * Otherwise slot_offset is an offset to a pointer to a sub-slots + * struct (such as "tp_as_number"), and subslot_offset is the + * offset within that struct. + * + * If both are zero, the slot needs special handling; that is, + * the offset mechanism isn't used. + */ + short subslot_offset; + short slot_offset; + } type_info; + struct { + /* For compat slots (_PySlot_KIND_COMPAT): + * these slots were renumbered; `type_id` is the ID of the new + * type slot; `mod_id` of the module slot. + */ + uint8_t type_id; + uint8_t mod_id; + } compat_info; + }; +} _PySlot_Info; + +// The actual table is generated by a script. +extern _PySlot_Info _PySlot_InfoTable[]; + +#define _PySlot_MAX_NESTING 5 + +/* State for one nesting level of a slots iterator */ +typedef struct _PySlotIterator_state { + union { + // tagged by slot_struct_kind: + PySlot *slot; // with _PySlot_KIND_SLOT + PyType_Slot *tp_slot; // with _PySlot_KIND_TYPE + PyModuleDef_Slot *mod_slot; // with _PySlot_KIND_MOD + }; + uint8_t slot_struct_kind; + bool ignoring_fallbacks :1; +} _PySlotIterator_state; + +/* State for a slots iterator */ +typedef struct { + _PySlotIterator_state *state; + _PySlotIterator_state states[_PySlot_MAX_NESTING]; + uint8_t kind; + uint8_t recursion_level; + unsigned int seen[_Py_slot_COUNT / sizeof(unsigned int) + 1]; + + /* Output information: */ + + const _PySlot_Info *info; + + // The slot. Always a copy; may be modified by caller of the iterator. + PySlot current; + + // Name of the object (type/module) being defined, NULL if unknown. + // Set by _PySlotIterator_Next as soon as it sees a tp_name/mod_name slot; + // used for error messages but available to the caller too. + // This points to the slot; must be copied for longer usage. + char *name; +} _PySlotIterator; + +PyAPI_FUNC(int) _PySlotIterator_InitWithKind( + _PySlotIterator *, void*, + int result_kind, int slot_struct_kind); + +static inline int +_PySlotIterator_Init(_PySlotIterator *it, PySlot *slots, int result_kind) +{ + return _PySlotIterator_InitWithKind(it, slots, result_kind, + _PySlot_KIND_SLOT); +} + +/* Iteration function */ +PyAPI_FUNC(int) _PySlotIterator_Next(_PySlotIterator *); +/* Additional validation (like rejecting duplicates); must be called after each + * _PySlotIterator_Next during the *first* time a particular slots array + * is iterated over. */ +PyAPI_FUNC(int) _PySlotIterator_ValidateCurrentSlot(_PySlotIterator *); + +/* Return 1 if given slot was "seen" by an earlier ValidateCurrentSlot call. */ +PyAPI_FUNC(bool) _PySlotIterator_SawSlot(_PySlotIterator *, int); + +static inline bool +_PySlot_IsStatic(PySlot *slot, const _PySlot_Info *info) +{ + return (slot->sl_flags & PySlot_STATIC) + || (info->dtype == _PySlot_TYPE_VOID) + || (info->dtype == _PySlot_TYPE_FUNC) + || (info->dtype == _PySlot_TYPE_SIZE) + || (info->dtype == _PySlot_TYPE_INT64) + || (info->dtype == _PySlot_TYPE_UINT64); +} + +#endif // _Py_PYCORE_SLOTS_H diff --git a/Include/pytypedefs.h b/Include/pytypedefs.h index e78ed56a3b67cd..1d0b4e3e85ef74 100644 --- a/Include/pytypedefs.h +++ b/Include/pytypedefs.h @@ -14,6 +14,7 @@ typedef struct PyModuleDef_Slot PyModuleDef_Slot; typedef struct PyMethodDef PyMethodDef; typedef struct PyGetSetDef PyGetSetDef; typedef struct PyMemberDef PyMemberDef; +typedef struct PySlot PySlot; typedef struct _object PyObject; typedef struct _longobject PyLongObject; diff --git a/Makefile.pre.in b/Makefile.pre.in index 8b46db33a2ac18..6f1efa8a638946 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1429,6 +1429,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_setobject.h \ $(srcdir)/Include/internal/pycore_signal.h \ $(srcdir)/Include/internal/pycore_sliceobject.h \ + $(srcdir)/Include/internal/pycore_slots.h \ $(srcdir)/Include/internal/pycore_stats.h \ $(srcdir)/Include/internal/pycore_strhex.h \ $(srcdir)/Include/internal/pycore_stackref.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 07305add81d055..220f9476d04b03 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -315,6 +315,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 629f063861de9a..3dbb810591a8ac 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -843,6 +843,9 @@ Include\internal + + Include\internal + Include\internal From 0396743c4c98d3e9f832628c959ed9441d355caf Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 15 Jan 2026 16:20:24 +0100 Subject: [PATCH 02/36] Add public API and a generated list of slots --- .gitattributes | 2 + Include/Python.h | 3 +- Include/moduleobject.h | 21 - Include/slots.h | 53 ++ Include/slots_generated.h | 121 ++++ Makefile.pre.in | 23 +- PCbuild/pythoncore.vcxproj | 4 + PCbuild/pythoncore.vcxproj.filters | 12 + Python/slots.c | 0 Python/slots.csv | 155 +++++ Python/slots_generated.c | 971 +++++++++++++++++++++++++++++ Tools/build/generate_slots.py | 276 ++++++++ configure | 2 +- configure.ac | 2 +- 14 files changed, 1612 insertions(+), 33 deletions(-) create mode 100644 Include/slots.h create mode 100644 Include/slots_generated.h create mode 100644 Python/slots.c create mode 100644 Python/slots.csv create mode 100644 Python/slots_generated.c create mode 100755 Tools/build/generate_slots.py diff --git a/.gitattributes b/.gitattributes index f4d65dfd1dfbd3..2debc1de7e58d7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -84,6 +84,7 @@ Include/internal/pycore_uop_ids.h generated Include/internal/pycore_uop_metadata.h generated Include/opcode.h generated Include/opcode_ids.h generated +Include/slots_generated.h generated Include/token.h generated Lib/_opcode_metadata.py generated Lib/idlelib/help.html generated @@ -110,6 +111,7 @@ Python/generated_cases.c.h generated Python/optimizer_cases.c.h generated Python/opcode_targets.h generated Python/record_functions.c.h generated +Python/slots_generated.c generated Python/stdlib_module_names.h generated Tools/peg_generator/pegen/grammar_parser.py generated aclocal.m4 generated diff --git a/Include/Python.h b/Include/Python.h index 8b76195b320998..b10fcdac9c1de0 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -79,7 +79,8 @@ __pragma(warning(disable: 4201)) #include "object.h" #include "refcount.h" #include "objimpl.h" -#include "typeslots.h" +#include "slots.h" +#include "slots_generated.h" #include "pyhash.h" #include "cpython/pydebug.h" #include "bytearrayobject.h" diff --git a/Include/moduleobject.h b/Include/moduleobject.h index c2fb1f85165f7d..ea11863a0d0273 100644 --- a/Include/moduleobject.h +++ b/Include/moduleobject.h @@ -73,27 +73,6 @@ struct PyModuleDef_Slot { void *value; }; -#define Py_mod_create 1 -#define Py_mod_exec 2 -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030c0000 -# define Py_mod_multiple_interpreters 3 -#endif -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000 -# define Py_mod_gil 4 -#endif -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15) -# define Py_mod_abi 5 -# define Py_mod_name 6 -# define Py_mod_doc 7 -# define Py_mod_state_size 8 -# define Py_mod_methods 9 -# define Py_mod_state_traverse 10 -# define Py_mod_state_clear 11 -# define Py_mod_state_free 12 -# define Py_mod_token 13 -#endif - - #ifndef Py_LIMITED_API #define _Py_mod_LAST_SLOT 13 #endif diff --git a/Include/slots.h b/Include/slots.h new file mode 100644 index 00000000000000..07633c112a8b95 --- /dev/null +++ b/Include/slots.h @@ -0,0 +1,53 @@ +#ifndef _Py_HAVE_SLOTS_H +#define _Py_HAVE_SLOTS_H + +struct PySlot { + uint16_t sl_id; + uint16_t sl_flags; + _Py_ANONYMOUS union { + uint32_t sl_reserved; + }; + _Py_ANONYMOUS union { + void *sl_ptr; + void (*sl_func)(void); + Py_ssize_t sl_size; + int64_t sl_int64; + uint64_t sl_uint64; + }; +}; + +#define PySlot_OPTIONAL 0x01 +#define PySlot_HAS_FALLBACK 0x02 +#define PySlot_STATIC 0x08 +#define PySlot_INTPTR 0x10 + +#define Py_slot_invalid 0xffff + +#define PySlot_DATA(NAME, VALUE) \ + {.sl_id=NAME, .sl_flags=PySlot_INTPTR, .sl_ptr=(void*)(VALUE)} + +#define PySlot_FUNC(NAME, VALUE) \ + {.sl_id=NAME, .sl_func=(VALUE)} + +#define PySlot_SIZE(NAME, VALUE) \ + {.sl_id=NAME, .sl_size=(Py_ssize_t)(VALUE)} + +#define PySlot_INT64(NAME, VALUE) \ + {.sl_id=NAME, .sl_int64=(int64_t)(VALUE)} + +#define PySlot_UINT64(NAME, VALUE) \ + {.sl_id=NAME, .sl_uint64=(uint64_t)(VALUE)} + +#define PySlot_STATIC_DATA(NAME, VALUE) \ + {.sl_id=NAME, .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)} + +#define PySlot_END {.sl_id=0} + + +#define PySlot_PTR(NAME, VALUE) \ + {NAME, PySlot_INTPTR, {0}, {(void*)VALUE}} + +#define PySlot_PTR_STATIC(NAME, VALUE) \ + {NAME, PySlot_INTPTR | PySlot_STATIC, {0}, {(void*)VALUE}} + +#endif // _Py_HAVE_SLOTS_H diff --git a/Include/slots_generated.h b/Include/slots_generated.h new file mode 100644 index 00000000000000..6ef5b252158b95 --- /dev/null +++ b/Include/slots_generated.h @@ -0,0 +1,121 @@ +/* Generated by Tools/build/generate_slots.py */ + +#ifndef _PY_HAVE_SLOTS_GENERATED_H +#define _PY_HAVE_SLOTS_GENERATED_H + +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15) +#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD +#else +#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD +#endif + +#define Py_slot_end 0 +#define Py_mp_subscript 5 +#define Py_nb_absolute 6 +#define Py_nb_add 7 +#define Py_nb_and 8 +#define Py_nb_bool 9 +#define Py_nb_divmod 10 +#define Py_nb_float 11 +#define Py_nb_floor_divide 12 +#define Py_nb_index 13 +#define Py_nb_inplace_add 14 +#define Py_nb_inplace_and 15 +#define Py_nb_inplace_floor_divide 16 +#define Py_nb_inplace_lshift 17 +#define Py_nb_inplace_multiply 18 +#define Py_nb_inplace_or 19 +#define Py_nb_inplace_power 20 +#define Py_nb_inplace_remainder 21 +#define Py_nb_inplace_rshift 22 +#define Py_nb_inplace_subtract 23 +#define Py_nb_inplace_true_divide 24 +#define Py_nb_inplace_xor 25 +#define Py_nb_int 26 +#define Py_nb_invert 27 +#define Py_nb_lshift 28 +#define Py_nb_multiply 29 +#define Py_nb_negative 30 +#define Py_nb_or 31 +#define Py_nb_positive 32 +#define Py_nb_power 33 +#define Py_nb_remainder 34 +#define Py_nb_rshift 35 +#define Py_nb_subtract 36 +#define Py_nb_true_divide 37 +#define Py_nb_xor 38 +#define Py_sq_ass_item 39 +#define Py_sq_concat 40 +#define Py_sq_contains 41 +#define Py_sq_inplace_concat 42 +#define Py_sq_inplace_repeat 43 +#define Py_sq_item 44 +#define Py_sq_length 45 +#define Py_sq_repeat 46 +#define Py_tp_alloc 47 +#define Py_tp_base 48 +#define Py_tp_bases 49 +#define Py_tp_call 50 +#define Py_tp_clear 51 +#define Py_tp_dealloc 52 +#define Py_tp_del 53 +#define Py_tp_descr_get 54 +#define Py_tp_descr_set 55 +#define Py_tp_doc 56 +#define Py_tp_getattr 57 +#define Py_tp_getattro 58 +#define Py_tp_hash 59 +#define Py_tp_init 60 +#define Py_tp_is_gc 61 +#define Py_tp_iter 62 +#define Py_tp_iternext 63 +#define Py_tp_methods 64 +#define Py_tp_new 65 +#define Py_tp_repr 66 +#define Py_tp_richcompare 67 +#define Py_tp_setattr 68 +#define Py_tp_setattro 69 +#define Py_tp_str 70 +#define Py_tp_traverse 71 +#define Py_tp_members 72 +#define Py_tp_getset 73 +#define Py_tp_free 74 +#define Py_nb_matrix_multiply 75 +#define Py_nb_inplace_matrix_multiply 76 +#define Py_am_await 77 +#define Py_am_aiter 78 +#define Py_am_anext 79 +#define Py_tp_finalize 80 +#define Py_am_send 81 +#define Py_tp_vectorcall 82 +#define Py_tp_token 83 +#define Py_mod_create _Py_SLOT_COMPAT_VALUE(1, 84) +#define Py_mod_exec _Py_SLOT_COMPAT_VALUE(2, 85) +#define Py_mod_multiple_interpreters _Py_SLOT_COMPAT_VALUE(3, 86) +#define Py_mod_gil _Py_SLOT_COMPAT_VALUE(4, 87) +#define Py_bf_getbuffer _Py_SLOT_COMPAT_VALUE(1, 88) +#define Py_bf_releasebuffer _Py_SLOT_COMPAT_VALUE(2, 89) +#define Py_mp_ass_subscript _Py_SLOT_COMPAT_VALUE(3, 90) +#define Py_mp_length _Py_SLOT_COMPAT_VALUE(4, 91) +#define Py_slot_subslots 92 +#define Py_tp_slots 93 +#define Py_mod_slots 94 +#define Py_tp_name 95 +#define Py_tp_basicsize 96 +#define Py_tp_extra_basicsize 97 +#define Py_tp_itemsize 98 +#define Py_tp_flags 99 +#define Py_mod_name 100 +#define Py_mod_doc 101 +#define Py_mod_state_size 102 +#define Py_mod_methods 103 +#define Py_mod_state_traverse 104 +#define Py_mod_state_clear 105 +#define Py_mod_state_free 106 +#define Py_tp_metaclass 107 +#define Py_tp_module 108 +#define Py_mod_abi 109 +#define Py_mod_token 110 + +#define _Py_slot_COUNT 111 +#endif diff --git a/Makefile.pre.in b/Makefile.pre.in index 6f1efa8a638946..46cef3104b418f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -497,6 +497,8 @@ PYTHON_OBJS= \ Python/qsbr.o \ Python/bootstrap_hash.o \ Python/specialize.o \ + Python/slots.o \ + Python/slots_generated.o \ Python/stackrefs.o \ Python/structmember.o \ Python/symtable.o \ @@ -1242,6 +1244,8 @@ PYTHON_HEADERS= \ $(srcdir)/Include/refcount.h \ $(srcdir)/Include/setobject.h \ $(srcdir)/Include/sliceobject.h \ + $(srcdir)/Include/slots.h \ + $(srcdir)/Include/slots_generated.h \ $(srcdir)/Include/structmember.h \ $(srcdir)/Include/structseq.h \ $(srcdir)/Include/sysmodule.h \ @@ -1939,7 +1943,7 @@ regen-unicodedata: # "clinic" is regenerated implicitly via "regen-global-objects". .PHONY: regen-all -regen-all: regen-cases regen-typeslots \ +regen-all: regen-cases regen-slots \ regen-token regen-ast regen-keyword regen-sre regen-frozen \ regen-pegen-metaparser regen-pegen regen-test-frozenmain \ regen-test-levenshtein regen-global-objects @@ -2312,16 +2316,17 @@ Python/import.o: $(srcdir)/Include/pydtrace.h Python/pydtrace.o: $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS) CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -G -s $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS) -Objects/typeobject.o: Objects/typeslots.inc - .PHONY: regen-typeslots regen-typeslots: - # Regenerate Objects/typeslots.inc from Include/typeslotsh - # using Objects/typeslots.py - $(PYTHON_FOR_REGEN) $(srcdir)/Objects/typeslots.py \ - < $(srcdir)/Include/typeslots.h \ - $(srcdir)/Objects/typeslots.inc.new - $(UPDATE_FILE) $(srcdir)/Objects/typeslots.inc $(srcdir)/Objects/typeslots.inc.new + echo 'NOTE: "regen-typeslots" was renamed to "regen-slots"' + $(MAKE) regen-slots + +.PHONY: regen-slots +regen-slots: + # Regenerate {Python,Include}/slots_generated.{c,h} + # from Python/slots.csv using Tools/build/generate_slots.py + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_slots.py \ + --generate-all $(LIBRARY_OBJS) $(MODOBJS) Programs/python.o: $(PYTHON_HEADERS) diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 220f9476d04b03..52bb9e1b691591 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -387,6 +387,8 @@ + + @@ -681,6 +683,8 @@ + + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 3dbb810591a8ac..aeab93e7ed3a64 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -228,6 +228,12 @@ Include + + Include + + + Include + Include @@ -1571,6 +1577,12 @@ Python + + Objects + + + Objects + Python diff --git a/Python/slots.c b/Python/slots.c new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Python/slots.csv b/Python/slots.csv new file mode 100644 index 00000000000000..1c616f54f7a60e --- /dev/null +++ b/Python/slots.csv @@ -0,0 +1,155 @@ +id, name, kind, dtype + +#, The syntax of this file is what currently works best for editing; +#, it can change at any time. Run `typeslots.py --jsonl` to get the info +#, in a more stable format. + +#, This file is a superset of CSV; readable with +#, csv.DictReader but also hopefully human-readable and -editable. +#, Perhaps CSV shouldn't be used this way; sue me. +#, +#, Comment lines start with '#' and a comma; that is they have '#' in the first +#, field. Completely blank lines should be ignored too. +#, Avoid commas in comments; you can use semicolons. +#, +#, Each non-comment line has these mandatory columns: +#, - id: the slot's unique number (or '#' for comments) +#, - name: the slot's name without "Py_" +#, - dtype: basic data type +#, - 'func'; 'ptr'; 'size'; 'uint64'; 'int64': the field of the union +#, - 'void': value is ignored +#, - kind: +#, - 'type' is for PyTypeObject slots +#, - 'mod' is for module slots +#, - 'slot' works for any kind; handled by the slot system itself +#, - 'compat' reserved for backwards compatibility +#, +#, After these there can be 0 or more "named" columns whose contents start with +#, with '='; everything after that is the value. No spaces around '='. +#, +#, For all types: +#, - 'added': Python version that added the slot *to the limited API* +#, - 'compat': slot number before 3.14 +#, - 'subslots=true': contains an array of slots to be merged in +#, For pointers: +#, - 'PyObject=true': This is a Python object (or NULL) +#, - 'null': Behavior for NULL values: +#, - 'reject': NULLs not allowed +#, - 'allow': NULLs allowed +#, - default is that NULL is deprecated +#, For type slots: +#, - 'table': Slot table like 'nb' or 'tp'. If not specified: use the first +#, part of the name +#, - 'virtual=true': Does not directly correspond to a PyTypeObject (sub)field + +0, "slot_end", slot, void, added=3.14 +1, "bf_getbuffer/mod_create", compat, void +2, "bf_releasebuffer/mod_exec", compat, void +3, "mp_ass_subscript/mod_multiple_interpreters", compat, void +4, "mp_length/mod_gil", compat, void +5, "mp_subscript", type, func +6, "nb_absolute", type, func +7, "nb_add", type, func +8, "nb_and", type, func +9, "nb_bool", type, func +10, "nb_divmod", type, func +11, "nb_float", type, func +12, "nb_floor_divide", type, func +13, "nb_index", type, func +14, "nb_inplace_add", type, func +15, "nb_inplace_and", type, func +16, "nb_inplace_floor_divide", type, func +17, "nb_inplace_lshift", type, func +18, "nb_inplace_multiply", type, func +19, "nb_inplace_or", type, func +20, "nb_inplace_power", type, func +21, "nb_inplace_remainder", type, func +22, "nb_inplace_rshift", type, func +23, "nb_inplace_subtract", type, func +24, "nb_inplace_true_divide", type, func +25, "nb_inplace_xor", type, func +26, "nb_int", type, func +27, "nb_invert", type, func +28, "nb_lshift", type, func +29, "nb_multiply", type, func +30, "nb_negative", type, func +31, "nb_or", type, func +32, "nb_positive", type, func +33, "nb_power", type, func +34, "nb_remainder", type, func +35, "nb_rshift", type, func +36, "nb_subtract", type, func +37, "nb_true_divide", type, func +38, "nb_xor", type, func +39, "sq_ass_item", type, func +40, "sq_concat", type, func +41, "sq_contains", type, func +42, "sq_inplace_concat", type, func +43, "sq_inplace_repeat", type, func +44, "sq_item", type, func +45, "sq_length", type, func +46, "sq_repeat", type, func +47, "tp_alloc", type, func +48, "tp_base", type, ptr, PyObject=true +49, "tp_bases", type, ptr, PyObject=true +50, "tp_call", type, func +51, "tp_clear", type, func +52, "tp_dealloc", type, func +53, "tp_del", type, func +54, "tp_descr_get", type, func +55, "tp_descr_set", type, func +56, "tp_doc", type, ptr, null=allow, duplicates=no +57, "tp_getattr", type, func +58, "tp_getattro", type, func +59, "tp_hash", type, func +60, "tp_init", type, func +61, "tp_is_gc", type, func +62, "tp_iter", type, func +63, "tp_iternext", type, func +64, "tp_methods", type, ptr, duplicates=no +65, "tp_new", type, func +66, "tp_repr", type, func +67, "tp_richcompare", type, func +68, "tp_setattr", type, func +69, "tp_setattro", type, func +70, "tp_str", type, func +71, "tp_traverse", type, func +72, "tp_members", type, ptr, duplicates=no +73, "tp_getset", type, ptr, duplicates=no +74, "tp_free", type, func +75, "nb_matrix_multiply", type, func +76, "nb_inplace_matrix_multiply", type, func +77, "am_await", type, func +78, "am_aiter", type, func +79, "am_anext", type, func +80, "tp_finalize", type, func, added=3.5 +81, "am_send", type, func, added=3.10 +82, "tp_vectorcall", type, func, added=3.14 +83, "tp_token", type, ptr, added=3.14, table=ht, field=ht_token, null=allow, duplicates=no +84, "mod_create", mod, func, compat=1, duplicates=no +85, "mod_exec", mod, func, compat=2, duplicates=yes +86, "mod_multiple_interpreters", mod, uint64, compat=3, added=3.12, duplicates=no +87, "mod_gil", mod, uint64, compat=4, added=3.13, duplicates=no +88, "bf_getbuffer", type, func, compat=1, added=3.14 +89, "bf_releasebuffer", type, func, compat=2, added=3.14 +90, "mp_ass_subscript", type, func, compat=3, added=3.14 +91, "mp_length", type, func, compat=4, added=3.14 +92, "slot_subslots", slot, ptr, added=3.14, subslots=true, null=allow +93, "tp_slots", type, ptr, virtual=true, added=3.14, subslots=true, null=allow +94, "mod_slots", mod, ptr, added=3.14, subslots=true, null=allow +95, "tp_name", type, ptr, virtual=true, added=3.14, null=reject, duplicates=no, is_name=true +96, "tp_basicsize", type, size, added=3.14, duplicates=no +97, "tp_extra_basicsize", type, size, virtual=true, added=3.14, duplicates=no +98, "tp_itemsize", type, size, added=3.14, duplicates=no +99, "tp_flags", type, uint64, added=3.14, duplicates=no +100, "mod_name", mod, ptr, added=3.14, null=reject, duplicates=no, is_name=true +101, "mod_doc", mod, ptr, added=3.14, null=allow, duplicates=no +102, "mod_state_size", mod, size, added=3.14, duplicates=no +103, "mod_methods", mod, ptr, added=3.14, null=reject, duplicates=no +104, "mod_state_traverse", mod, func, added=3.14, null=reject, duplicates=no +105, "mod_state_clear", mod, func, added=3.14, null=reject, duplicates=no +106, "mod_state_free", mod, func, added=3.14, null=reject, duplicates=no +107, "tp_metaclass", type, ptr, virtual=true, PyObject=true, added=3.14, null=reject, duplicates=no +108, "tp_module", type, ptr, virtual=true, PyObject=true, added=3.14, null=reject, duplicates=no +109, "mod_abi", mod, ptr, added=3.15, null=reject, duplicates=no +110, "mod_token", mod, ptr, added=3.15, null=reject, duplicates=no diff --git a/Python/slots_generated.c b/Python/slots_generated.c new file mode 100644 index 00000000000000..83677d8115a16a --- /dev/null +++ b/Python/slots_generated.c @@ -0,0 +1,971 @@ +/* Generated by Tools/build/generate_slots.py */ + +#include "Python.h" +#include "pycore_slots.h" // _PySlot_Info +#include // offsetof +#include // true + +_PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { + [0] = { + .name = "slot_end", + .dtype = _PySlot_TYPE_VOID, + .kind = _PySlot_KIND_SLOT, + .null_handling = _PySlot_NULL_ALLOW, + .deprecate_duplicates = true, + }, + [1] = { + .name = "bf_getbuffer/mod_create", + .dtype = _PySlot_TYPE_VOID, + .kind = _PySlot_KIND_COMPAT, + .compat_info.mod_id = 84, + .compat_info.type_id = 88, + .null_handling = _PySlot_NULL_ALLOW, + .deprecate_duplicates = true, + }, + [2] = { + .name = "bf_releasebuffer/mod_exec", + .dtype = _PySlot_TYPE_VOID, + .kind = _PySlot_KIND_COMPAT, + .compat_info.mod_id = 85, + .compat_info.type_id = 89, + .null_handling = _PySlot_NULL_ALLOW, + .deprecate_duplicates = true, + }, + [3] = { + .name = "mp_ass_subscript/mod_multiple_interpreters", + .dtype = _PySlot_TYPE_VOID, + .kind = _PySlot_KIND_COMPAT, + .compat_info.mod_id = 86, + .compat_info.type_id = 90, + .null_handling = _PySlot_NULL_ALLOW, + .deprecate_duplicates = true, + }, + [4] = { + .name = "mp_length/mod_gil", + .dtype = _PySlot_TYPE_VOID, + .kind = _PySlot_KIND_COMPAT, + .compat_info.mod_id = 87, + .compat_info.type_id = 91, + .null_handling = _PySlot_NULL_ALLOW, + .deprecate_duplicates = true, + }, + [5] = { + .name = "mp_subscript", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), + .type_info.subslot_offset = offsetof(PyMappingMethods, mp_subscript), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [6] = { + .name = "nb_absolute", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_absolute), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [7] = { + .name = "nb_add", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_add), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [8] = { + .name = "nb_and", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_and), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [9] = { + .name = "nb_bool", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_bool), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [10] = { + .name = "nb_divmod", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_divmod), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [11] = { + .name = "nb_float", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_float), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [12] = { + .name = "nb_floor_divide", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_floor_divide), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [13] = { + .name = "nb_index", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_index), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [14] = { + .name = "nb_inplace_add", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_add), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [15] = { + .name = "nb_inplace_and", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_and), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [16] = { + .name = "nb_inplace_floor_divide", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_floor_divide), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [17] = { + .name = "nb_inplace_lshift", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_lshift), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [18] = { + .name = "nb_inplace_multiply", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_multiply), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [19] = { + .name = "nb_inplace_or", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_or), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [20] = { + .name = "nb_inplace_power", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_power), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [21] = { + .name = "nb_inplace_remainder", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_remainder), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [22] = { + .name = "nb_inplace_rshift", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_rshift), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [23] = { + .name = "nb_inplace_subtract", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_subtract), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [24] = { + .name = "nb_inplace_true_divide", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_true_divide), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [25] = { + .name = "nb_inplace_xor", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_xor), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [26] = { + .name = "nb_int", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_int), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [27] = { + .name = "nb_invert", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_invert), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [28] = { + .name = "nb_lshift", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_lshift), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [29] = { + .name = "nb_multiply", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_multiply), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [30] = { + .name = "nb_negative", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_negative), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [31] = { + .name = "nb_or", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_or), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [32] = { + .name = "nb_positive", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_positive), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [33] = { + .name = "nb_power", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_power), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [34] = { + .name = "nb_remainder", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_remainder), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [35] = { + .name = "nb_rshift", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_rshift), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [36] = { + .name = "nb_subtract", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_subtract), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [37] = { + .name = "nb_true_divide", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_true_divide), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [38] = { + .name = "nb_xor", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_xor), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [39] = { + .name = "sq_ass_item", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), + .type_info.subslot_offset = offsetof(PySequenceMethods, sq_ass_item), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [40] = { + .name = "sq_concat", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), + .type_info.subslot_offset = offsetof(PySequenceMethods, sq_concat), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [41] = { + .name = "sq_contains", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), + .type_info.subslot_offset = offsetof(PySequenceMethods, sq_contains), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [42] = { + .name = "sq_inplace_concat", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), + .type_info.subslot_offset = offsetof(PySequenceMethods, sq_inplace_concat), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [43] = { + .name = "sq_inplace_repeat", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), + .type_info.subslot_offset = offsetof(PySequenceMethods, sq_inplace_repeat), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [44] = { + .name = "sq_item", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), + .type_info.subslot_offset = offsetof(PySequenceMethods, sq_item), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [45] = { + .name = "sq_length", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), + .type_info.subslot_offset = offsetof(PySequenceMethods, sq_length), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [46] = { + .name = "sq_repeat", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), + .type_info.subslot_offset = offsetof(PySequenceMethods, sq_repeat), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [47] = { + .name = "tp_alloc", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_alloc), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [48] = { + .name = "tp_base", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_base), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [49] = { + .name = "tp_bases", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_bases), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [50] = { + .name = "tp_call", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_call), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [51] = { + .name = "tp_clear", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_clear), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [52] = { + .name = "tp_dealloc", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_dealloc), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [53] = { + .name = "tp_del", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_del), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [54] = { + .name = "tp_descr_get", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_descr_get), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [55] = { + .name = "tp_descr_set", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_descr_set), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [56] = { + .name = "tp_doc", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_doc), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [57] = { + .name = "tp_getattr", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_getattr), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [58] = { + .name = "tp_getattro", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_getattro), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [59] = { + .name = "tp_hash", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_hash), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [60] = { + .name = "tp_init", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_init), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [61] = { + .name = "tp_is_gc", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_is_gc), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [62] = { + .name = "tp_iter", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_iter), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [63] = { + .name = "tp_iternext", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_iternext), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [64] = { + .name = "tp_methods", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_methods), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .reject_duplicates = true, + }, + [65] = { + .name = "tp_new", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_new), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [66] = { + .name = "tp_repr", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_repr), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [67] = { + .name = "tp_richcompare", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_richcompare), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [68] = { + .name = "tp_setattr", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_setattr), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [69] = { + .name = "tp_setattro", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_setattro), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [70] = { + .name = "tp_str", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_str), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [71] = { + .name = "tp_traverse", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_traverse), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [72] = { + .name = "tp_members", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_members), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .reject_duplicates = true, + }, + [73] = { + .name = "tp_getset", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_getset), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .reject_duplicates = true, + }, + [74] = { + .name = "tp_free", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_free), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [75] = { + .name = "nb_matrix_multiply", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_matrix_multiply), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [76] = { + .name = "nb_inplace_matrix_multiply", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), + .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_matrix_multiply), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [77] = { + .name = "am_await", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), + .type_info.subslot_offset = offsetof(PyAsyncMethods, am_await), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [78] = { + .name = "am_aiter", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), + .type_info.subslot_offset = offsetof(PyAsyncMethods, am_aiter), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [79] = { + .name = "am_anext", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), + .type_info.subslot_offset = offsetof(PyAsyncMethods, am_anext), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [80] = { + .name = "tp_finalize", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_finalize), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [81] = { + .name = "am_send", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), + .type_info.subslot_offset = offsetof(PyAsyncMethods, am_send), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [82] = { + .name = "tp_vectorcall", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_vectorcall), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [83] = { + .name = "tp_token", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyHeapTypeObject, ht_token), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [84] = { + .name = "mod_create", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_DEPRECATED, + .reject_duplicates = true, + }, + [85] = { + .name = "mod_exec", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_DEPRECATED, + }, + [86] = { + .name = "mod_multiple_interpreters", + .dtype = _PySlot_TYPE_UINT64, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [87] = { + .name = "mod_gil", + .dtype = _PySlot_TYPE_UINT64, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [88] = { + .name = "bf_getbuffer", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_buffer), + .type_info.subslot_offset = offsetof(PyBufferProcs, bf_getbuffer), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [89] = { + .name = "bf_releasebuffer", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_buffer), + .type_info.subslot_offset = offsetof(PyBufferProcs, bf_releasebuffer), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [90] = { + .name = "mp_ass_subscript", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), + .type_info.subslot_offset = offsetof(PyMappingMethods, mp_ass_subscript), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [91] = { + .name = "mp_length", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), + .type_info.subslot_offset = offsetof(PyMappingMethods, mp_length), + .null_handling = _PySlot_NULL_DEPRECATED, + .deprecate_duplicates = true, + }, + [92] = { + .name = "slot_subslots", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_SLOT, + .is_subslots = true, + .null_handling = _PySlot_NULL_ALLOW, + .deprecate_duplicates = true, + }, + [93] = { + .name = "tp_slots", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .is_subslots = true, + .null_handling = _PySlot_NULL_ALLOW, + .deprecate_duplicates = true, + }, + [94] = { + .name = "mod_slots", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_MOD, + .is_subslots = true, + .null_handling = _PySlot_NULL_ALLOW, + .deprecate_duplicates = true, + }, + [95] = { + .name = "tp_name", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + .is_name = true, + }, + [96] = { + .name = "tp_basicsize", + .dtype = _PySlot_TYPE_SIZE, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_basicsize), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [97] = { + .name = "tp_extra_basicsize", + .dtype = _PySlot_TYPE_SIZE, + .kind = _PySlot_KIND_TYPE, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [98] = { + .name = "tp_itemsize", + .dtype = _PySlot_TYPE_SIZE, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_itemsize), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [99] = { + .name = "tp_flags", + .dtype = _PySlot_TYPE_UINT64, + .kind = _PySlot_KIND_TYPE, + .type_info.slot_offset = offsetof(PyTypeObject, tp_flags), + .type_info.subslot_offset = -1, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [100] = { + .name = "mod_name", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + .is_name = true, + }, + [101] = { + .name = "mod_doc", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [102] = { + .name = "mod_state_size", + .dtype = _PySlot_TYPE_SIZE, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_ALLOW, + .reject_duplicates = true, + }, + [103] = { + .name = "mod_methods", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + }, + [104] = { + .name = "mod_state_traverse", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + }, + [105] = { + .name = "mod_state_clear", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + }, + [106] = { + .name = "mod_state_free", + .dtype = _PySlot_TYPE_FUNC, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + }, + [107] = { + .name = "tp_metaclass", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + }, + [108] = { + .name = "tp_module", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_TYPE, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + }, + [109] = { + .name = "mod_abi", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + }, + [110] = { + .name = "mod_token", + .dtype = _PySlot_TYPE_PTR, + .kind = _PySlot_KIND_MOD, + .null_handling = _PySlot_NULL_REJECT, + .reject_duplicates = true, + }, + [111] = {0} +}; diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py new file mode 100755 index 00000000000000..2e25b67e972ad2 --- /dev/null +++ b/Tools/build/generate_slots.py @@ -0,0 +1,276 @@ +#!/usr/bin/python +"""Generate type/module slot files. +""" + +import io +import csv +import sys +import json +import argparse +import functools +import contextlib +from pathlib import Path + +GENERATED_BY = 'Generated by Tools/build/generate_slots.py' + +REPO_ROOT = Path(__file__).parent.parent.parent +DEFAULT_INPUT_PATH = REPO_ROOT / 'Python/slots.csv' +DEFAULT_HEADER_PATH = REPO_ROOT / 'Include/slots_generated.h' +DEFAULT_C_PATH = REPO_ROOT / 'Python/slots_generated.c' + +class SlotInfo: + def __init__(self, **info_dict): + self._d = info_dict + self.id = int(self['id']) + assert self.id >= 0 + self.name = self['name'] + self.kind = self['kind'] + self.dtype = self['dtype'] + + def to_dict(self): + return self._d + + def __getitem__(self, name): + return self._d[name] + + def get(self, name, default=None): + return self._d.get(name, default) + + def get_int(self, name, default=None): + try: + return int(self._d[name]) + except KeyError: + return default + + def get_bool(self, name, default=None): + try: + value = self._d[name] + except KeyError: + return default + return {'true': True, 'false': False}[value] + + def __repr__(self): + return f'<{type(self).__name__} {self._d}>' + + @functools.cached_property + def added(self): + added = self._d.get('added') + if added is None: + return None + x, y = added.split('.') + return int(x), int(y) + + @functools.cached_property + def type_table(self): + try: + return self['table'] + except KeyError: + return self.name.partition('_')[0] + + @functools.cached_property + def initializers_for_duplicates(self): + match self.get('duplicates'): + case 'no': + return {'reject_duplicates': 'true'} + case 'yes': + return {} + return {'deprecate_duplicates': 'true'} + + +def parse_slots(lines): + result = [] + reader = csv.DictReader(lines, restkey='named', skipinitialspace=True) + expected_id = 0 + for info_dict in reader: + if info_dict.get('id', '#') == '#': + continue + for kv in info_dict.pop('named', ()): + key, eq, value = kv.partition('=') + info_dict[key] = value + slot = SlotInfo(**info_dict) + if slot.id != expected_id: + raise ValueError( + 'for now, slots must be defined in order with no gaps ' + + f'({expected_id=}, got={info_dict}') + expected_id += 1 + result.append(slot) + return result + + +def write_header(f, slots): + out = functools.partial(print, file=f) + out(f'/* {GENERATED_BY} */') + out() + out(f'#ifndef _PY_HAVE_SLOTS_GENERATED_H') + out(f'#define _PY_HAVE_SLOTS_GENERATED_H') + out() + out(f'#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)') + out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD') + out(f'#else') + out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD') + out(f'#endif') + out() + for slot in slots: + if slot.kind == 'compat': + continue + slot_id = slot.id + if compat := slot.get('compat'): + slot_id = f'_Py_SLOT_COMPAT_VALUE({compat}, {slot.id})' + out(f'#define Py_{slot.name} {slot_id}') + out() + out(f'#define _Py_slot_COUNT {len(slots)}') + out(f'#endif') + + +def write_c(f, slots): + out = functools.partial(print, file=f) + out(f'/* {GENERATED_BY} */') + out() + out('#include "Python.h"') + out('#include "pycore_slots.h" // _PySlot_Info') + out('#include // offsetof') + out('#include // true') + out() + out(f'_PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = {{') + + assert len(slots) == max(slot.id for slot in slots) + 1 + for slot in slots: + try: + out(f" [{slot.id}] = {{") + initializers = { + "name": f'"{slot.name}"', + "dtype": f'_PySlot_TYPE_{slot.dtype.upper()}', + "kind": f'_PySlot_KIND_{slot.kind.upper()}', + } + match slot.kind: + case 'slot': + pass + case 'compat': + for newslot in slots: + if newslot.get_int('compat') == slot.id: + key = f'compat_info.{newslot.kind}_id' + initializers[key] = newslot.id + case 'type': + field = slot.get('field', slot.name) + if not slot.get_bool('virtual'): + tpo = 'PyTypeObject' + typeobj, subtable, tabletype = { + 'tp': (tpo, None, None), + 'mp': (tpo, 'tp_as_mapping', 'PyMappingMethods'), + 'nb': (tpo, 'tp_as_number', 'PyNumberMethods'), + 'sq': (tpo, 'tp_as_sequence', 'PySequenceMethods'), + 'am': (tpo, 'tp_as_async', 'PyAsyncMethods'), + 'bf': (tpo, 'tp_as_buffer', 'PyBufferProcs'), + 'ht': ('PyHeapTypeObject', None, None), + }[slot.type_table] + if subtable: + initializers['type_info.slot_offset'] = ( + f'offsetof({typeobj}, {subtable})') + initializers['type_info.subslot_offset'] = ( + f'offsetof({tabletype}, {field})') + else: + initializers['type_info.slot_offset'] = ( + f'offsetof({typeobj}, {field})') + initializers['type_info.subslot_offset'] = '-1' + case 'mod': + pass + case _: + raise ValueError(slot.kind) + if slot.get_bool('subslots'): + initializers['is_subslots'] = 'true' + match slot.get('null'): + case 'reject': + initializers['null_handling'] = '_PySlot_NULL_REJECT' + case 'allow': + initializers['null_handling'] = '_PySlot_NULL_ALLOW' + case None: + if slot.dtype in {'func', 'ptr'}: + if slot.id > 92: + raise ValueError( + f'slot {slot.name} needs explicit NULL ' + + 'handling specification') + initializers['null_handling'] = ( + '_PySlot_NULL_DEPRECATED') + else: + initializers['null_handling'] = '_PySlot_NULL_ALLOW' + initializers.update(slot.initializers_for_duplicates) + if slot.get_bool('is_name'): + initializers['is_name'] = 'true' + for name, initializer in initializers.items(): + out(f' .{name} = {initializer},') + out(f" }},") + except Exception as e: + e.add_note(f'handling slot {slot}') + raise e + out(f" [{len(slots)}] = {{0}}") + out(f"}};") + + + +@contextlib.contextmanager +def replace_file(filename): + file_path = Path(filename) + with io.StringIO() as sio: + yield sio + try: + old_text = file_path.read_text() + except FileNotFoundError: + old_text = None + new_text = sio.getvalue() + if old_text == new_text: + print(f'{filename}: not modified', file=sys.stderr) + else: + print(f'{filename}: writing new content', file=sys.stderr) + file_path.write_text(new_text) + + +def main(argv): + if len(argv) == 1: + # No sens calling this with no arguments. + argv.append('--help') + + parser = argparse.ArgumentParser(prog=argv[0], description=__doc__) + parser.add_argument( + '-i', '--input', default=DEFAULT_INPUT_PATH, + help=f'the input file (default: {DEFAULT_INPUT_PATH})') + parser.add_argument( + '--generate-all', action=argparse.BooleanOptionalAction, + help='write all output files to their default locations') + parser.add_argument( + '-j', '--jsonl', action=argparse.BooleanOptionalAction, + help='write info to stdout in "JSON Lines" format (one JSON per line)') + outfile_group = parser.add_argument_group( + 'output files', + description='By default, no files are generated. Use --generate-all ' + + 'or the options below to generate them.') + outfile_group.add_argument( + '-H', '--header', + help='file into which to write the public header') + outfile_group.add_argument( + '-C', '--cfile', + help='file into which to write internal C code') + args = parser.parse_args(argv[1:]) + + if args.generate_all: + if args.header is None: + args.header = DEFAULT_HEADER_PATH + if args.cfile is None: + args.cfile = DEFAULT_C_PATH + + with open(args.input) as f: + slots = parse_slots(f) + + if args.jsonl: + for slot in slots: + print(json.dumps(slot.to_dict())) + + if args.header: + with replace_file(args.header) as f: + write_header(f, slots) + + if args.cfile: + with replace_file(args.cfile) as f: + write_c(f, slots) + +if __name__ == "__main__": + main(sys.argv) diff --git a/configure b/configure index 6cd7a1900463ee..386f6e16fa2ab7 100755 --- a/configure +++ b/configure @@ -3596,7 +3596,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "$srcdir" != . -a "$srcdir" != "$(pwd)"; then # If we're building out-of-tree, we need to make sure the following # resources get picked up before their $srcdir counterparts. - # Objects/ -> typeslots.inc + # Objects/ -> slots_generated.c # Include/ -> Python.h # (A side effect of this is that these resources will automatically be # regenerated when building out-of-tree, regardless of whether or not diff --git a/configure.ac b/configure.ac index 60511db39fad1e..fe13f3b2fbf61a 100644 --- a/configure.ac +++ b/configure.ac @@ -99,7 +99,7 @@ AC_SUBST([BASECPPFLAGS]) if test "$srcdir" != . -a "$srcdir" != "$(pwd)"; then # If we're building out-of-tree, we need to make sure the following # resources get picked up before their $srcdir counterparts. - # Objects/ -> typeslots.inc + # Objects/ -> slots_generated.c # Include/ -> Python.h # (A side effect of this is that these resources will automatically be # regenerated when building out-of-tree, regardless of whether or not From 6da3bb19b104bf51a1f577e353563dee7f002fd1 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 15 Jan 2026 16:59:49 +0100 Subject: [PATCH 03/36] Add slot iterator implementation & use it for types --- Include/internal/pycore_slots.h | 96 +++++---- Objects/typeobject.c | 47 ++-- Python/slots.c | 369 ++++++++++++++++++++++++++++++++ 3 files changed, 455 insertions(+), 57 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index 285b0918d25510..c170a66d76220f 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -7,28 +7,34 @@ #include -#define _PySlot_TYPE_VOID 1 -#define _PySlot_TYPE_FUNC 2 -#define _PySlot_TYPE_PTR 3 -#define _PySlot_TYPE_SIZE 4 -#define _PySlot_TYPE_INT64 5 -#define _PySlot_TYPE_UINT64 6 - -#define _PySlot_KIND_TYPE 1 -#define _PySlot_KIND_MOD 2 -#define _PySlot_KIND_COMPAT 0x10 -#define _PySlot_KIND_SLOT 0x20 - -#define _PySlot_NULL_DEPRECATED 0 -#define _PySlot_NULL_REJECT 1 -#define _PySlot_NULL_ALLOW 2 +typedef enum _PySlot_TYPE { + _PySlot_TYPE_VOID, + _PySlot_TYPE_FUNC, + _PySlot_TYPE_PTR, + _PySlot_TYPE_SIZE, + _PySlot_TYPE_INT64, + _PySlot_TYPE_UINT64, +}_PySlot_TYPE; + +typedef enum _PySlot_KIND { + _PySlot_KIND_TYPE, + _PySlot_KIND_MOD, + _PySlot_KIND_COMPAT, + _PySlot_KIND_SLOT, +} _PySlot_KIND; + +typedef enum _PySlot_NULL_HANDLING { + _PySlot_NULL_DEPRECATED, + _PySlot_NULL_REJECT, + _PySlot_NULL_ALLOW, +} _PySlot_NULL_HANDLING; /* Internal information about a slot */ typedef struct _PySlot_Info { - const char *name; - uint8_t dtype; /* _PySlot_TYPE_* */ - uint8_t kind; /* _PySlot_KIND */ - uint8_t null_handling; // for pointers (incl. functions) + const char *name; /* without the Py_ prefix */ + _PySlot_TYPE dtype; + _PySlot_KIND kind; + _PySlot_NULL_HANDLING null_handling; bool is_subslots :1; bool is_name :1; bool reject_duplicates :1; @@ -74,8 +80,9 @@ typedef struct _PySlotIterator_state { PySlot *slot; // with _PySlot_KIND_SLOT PyType_Slot *tp_slot; // with _PySlot_KIND_TYPE PyModuleDef_Slot *mod_slot; // with _PySlot_KIND_MOD + void *any_slot; }; - uint8_t slot_struct_kind; + _PySlot_KIND slot_struct_kind; bool ignoring_fallbacks :1; } _PySlotIterator_state; @@ -83,43 +90,60 @@ typedef struct _PySlotIterator_state { typedef struct { _PySlotIterator_state *state; _PySlotIterator_state states[_PySlot_MAX_NESTING]; - uint8_t kind; + _PySlot_KIND kind; uint8_t recursion_level; unsigned int seen[_Py_slot_COUNT / sizeof(unsigned int) + 1]; + bool is_at_end :1; + bool is_first_run :1; /* Output information: */ - const _PySlot_Info *info; - // The slot. Always a copy; may be modified by caller of the iterator. PySlot current; + // Information about the slot + const _PySlot_Info *info; + // Name of the object (type/module) being defined, NULL if unknown. // Set by _PySlotIterator_Next as soon as it sees a tp_name/mod_name slot; - // used for error messages but available to the caller too. + // used for internal error messages but available to the caller too. // This points to the slot; must be copied for longer usage. + // The name is not reset by rewinding. char *name; } _PySlotIterator; -PyAPI_FUNC(int) _PySlotIterator_InitWithKind( +/* Initialize a iterator. Currently cannot fail. */ +PyAPI_FUNC(void) _PySlotIterator_InitWithKind( _PySlotIterator *, void*, - int result_kind, int slot_struct_kind); + _PySlot_KIND result_kind, _PySlot_KIND slot_struct_kind); -static inline int -_PySlotIterator_Init(_PySlotIterator *it, PySlot *slots, int result_kind) +static inline void +_PySlotIterator_Init(_PySlotIterator *it, PySlot *slots, + _PySlot_KIND result_kind) { return _PySlotIterator_InitWithKind(it, slots, result_kind, _PySlot_KIND_SLOT); } -/* Iteration function */ -PyAPI_FUNC(int) _PySlotIterator_Next(_PySlotIterator *); -/* Additional validation (like rejecting duplicates); must be called after each - * _PySlotIterator_Next during the *first* time a particular slots array - * is iterated over. */ -PyAPI_FUNC(int) _PySlotIterator_ValidateCurrentSlot(_PySlotIterator *); - -/* Return 1 if given slot was "seen" by an earlier ValidateCurrentSlot call. */ +/* Reset a *successfully exhausted* iterator to the beginning. + * The *slots* must be the same as for the previous + * `_PySlotIterator_InitWithKind` call. + * (Subsequent iterations skip some validation.) + */ +PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, void *slots); + +/* Iteration function. + * + * Return false at the end (when successfully exhausted). + * Otherwise (even on error), fill output information in `it` and return true. + * + * On error, set an exception and set `it->current.sl_id` to `Py_slot_invalid`. + */ +PyAPI_FUNC(bool) _PySlotIterator_Next(_PySlotIterator *it); + +/* Return 1 if given slot was "seen" by an earlier _PySlotIterator_Next call. + * (This state is not reset by rewiding.) + */ PyAPI_FUNC(bool) _PySlotIterator_SawSlot(_PySlotIterator *, int); static inline bool diff --git a/Objects/typeobject.c b/Objects/typeobject.c index fb3c7101410683..158c3abc159ae6 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -18,6 +18,7 @@ #include "pycore_pyatomic_ft_wrappers.h" #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_slots.h" // _PySlotIterator_Init #include "pycore_symtable.h" // _Py_Mangle() #include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_typeobject.h" // struct type_cache @@ -5297,20 +5298,19 @@ PyType_FromMetaclass( * if that would cause trouble (leaks, UB, ...), raise an exception. */ - const PyType_Slot *slot; Py_ssize_t nmembers = 0; const PyMemberDef *weaklistoffset_member = NULL; const PyMemberDef *dictoffset_member = NULL; const PyMemberDef *vectorcalloffset_member = NULL; char *res_start; - for (slot = spec->slots; slot->slot; slot++) { - if (slot->slot < 0 - || (size_t)slot->slot >= Py_ARRAY_LENGTH(pyslot_offsets)) { - PyErr_SetString(PyExc_RuntimeError, "invalid slot offset"); + _PySlotIterator it; + _PySlotIterator_InitWithKind(&it, spec->slots, + _PySlot_KIND_TYPE, _PySlot_KIND_TYPE); + while (_PySlotIterator_Next(&it)) { + switch (it.current.sl_id) { + case Py_slot_invalid: goto finally; - } - switch (slot->slot) { case Py_tp_members: if (nmembers != 0) { PyErr_SetString( @@ -5318,7 +5318,7 @@ PyType_FromMetaclass( "Multiple Py_tp_members slots are not supported."); goto finally; } - for (const PyMemberDef *memb = slot->pfunc; memb->name != NULL; memb++) { + for (const PyMemberDef *memb = it.current.sl_ptr; memb->name != NULL; memb++) { nmembers++; if (memb->flags & Py_RELATIVE_OFFSET) { if (spec->basicsize > 0) { @@ -5354,18 +5354,18 @@ PyType_FromMetaclass( "Multiple Py_tp_doc slots are not supported."); goto finally; } - if (slot->pfunc == NULL) { + if (it.current.sl_ptr == NULL) { PyMem_Free(tp_doc); tp_doc = NULL; } else { - size_t len = strlen(slot->pfunc)+1; + size_t len = strlen(it.current.sl_ptr)+1; tp_doc = PyMem_Malloc(len); if (tp_doc == NULL) { PyErr_NoMemory(); goto finally; } - memcpy(tp_doc, slot->pfunc, len); + memcpy(tp_doc, it.current.sl_ptr, len); } break; } @@ -5554,8 +5554,11 @@ PyType_FromMetaclass( /* Copy all the ordinary slots */ - for (slot = spec->slots; slot->slot; slot++) { - switch (slot->slot) { + _PySlotIterator_Rewind(&it, spec->slots); + while (_PySlotIterator_Next(&it)) { + switch (it.current.sl_id) { + case Py_slot_invalid: + goto finally; case Py_tp_base: case Py_tp_bases: case Py_tp_doc: @@ -5565,7 +5568,7 @@ PyType_FromMetaclass( { /* Move the slots to the heap type itself */ size_t len = Py_TYPE(type)->tp_itemsize * nmembers; - memcpy(_PyHeapType_GET_MEMBERS(res), slot->pfunc, len); + memcpy(_PyHeapType_GET_MEMBERS(res), it.current.sl_ptr, len); type->tp_members = _PyHeapType_GET_MEMBERS(res); PyMemberDef *memb; Py_ssize_t i; @@ -5581,22 +5584,24 @@ PyType_FromMetaclass( break; case Py_tp_token: { - res->ht_token = slot->pfunc == Py_TP_USE_SPEC ? spec : slot->pfunc; + res->ht_token = it.current.sl_ptr == Py_TP_USE_SPEC ? spec : it.current.sl_ptr; } break; default: { /* Copy other slots directly */ - PySlot_Offset slotoffsets = pyslot_offsets[slot->slot]; - short slot_offset = slotoffsets.slot_offset; - if (slotoffsets.subslot_offset == -1) { + short slot_offset = it.info->type_info.slot_offset; + short subslot_offset = it.info->type_info.subslot_offset; + if (slot_offset == 0 && subslot_offset == 0) { + /* slot should have been processed above */ + } + else if (subslot_offset == -1) { /* Set a slot in the main PyTypeObject */ - *(void**)((char*)res_start + slot_offset) = slot->pfunc; + *(void**)((char*)res_start + slot_offset) = it.current.sl_func; } else { void *procs = *(void**)((char*)res_start + slot_offset); - short subslot_offset = slotoffsets.subslot_offset; - *(void**)((char*)procs + subslot_offset) = slot->pfunc; + *(void**)((char*)procs + subslot_offset) = it.current.sl_func; } } break; diff --git a/Python/slots.c b/Python/slots.c index e69de29bb2d1d6..d7a973b14743eb 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -0,0 +1,369 @@ +/* Common handling of type/module slots + */ + +#include "Python.h" + +#include "pycore_slots.h" // _PySlot_Info + +#include + +// Iterating through a recursive structure doesn't look great in a debugger. +// Define this to get a trace on stderr. +// (The messages can also serve as code comments.) +#if 0 +#define MSG(...) { \ + fprintf(stderr, "slotiter: " __VA_ARGS__); fprintf(stderr, "\n");} +#else +#define MSG(...) +#endif + +static int validate_current_slot(_PySlotIterator *it); + +static char* +kind_name(_PySlot_KIND kind) +{ + switch (kind) { + case _PySlot_KIND_TYPE: return "type"; + case _PySlot_KIND_MOD: return "module"; + case _PySlot_KIND_COMPAT: return "compat"; + case _PySlot_KIND_SLOT: return "generic slot"; + } + Py_UNREACHABLE(); + return ""; +} + +void +_PySlotIterator_InitWithKind(_PySlotIterator *it, void *slots, + _PySlot_KIND result_kind, + _PySlot_KIND slot_struct_kind) +{ + MSG(""); + MSG("init (%s slot iterator)", kind_name(result_kind)); + it->state = it->states; + it->state->any_slot = slots; + it->state->slot_struct_kind = slot_struct_kind; + it->state->ignoring_fallbacks = false; + it->kind = result_kind; + it->name = NULL; + it->recursion_level = 0; + it->is_at_end = false; + it->is_first_run = true; + it->current.sl_id = 0; + memset(it->seen, 0, sizeof(it->seen)); +} + +void +_PySlotIterator_Rewind(_PySlotIterator *it, void *slots) +{ + MSG(""); + MSG("rewind (%s slot iterator)", kind_name(it->kind)); + assert (it->is_at_end); + assert (it->recursion_level == 0); + assert (it->state == it->states); + assert (!it->state->ignoring_fallbacks); + it->is_at_end = false; + it->state->any_slot = slots; + it->is_first_run = false; +} + +static Py_ssize_t +seen_index(uint16_t id) +{ + return id / sizeof(unsigned int); +} + +static unsigned int +seen_mask(uint16_t id) +{ + return ((unsigned int)1) << (id % sizeof(unsigned int)); +} + +bool +_PySlotIterator_SawSlot(_PySlotIterator *it, int id) +{ + assert (id > 0); + assert (id < _Py_slot_COUNT); + return it->seen[seen_index(id)] & seen_mask(id); +} + +// Advance `it` to the next entry. Currently cannot fail. +static void +advance(_PySlotIterator *it) +{ + MSG("advance (at level %d)", (int)it->recursion_level); + switch (it->state->slot_struct_kind) { + case _PySlot_KIND_SLOT: it->state->slot++; break; + case _PySlot_KIND_TYPE: it->state->tp_slot++; break; + case _PySlot_KIND_MOD: it->state->mod_slot++; break; + default: + Py_UNREACHABLE(); + } +} + +bool +_PySlotIterator_Next(_PySlotIterator *it) +{ + MSG("next"); + assert(it); + assert(!it->is_at_end); + + it->current.sl_id = -1; + + while (true) { + if (it->state->slot == NULL) { + if (it->recursion_level == 0) { + MSG("end (initial nesting level done)"); + it->is_at_end = true; + return 0; + } + MSG("pop nesting level %d", (int)it->recursion_level); + it->recursion_level--; + it->state = &it->states[it->recursion_level]; + advance(it); + continue; + } + + /* Convert legacy structure */ + switch (it->state->slot_struct_kind) { + case _PySlot_KIND_SLOT: { + MSG("copying PySlot structure"); + it->current = *it->state->slot; /* struct copy */ + } break; + case _PySlot_KIND_TYPE: { + MSG("converting PyType_Slot structure"); + memset(&it->current, 0, sizeof(it->current)); + it->current.sl_id = it->state->tp_slot->slot; + it->current.sl_flags = PySlot_INTPTR; + it->current.sl_ptr = (void*)it->state->tp_slot->pfunc; + } break; + case _PySlot_KIND_MOD: { + MSG("converting PyModuleDef_Slot structure"); + memset(&it->current, 0, sizeof(it->current)); + it->current.sl_id = it->state->mod_slot->slot; + it->current.sl_flags = PySlot_INTPTR; + it->current.sl_ptr = (void*)it->state->mod_slot->value; + } break; + default: { + Py_UNREACHABLE(); + } break; + } + + /* shorter local names */ + PySlot *const result = &it->current; + uint16_t flags = result->sl_flags; + + MSG("slot %d, flags 0x%x, from %p", + (int)result->sl_id, (unsigned)flags, it->state->slot); + + if (it->state->ignoring_fallbacks) { + if (!(flags & PySlot_HAS_FALLBACK)) { + MSG("stopping to ignore fallbacks"); + it->state->ignoring_fallbacks = false; + } + MSG("skipped (ignoring fallbacks)"); + advance(it); + continue; + } + if (result->sl_id >= _Py_slot_COUNT) { + if (flags & (PySlot_OPTIONAL | PySlot_HAS_FALLBACK)) { + MSG("skipped (unknown slot)"); + advance(it); + continue; + } + MSG("error (unknown slot)"); + PyErr_Format(PyExc_SystemError, + "unknown slot ID %u", (unsigned int)result->sl_id); + goto error; + } + if (result->sl_id == Py_slot_end) { + flags &= ~PySlot_INTPTR; + MSG("sentinel slot, flags %x", (unsigned)flags); + if (flags == PySlot_OPTIONAL) { + MSG("skipped (optional sentinel)"); + advance(it); + continue; + } + const uint16_t bad_flags = PySlot_OPTIONAL | PySlot_HAS_FALLBACK; + if (flags & bad_flags) { + MSG("error (bad flags on sentinel)"); + PyErr_Format(PyExc_SystemError, + "invalid flags for Py_slot_end: 0x%x", + (unsigned int)flags); + goto error; + } + it->state->slot = NULL; + continue; + } + it->info = &_PySlot_InfoTable[result->sl_id]; + MSG("slot %d: %s", (int)result->sl_id, it->info->name); + + if (it->is_first_run && it->info->is_name) { + MSG("setting name for error messages"); + assert(it->info->dtype == _PySlot_TYPE_PTR); + it->name = result->sl_ptr; + } + + // Resolve a legacy ambiguous slot number. + // Save the original slot info for error messages. + uint16_t orig_id = result->sl_id; + _PySlot_Info *orig_info = &_PySlot_InfoTable[result->sl_id]; + if (it->info->kind == _PySlot_KIND_COMPAT) { + MSG("resolving compat slot"); + switch (it->kind) { + case _PySlot_KIND_TYPE: { + result->sl_id = it->info->compat_info.type_id; + } break; + case _PySlot_KIND_MOD: { + result->sl_id = it->info->compat_info.mod_id; + } break; + default: { + Py_UNREACHABLE(); + } break; + } + it->info = &_PySlot_InfoTable[result->sl_id]; + MSG("slot %d: %s", (int)result->sl_id, it->info->name); + } + + if (it->info->is_subslots) { + if (result->sl_ptr == NULL) { + MSG("NULL subslots; skipping"); + advance(it); + continue; + } + it->recursion_level++; + MSG("recursing into level %d", it->recursion_level); + if (it->recursion_level >= _PySlot_MAX_NESTING) { + MSG("error (too much nesting)"); + PyErr_Format(PyExc_SystemError, + "Py_%s (slot %d): too many levels of nested slots", + orig_info->name, orig_id); + goto error; + } + it->state = &it->states[it->recursion_level]; + memset(it->state, 0, sizeof(_PySlotIterator_state)); + it->state->slot = result->sl_ptr; + it->state->slot_struct_kind = it->info->kind; + continue; + } + + if (flags & PySlot_INTPTR) { + MSG("casting from intptr"); + switch (it->info->dtype) { + case _PySlot_TYPE_SIZE: { + result->sl_size = (Py_ssize_t)(intptr_t)result->sl_ptr; + } break; + case _PySlot_TYPE_INT64: { + result->sl_int64 = (int64_t)(intptr_t)result->sl_ptr; + } break; + case _PySlot_TYPE_UINT64: { + result->sl_uint64 = (uint64_t)(intptr_t)result->sl_ptr; + } break; + case _PySlot_TYPE_PTR: + case _PySlot_TYPE_FUNC: + case _PySlot_TYPE_VOID: + break; + } + } + + if (flags & PySlot_HAS_FALLBACK) { + MSG("starting to ignore fallbacks"); + it->state->ignoring_fallbacks = true; + } + + advance(it); + MSG("result: %d (%s)", (int)result->sl_id, it->info->name); + assert (result->sl_id > 0); + assert (result->sl_id <= INT_MAX); + if (it->is_first_run && validate_current_slot(it) < 0) { + goto error; + } + return result->sl_id != Py_slot_end; + } + Py_UNREACHABLE(); + +error: + it->current.sl_id = Py_slot_invalid; + return true; +} + +static int +validate_current_slot(_PySlotIterator *it) +{ + const _PySlot_Info *info = it->info; + int id = it->current.sl_id; + + if (it->info->kind != it->kind) { + MSG("error (bad slot kind)"); + PyErr_Format(PyExc_SystemError, + "Py_%s (slot %d) is not compatible with %ss", + info->name, + id, + kind_name(it->kind)); + return -1; + } + + if (it->info->null_handling != _PySlot_NULL_ALLOW) { + bool is_null = false; + switch (it->info->dtype) { + case _PySlot_TYPE_PTR: { + is_null = it->current.sl_ptr == NULL; + } break; + case _PySlot_TYPE_FUNC: { + is_null = it->current.sl_func == NULL; + } break; + default: { + Py_UNREACHABLE(); + } break; + } + if (is_null) { + if (it->info->null_handling == _PySlot_NULL_DEPRECATED) { + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + 1, + "NULL value in slot Py_%s is deprecated", + it->info->name) < 0) + { + return -1; + } + } + else { + PyErr_Format(PyExc_SystemError, + "NULL not allowed for slot %s", + it->info->name); + return -1; + } + } + } + + if (info->reject_duplicates || info->deprecate_duplicates) { + if (_PySlotIterator_SawSlot(it, id)) { + MSG("slot was seen before"); + if (info->reject_duplicates) { + MSG("error (duplicate rejected)"); + PyErr_Format( + PyExc_SystemError, + "%s%s%s has multiple Py_%s (%d) slots", + kind_name(it->kind), + it->name ? " " : "", + it->name ? it->name : "", + info->name, + (int)it->current.sl_id); + return -1; + } + MSG("deprecated duplicate"); + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + 0, + "%s%s%s has multiple Py_%s (%d) slots. This is deprecated.", + kind_name(it->kind), + it->name ? " " : "", + it->name ? it->name : "", + info->name, + (int)it->current.sl_id) < 0) { + return -1; + } + } + } + it->seen[seen_index(id)] |= seen_mask(id); + return 0; +} From a8e018fd8b7921fd9c034ca0440ec695f3606861 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 16 Jan 2026 15:05:14 +0100 Subject: [PATCH 04/36] Unify duplicate & NULL handling --- Include/internal/pycore_slots.h | 15 +- Python/slots.c | 31 +-- Python/slots_generated.c | 430 ++++++++++++++++---------------- Tools/build/generate_slots.py | 12 +- 4 files changed, 237 insertions(+), 251 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index c170a66d76220f..d5cfd9f85f188d 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -23,22 +23,21 @@ typedef enum _PySlot_KIND { _PySlot_KIND_SLOT, } _PySlot_KIND; -typedef enum _PySlot_NULL_HANDLING { - _PySlot_NULL_DEPRECATED, - _PySlot_NULL_REJECT, - _PySlot_NULL_ALLOW, -} _PySlot_NULL_HANDLING; +typedef enum _PySlot_PROBLEM_HANDLING { + _PySlot_PROBLEM_ALLOW = 0, + _PySlot_PROBLEM_DEPRECATED, + _PySlot_PROBLEM_REJECT, +} _PySlot_PROBLEM_HANDLING; /* Internal information about a slot */ typedef struct _PySlot_Info { const char *name; /* without the Py_ prefix */ _PySlot_TYPE dtype; _PySlot_KIND kind; - _PySlot_NULL_HANDLING null_handling; + _PySlot_PROBLEM_HANDLING null_handling; + _PySlot_PROBLEM_HANDLING duplicate_handling; bool is_subslots :1; bool is_name :1; - bool reject_duplicates :1; - bool deprecate_duplicates :1; union { struct { /* For type slots (_PySlot_KIND_TYPE): diff --git a/Python/slots.c b/Python/slots.c index d7a973b14743eb..0b563f6c7ecd73 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -302,7 +302,7 @@ validate_current_slot(_PySlotIterator *it) return -1; } - if (it->info->null_handling != _PySlot_NULL_ALLOW) { + if (it->info->null_handling != _PySlot_PROBLEM_ALLOW) { bool is_null = false; switch (it->info->dtype) { case _PySlot_TYPE_PTR: { @@ -316,29 +316,30 @@ validate_current_slot(_PySlotIterator *it) } break; } if (is_null) { - if (it->info->null_handling == _PySlot_NULL_DEPRECATED) { - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, - 1, - "NULL value in slot Py_%s is deprecated", - it->info->name) < 0) - { - return -1; - } - } - else { + MSG("slot is NULL but shouldn't"); + if (it->info->null_handling == _PySlot_PROBLEM_REJECT) { + MSG("error (NULL rejected)"); PyErr_Format(PyExc_SystemError, "NULL not allowed for slot %s", it->info->name); return -1; } + MSG("deprecated NULL"); + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + 1, + "NULL value in slot Py_%s is deprecated", + it->info->name) < 0) + { + return -1; + } } } - if (info->reject_duplicates || info->deprecate_duplicates) { + if (info->duplicate_handling != _PySlot_PROBLEM_ALLOW) { if (_PySlotIterator_SawSlot(it, id)) { - MSG("slot was seen before"); - if (info->reject_duplicates) { + MSG("slot was seen before but shouldn't be duplicated"); + if (info->duplicate_handling == _PySlot_PROBLEM_REJECT) { MSG("error (duplicate rejected)"); PyErr_Format( PyExc_SystemError, diff --git a/Python/slots_generated.c b/Python/slots_generated.c index 83677d8115a16a..9cecd53229be8b 100644 --- a/Python/slots_generated.c +++ b/Python/slots_generated.c @@ -10,8 +10,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .name = "slot_end", .dtype = _PySlot_TYPE_VOID, .kind = _PySlot_KIND_SLOT, - .null_handling = _PySlot_NULL_ALLOW, - .deprecate_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [1] = { .name = "bf_getbuffer/mod_create", @@ -19,8 +18,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_COMPAT, .compat_info.mod_id = 84, .compat_info.type_id = 88, - .null_handling = _PySlot_NULL_ALLOW, - .deprecate_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [2] = { .name = "bf_releasebuffer/mod_exec", @@ -28,8 +26,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_COMPAT, .compat_info.mod_id = 85, .compat_info.type_id = 89, - .null_handling = _PySlot_NULL_ALLOW, - .deprecate_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [3] = { .name = "mp_ass_subscript/mod_multiple_interpreters", @@ -37,8 +34,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_COMPAT, .compat_info.mod_id = 86, .compat_info.type_id = 90, - .null_handling = _PySlot_NULL_ALLOW, - .deprecate_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [4] = { .name = "mp_length/mod_gil", @@ -46,8 +42,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_COMPAT, .compat_info.mod_id = 87, .compat_info.type_id = 91, - .null_handling = _PySlot_NULL_ALLOW, - .deprecate_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [5] = { .name = "mp_subscript", @@ -55,8 +50,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), .type_info.subslot_offset = offsetof(PyMappingMethods, mp_subscript), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [6] = { .name = "nb_absolute", @@ -64,8 +59,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_absolute), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [7] = { .name = "nb_add", @@ -73,8 +68,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_add), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [8] = { .name = "nb_and", @@ -82,8 +77,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_and), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [9] = { .name = "nb_bool", @@ -91,8 +86,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_bool), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [10] = { .name = "nb_divmod", @@ -100,8 +95,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_divmod), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [11] = { .name = "nb_float", @@ -109,8 +104,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_float), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [12] = { .name = "nb_floor_divide", @@ -118,8 +113,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_floor_divide), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [13] = { .name = "nb_index", @@ -127,8 +122,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_index), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [14] = { .name = "nb_inplace_add", @@ -136,8 +131,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_add), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [15] = { .name = "nb_inplace_and", @@ -145,8 +140,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_and), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [16] = { .name = "nb_inplace_floor_divide", @@ -154,8 +149,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_floor_divide), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [17] = { .name = "nb_inplace_lshift", @@ -163,8 +158,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_lshift), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [18] = { .name = "nb_inplace_multiply", @@ -172,8 +167,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_multiply), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [19] = { .name = "nb_inplace_or", @@ -181,8 +176,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_or), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [20] = { .name = "nb_inplace_power", @@ -190,8 +185,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_power), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [21] = { .name = "nb_inplace_remainder", @@ -199,8 +194,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_remainder), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [22] = { .name = "nb_inplace_rshift", @@ -208,8 +203,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_rshift), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [23] = { .name = "nb_inplace_subtract", @@ -217,8 +212,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_subtract), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [24] = { .name = "nb_inplace_true_divide", @@ -226,8 +221,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_true_divide), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [25] = { .name = "nb_inplace_xor", @@ -235,8 +230,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_xor), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [26] = { .name = "nb_int", @@ -244,8 +239,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_int), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [27] = { .name = "nb_invert", @@ -253,8 +248,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_invert), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [28] = { .name = "nb_lshift", @@ -262,8 +257,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_lshift), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [29] = { .name = "nb_multiply", @@ -271,8 +266,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_multiply), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [30] = { .name = "nb_negative", @@ -280,8 +275,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_negative), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [31] = { .name = "nb_or", @@ -289,8 +284,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_or), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [32] = { .name = "nb_positive", @@ -298,8 +293,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_positive), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [33] = { .name = "nb_power", @@ -307,8 +302,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_power), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [34] = { .name = "nb_remainder", @@ -316,8 +311,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_remainder), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [35] = { .name = "nb_rshift", @@ -325,8 +320,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_rshift), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [36] = { .name = "nb_subtract", @@ -334,8 +329,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_subtract), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [37] = { .name = "nb_true_divide", @@ -343,8 +338,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_true_divide), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [38] = { .name = "nb_xor", @@ -352,8 +347,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_xor), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [39] = { .name = "sq_ass_item", @@ -361,8 +356,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), .type_info.subslot_offset = offsetof(PySequenceMethods, sq_ass_item), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [40] = { .name = "sq_concat", @@ -370,8 +365,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), .type_info.subslot_offset = offsetof(PySequenceMethods, sq_concat), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [41] = { .name = "sq_contains", @@ -379,8 +374,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), .type_info.subslot_offset = offsetof(PySequenceMethods, sq_contains), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [42] = { .name = "sq_inplace_concat", @@ -388,8 +383,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), .type_info.subslot_offset = offsetof(PySequenceMethods, sq_inplace_concat), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [43] = { .name = "sq_inplace_repeat", @@ -397,8 +392,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), .type_info.subslot_offset = offsetof(PySequenceMethods, sq_inplace_repeat), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [44] = { .name = "sq_item", @@ -406,8 +401,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), .type_info.subslot_offset = offsetof(PySequenceMethods, sq_item), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [45] = { .name = "sq_length", @@ -415,8 +410,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), .type_info.subslot_offset = offsetof(PySequenceMethods, sq_length), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [46] = { .name = "sq_repeat", @@ -424,8 +419,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), .type_info.subslot_offset = offsetof(PySequenceMethods, sq_repeat), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [47] = { .name = "tp_alloc", @@ -433,8 +428,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_alloc), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [48] = { .name = "tp_base", @@ -442,8 +437,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_base), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [49] = { .name = "tp_bases", @@ -451,8 +446,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_bases), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [50] = { .name = "tp_call", @@ -460,8 +455,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_call), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [51] = { .name = "tp_clear", @@ -469,8 +464,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_clear), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [52] = { .name = "tp_dealloc", @@ -478,8 +473,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_dealloc), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [53] = { .name = "tp_del", @@ -487,8 +482,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_del), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [54] = { .name = "tp_descr_get", @@ -496,8 +491,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_descr_get), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [55] = { .name = "tp_descr_set", @@ -505,8 +500,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_descr_set), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [56] = { .name = "tp_doc", @@ -514,8 +509,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_doc), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_ALLOW, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [57] = { .name = "tp_getattr", @@ -523,8 +518,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_getattr), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [58] = { .name = "tp_getattro", @@ -532,8 +527,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_getattro), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [59] = { .name = "tp_hash", @@ -541,8 +536,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_hash), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [60] = { .name = "tp_init", @@ -550,8 +545,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_init), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [61] = { .name = "tp_is_gc", @@ -559,8 +554,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_is_gc), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [62] = { .name = "tp_iter", @@ -568,8 +563,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_iter), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [63] = { .name = "tp_iternext", @@ -577,8 +572,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_iternext), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [64] = { .name = "tp_methods", @@ -586,8 +581,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_methods), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [65] = { .name = "tp_new", @@ -595,8 +590,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_new), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [66] = { .name = "tp_repr", @@ -604,8 +599,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_repr), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [67] = { .name = "tp_richcompare", @@ -613,8 +608,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_richcompare), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [68] = { .name = "tp_setattr", @@ -622,8 +617,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_setattr), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [69] = { .name = "tp_setattro", @@ -631,8 +626,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_setattro), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [70] = { .name = "tp_str", @@ -640,8 +635,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_str), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [71] = { .name = "tp_traverse", @@ -649,8 +644,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_traverse), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [72] = { .name = "tp_members", @@ -658,8 +653,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_members), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [73] = { .name = "tp_getset", @@ -667,8 +662,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_getset), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [74] = { .name = "tp_free", @@ -676,8 +671,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_free), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [75] = { .name = "nb_matrix_multiply", @@ -685,8 +680,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_matrix_multiply), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [76] = { .name = "nb_inplace_matrix_multiply", @@ -694,8 +689,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_matrix_multiply), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [77] = { .name = "am_await", @@ -703,8 +698,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), .type_info.subslot_offset = offsetof(PyAsyncMethods, am_await), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [78] = { .name = "am_aiter", @@ -712,8 +707,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), .type_info.subslot_offset = offsetof(PyAsyncMethods, am_aiter), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [79] = { .name = "am_anext", @@ -721,8 +716,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), .type_info.subslot_offset = offsetof(PyAsyncMethods, am_anext), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [80] = { .name = "tp_finalize", @@ -730,8 +725,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_finalize), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [81] = { .name = "am_send", @@ -739,8 +734,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), .type_info.subslot_offset = offsetof(PyAsyncMethods, am_send), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [82] = { .name = "tp_vectorcall", @@ -748,8 +743,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_vectorcall), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [83] = { .name = "tp_token", @@ -757,35 +752,33 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyHeapTypeObject, ht_token), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_ALLOW, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [84] = { .name = "mod_create", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_DEPRECATED, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [85] = { .name = "mod_exec", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_DEPRECATED, + .null_handling = _PySlot_PROBLEM_DEPRECATED, }, [86] = { .name = "mod_multiple_interpreters", .dtype = _PySlot_TYPE_UINT64, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [87] = { .name = "mod_gil", .dtype = _PySlot_TYPE_UINT64, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [88] = { .name = "bf_getbuffer", @@ -793,8 +786,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_buffer), .type_info.subslot_offset = offsetof(PyBufferProcs, bf_getbuffer), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [89] = { .name = "bf_releasebuffer", @@ -802,8 +795,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_buffer), .type_info.subslot_offset = offsetof(PyBufferProcs, bf_releasebuffer), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [90] = { .name = "mp_ass_subscript", @@ -811,8 +804,8 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), .type_info.subslot_offset = offsetof(PyMappingMethods, mp_ass_subscript), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [91] = { .name = "mp_length", @@ -820,39 +813,39 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), .type_info.subslot_offset = offsetof(PyMappingMethods, mp_length), - .null_handling = _PySlot_NULL_DEPRECATED, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_DEPRECATED, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [92] = { .name = "slot_subslots", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_SLOT, .is_subslots = true, - .null_handling = _PySlot_NULL_ALLOW, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_ALLOW, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [93] = { .name = "tp_slots", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .is_subslots = true, - .null_handling = _PySlot_NULL_ALLOW, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_ALLOW, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [94] = { .name = "mod_slots", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, .is_subslots = true, - .null_handling = _PySlot_NULL_ALLOW, - .deprecate_duplicates = true, + .null_handling = _PySlot_PROBLEM_ALLOW, + .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [95] = { .name = "tp_name", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, .is_name = true, }, [96] = { @@ -861,15 +854,13 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_basicsize), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [97] = { .name = "tp_extra_basicsize", .dtype = _PySlot_TYPE_SIZE, .kind = _PySlot_KIND_TYPE, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [98] = { .name = "tp_itemsize", @@ -877,8 +868,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_itemsize), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [99] = { .name = "tp_flags", @@ -886,86 +876,84 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_flags), .type_info.subslot_offset = -1, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [100] = { .name = "mod_name", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, .is_name = true, }, [101] = { .name = "mod_doc", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_ALLOW, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [102] = { .name = "mod_state_size", .dtype = _PySlot_TYPE_SIZE, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_ALLOW, - .reject_duplicates = true, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [103] = { .name = "mod_methods", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [104] = { .name = "mod_state_traverse", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [105] = { .name = "mod_state_clear", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [106] = { .name = "mod_state_free", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [107] = { .name = "tp_metaclass", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [108] = { .name = "tp_module", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [109] = { .name = "mod_abi", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [110] = { .name = "mod_token", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_NULL_REJECT, - .reject_duplicates = true, + .null_handling = _PySlot_PROBLEM_REJECT, + .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [111] = {0} }; diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index 2e25b67e972ad2..1c2e23048ad6c2 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -71,10 +71,10 @@ def type_table(self): def initializers_for_duplicates(self): match self.get('duplicates'): case 'no': - return {'reject_duplicates': 'true'} + return {'duplicate_handling': '_PySlot_PROBLEM_REJECT'} case 'yes': return {} - return {'deprecate_duplicates': 'true'} + return {'duplicate_handling': '_PySlot_PROBLEM_DEPRECATED'} def parse_slots(lines): @@ -180,9 +180,9 @@ def write_c(f, slots): initializers['is_subslots'] = 'true' match slot.get('null'): case 'reject': - initializers['null_handling'] = '_PySlot_NULL_REJECT' + initializers['null_handling'] = '_PySlot_PROBLEM_REJECT' case 'allow': - initializers['null_handling'] = '_PySlot_NULL_ALLOW' + initializers['null_handling'] = '_PySlot_PROBLEM_ALLOW' case None: if slot.dtype in {'func', 'ptr'}: if slot.id > 92: @@ -190,9 +190,7 @@ def write_c(f, slots): f'slot {slot.name} needs explicit NULL ' + 'handling specification') initializers['null_handling'] = ( - '_PySlot_NULL_DEPRECATED') - else: - initializers['null_handling'] = '_PySlot_NULL_ALLOW' + '_PySlot_PROBLEM_DEPRECATED') initializers.update(slot.initializers_for_duplicates) if slot.get_bool('is_name'): initializers['is_name'] = 'true' From b96acd47f2b3fd3cb71f182f830c6ac44cc15512 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 16 Jan 2026 15:25:42 +0100 Subject: [PATCH 05/36] Simplify slot handling in typeobject.c --- Objects/typeobject.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 158c3abc159ae6..4f2ec33d00d904 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5312,12 +5312,6 @@ PyType_FromMetaclass( case Py_slot_invalid: goto finally; case Py_tp_members: - if (nmembers != 0) { - PyErr_SetString( - PyExc_SystemError, - "Multiple Py_tp_members slots are not supported."); - goto finally; - } for (const PyMemberDef *memb = it.current.sl_ptr; memb->name != NULL; memb++) { nmembers++; if (memb->flags & Py_RELATIVE_OFFSET) { @@ -5584,7 +5578,12 @@ PyType_FromMetaclass( break; case Py_tp_token: { - res->ht_token = it.current.sl_ptr == Py_TP_USE_SPEC ? spec : it.current.sl_ptr; + if (it.current.sl_ptr == Py_TP_USE_SPEC) { + res->ht_token = spec; + } + else { + res->ht_token = it.current.sl_ptr; + } } break; default: From f3a6d7c8908c6b625c1f98b3b6c724bf78c960b7 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 16 Jan 2026 16:03:14 +0100 Subject: [PATCH 06/36] Port PyType_GetSlot; remove typeslots.inc --- .gitattributes | 1 - Makefile.pre.in | 1 - Objects/typeobject.c | 45 ++++++++++------------- Objects/typeslots.inc | 84 ------------------------------------------- Objects/typeslots.py | 55 ---------------------------- 5 files changed, 19 insertions(+), 167 deletions(-) delete mode 100644 Objects/typeslots.inc delete mode 100755 Objects/typeslots.py diff --git a/.gitattributes b/.gitattributes index 2debc1de7e58d7..4d52f900b93179 100644 --- a/.gitattributes +++ b/.gitattributes @@ -100,7 +100,6 @@ Lib/token.py generated Misc/sbom.spdx.json generated Modules/_testinternalcapi/test_cases.c.h generated Modules/_testinternalcapi/test_targets.h generated -Objects/typeslots.inc generated PC/python3dll.c generated Parser/parser.c generated Parser/token.c generated diff --git a/Makefile.pre.in b/Makefile.pre.in index 46cef3104b418f..7218183124d792 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1251,7 +1251,6 @@ PYTHON_HEADERS= \ $(srcdir)/Include/sysmodule.h \ $(srcdir)/Include/traceback.h \ $(srcdir)/Include/tupleobject.h \ - $(srcdir)/Include/typeslots.h \ $(srcdir)/Include/unicodeobject.h \ $(srcdir)/Include/warnings.h \ $(srcdir)/Include/weakrefobject.h \ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 4f2ec33d00d904..b790095be28aca 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -199,11 +199,6 @@ type_lock_allow_release(void) #define PyTypeObject_CAST(op) ((PyTypeObject *)(op)) -typedef struct PySlot_Offset { - short subslot_offset; - short slot_offset; -} PySlot_Offset; - static void slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer); @@ -5113,21 +5108,6 @@ type_vectorcall(PyObject *metatype, PyObject *const *args, return _PyObject_MakeTpCall(tstate, metatype, args, nargs, kwnames); } -/* An array of type slot offsets corresponding to Py_tp_* constants, - * for use in e.g. PyType_Spec and PyType_GetSlot. - * Each entry has two offsets: "slot_offset" and "subslot_offset". - * If is subslot_offset is -1, slot_offset is an offset within the - * PyTypeObject struct. - * Otherwise slot_offset is an offset to a pointer to a sub-slots struct - * (such as "tp_as_number"), and subslot_offset is the offset within - * that struct. - * The actual table is generated by a script. - */ -static const PySlot_Offset pyslot_offsets[] = { - {0, 0}, -#include "typeslots.inc" -}; - /* Align up to the nearest multiple of alignof(max_align_t) * (like _Py_ALIGN_UP, but for a size rather than pointer) */ @@ -5746,13 +5726,26 @@ void * PyType_GetSlot(PyTypeObject *type, int slot) { void *parent_slot; - int slots_len = Py_ARRAY_LENGTH(pyslot_offsets); - - if (slot <= 0 || slot >= slots_len) { + if (slot <= 0 || slot >= _Py_slot_COUNT) { + PyErr_BadInternalCall(); + return NULL; + } + _PySlot_Info *slot_info = &_PySlot_InfoTable[slot]; + if (slot_info->kind != _PySlot_KIND_TYPE) { + if (slot_info->kind != _PySlot_KIND_COMPAT) { + PyErr_BadInternalCall(); + return NULL; + } + slot = slot_info->compat_info.type_id; + slot_info = &_PySlot_InfoTable[slot]; + assert(slot_info->kind == _PySlot_KIND_TYPE); + } + short slot_offset = slot_info->type_info.slot_offset; + short subslot_offset = slot_info->type_info.subslot_offset; + if ((slot_offset == 0) && (subslot_offset == 0)) { PyErr_BadInternalCall(); return NULL; } - int slot_offset = pyslot_offsets[slot].slot_offset; if (slot_offset >= (int)sizeof(PyTypeObject)) { if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { @@ -5765,10 +5758,10 @@ PyType_GetSlot(PyTypeObject *type, int slot) return NULL; } /* Return slot directly if we have no sub slot. */ - if (pyslot_offsets[slot].subslot_offset == -1) { + if (subslot_offset == -1) { return parent_slot; } - return *(void**)((char*)parent_slot + pyslot_offsets[slot].subslot_offset); + return *(void**)((char*)parent_slot + subslot_offset); } PyObject * diff --git a/Objects/typeslots.inc b/Objects/typeslots.inc deleted file mode 100644 index 642160fe0bd8bc..00000000000000 --- a/Objects/typeslots.inc +++ /dev/null @@ -1,84 +0,0 @@ -/* Generated by typeslots.py */ -{offsetof(PyBufferProcs, bf_getbuffer), offsetof(PyTypeObject, tp_as_buffer)}, -{offsetof(PyBufferProcs, bf_releasebuffer), offsetof(PyTypeObject, tp_as_buffer)}, -{offsetof(PyMappingMethods, mp_ass_subscript), offsetof(PyTypeObject, tp_as_mapping)}, -{offsetof(PyMappingMethods, mp_length), offsetof(PyTypeObject, tp_as_mapping)}, -{offsetof(PyMappingMethods, mp_subscript), offsetof(PyTypeObject, tp_as_mapping)}, -{offsetof(PyNumberMethods, nb_absolute), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_add), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_and), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_bool), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_divmod), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_float), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_floor_divide), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_index), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_add), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_and), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_floor_divide), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_lshift), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_multiply), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_or), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_power), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_remainder), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_rshift), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_subtract), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_true_divide), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_xor), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_int), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_invert), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_lshift), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_multiply), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_negative), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_or), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_positive), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_power), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_remainder), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_rshift), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_subtract), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_true_divide), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_xor), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PySequenceMethods, sq_ass_item), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_concat), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_contains), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_inplace_concat), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_inplace_repeat), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_item), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_length), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_repeat), offsetof(PyTypeObject, tp_as_sequence)}, -{-1, offsetof(PyTypeObject, tp_alloc)}, -{-1, offsetof(PyTypeObject, tp_base)}, -{-1, offsetof(PyTypeObject, tp_bases)}, -{-1, offsetof(PyTypeObject, tp_call)}, -{-1, offsetof(PyTypeObject, tp_clear)}, -{-1, offsetof(PyTypeObject, tp_dealloc)}, -{-1, offsetof(PyTypeObject, tp_del)}, -{-1, offsetof(PyTypeObject, tp_descr_get)}, -{-1, offsetof(PyTypeObject, tp_descr_set)}, -{-1, offsetof(PyTypeObject, tp_doc)}, -{-1, offsetof(PyTypeObject, tp_getattr)}, -{-1, offsetof(PyTypeObject, tp_getattro)}, -{-1, offsetof(PyTypeObject, tp_hash)}, -{-1, offsetof(PyTypeObject, tp_init)}, -{-1, offsetof(PyTypeObject, tp_is_gc)}, -{-1, offsetof(PyTypeObject, tp_iter)}, -{-1, offsetof(PyTypeObject, tp_iternext)}, -{-1, offsetof(PyTypeObject, tp_methods)}, -{-1, offsetof(PyTypeObject, tp_new)}, -{-1, offsetof(PyTypeObject, tp_repr)}, -{-1, offsetof(PyTypeObject, tp_richcompare)}, -{-1, offsetof(PyTypeObject, tp_setattr)}, -{-1, offsetof(PyTypeObject, tp_setattro)}, -{-1, offsetof(PyTypeObject, tp_str)}, -{-1, offsetof(PyTypeObject, tp_traverse)}, -{-1, offsetof(PyTypeObject, tp_members)}, -{-1, offsetof(PyTypeObject, tp_getset)}, -{-1, offsetof(PyTypeObject, tp_free)}, -{offsetof(PyNumberMethods, nb_matrix_multiply), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_matrix_multiply), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyAsyncMethods, am_await), offsetof(PyTypeObject, tp_as_async)}, -{offsetof(PyAsyncMethods, am_aiter), offsetof(PyTypeObject, tp_as_async)}, -{offsetof(PyAsyncMethods, am_anext), offsetof(PyTypeObject, tp_as_async)}, -{-1, offsetof(PyTypeObject, tp_finalize)}, -{offsetof(PyAsyncMethods, am_send), offsetof(PyTypeObject, tp_as_async)}, -{-1, offsetof(PyTypeObject, tp_vectorcall)}, -{-1, offsetof(PyHeapTypeObject, ht_token)}, diff --git a/Objects/typeslots.py b/Objects/typeslots.py deleted file mode 100755 index c7f8a33bb1e74e..00000000000000 --- a/Objects/typeslots.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python -# Usage: typeslots.py < Include/typeslots.h typeslots.inc - -import sys, re - - -def generate_typeslots(out=sys.stdout): - out.write("/* Generated by typeslots.py */\n") - res = {} - for line in sys.stdin: - m = re.match("#define Py_([a-z_]+) ([0-9]+)", line) - if not m: - continue - - member = m.group(1) - if member == "tp_token": - # The heap type structure (ht_*) is an implementation detail; - # the public slot for it has a familiar `tp_` prefix - member = '{-1, offsetof(PyHeapTypeObject, ht_token)}' - elif member.startswith("tp_"): - member = f'{{-1, offsetof(PyTypeObject, {member})}}' - elif member.startswith("am_"): - member = (f'{{offsetof(PyAsyncMethods, {member}),'+ - ' offsetof(PyTypeObject, tp_as_async)}') - elif member.startswith("nb_"): - member = (f'{{offsetof(PyNumberMethods, {member}),'+ - ' offsetof(PyTypeObject, tp_as_number)}') - elif member.startswith("mp_"): - member = (f'{{offsetof(PyMappingMethods, {member}),'+ - ' offsetof(PyTypeObject, tp_as_mapping)}') - elif member.startswith("sq_"): - member = (f'{{offsetof(PySequenceMethods, {member}),'+ - ' offsetof(PyTypeObject, tp_as_sequence)}') - elif member.startswith("bf_"): - member = (f'{{offsetof(PyBufferProcs, {member}),'+ - ' offsetof(PyTypeObject, tp_as_buffer)}') - res[int(m.group(2))] = member - - M = max(res.keys())+1 - for i in range(1,M): - if i in res: - out.write("%s,\n" % res[i]) - else: - out.write("{0, 0},\n") - - -def main(): - if len(sys.argv) == 2: - with open(sys.argv[1], "w") as f: - generate_typeslots(f) - else: - generate_typeslots() - -if __name__ == "__main__": - main() From 5f13d2e3d12e2f32227160d814a317f6d8908b6e Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 16 Jan 2026 17:58:50 +0100 Subject: [PATCH 07/36] Port module creation --- Include/internal/pycore_slots.h | 2 +- Include/moduleobject.h | 4 - Include/slots_generated.h | 2 +- Lib/test/test_capi/test_module.py | 4 +- Modules/_testcapi/module.c | 2 +- Modules/_testmultiphase.c | 2 +- Objects/moduleobject.c | 157 ++++++++------------- Python/slots.c | 12 +- Python/slots.csv | 2 +- Python/slots_generated.c | 223 +++++++++++++++--------------- Tools/build/generate_slots.py | 4 +- 11 files changed, 184 insertions(+), 230 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index d5cfd9f85f188d..4b55c1f348059e 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -141,7 +141,7 @@ PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, void *slots); PyAPI_FUNC(bool) _PySlotIterator_Next(_PySlotIterator *it); /* Return 1 if given slot was "seen" by an earlier _PySlotIterator_Next call. - * (This state is not reset by rewiding.) + * (This state is not reset by rewinding.) */ PyAPI_FUNC(bool) _PySlotIterator_SawSlot(_PySlotIterator *, int); diff --git a/Include/moduleobject.h b/Include/moduleobject.h index ea11863a0d0273..6b3cb959c35cf3 100644 --- a/Include/moduleobject.h +++ b/Include/moduleobject.h @@ -73,10 +73,6 @@ struct PyModuleDef_Slot { void *value; }; -#ifndef Py_LIMITED_API -#define _Py_mod_LAST_SLOT 13 -#endif - #endif /* New in 3.5 */ /* for Py_mod_multiple_interpreters: */ diff --git a/Include/slots_generated.h b/Include/slots_generated.h index 6ef5b252158b95..9a6024f057710c 100644 --- a/Include/slots_generated.h +++ b/Include/slots_generated.h @@ -4,7 +4,7 @@ #define _PY_HAVE_SLOTS_GENERATED_H #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15) -#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD +#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) NEW #else #define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD #endif diff --git a/Lib/test/test_capi/test_module.py b/Lib/test/test_capi/test_module.py index c32ca1098edc56..93efb43857c4df 100644 --- a/Lib/test/test_capi/test_module.py +++ b/Lib/test/test_capi/test_module.py @@ -150,7 +150,9 @@ def test_repeated_def_slot(self): with self.assertRaises(SystemError) as cm: _testcapi.module_from_slots_repeat_slot(spec) self.assertIn(name, str(cm.exception)) - self.assertIn("more than one", str(cm.exception)) + self.assertRegex( + str(cm.exception), + rf"^module( [_\w]+)? has multiple {name}( \(\d+\))? slots$") def test_null_def_slot(self): """Slots that replace PyModuleDef fields can't be NULL""" diff --git a/Modules/_testcapi/module.c b/Modules/_testcapi/module.c index 29eda7e008c6cb..c7589b26d35bd6 100644 --- a/Modules/_testcapi/module.c +++ b/Modules/_testcapi/module.c @@ -244,7 +244,7 @@ module_from_slots_repeat_slot(PyObject *self, PyObject *spec) PyModuleDef_Slot slots[] = { {Py_mod_abi, &abi_info}, {slot_id, "anything"}, - {slot_id, "anything else"}, + {slot_id, "anything_else"}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0}, diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index b0668e32eb57f4..a87ba6e90236b5 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -603,7 +603,7 @@ PyInit__testmultiphase_null_slots(void) /**** Problematic modules ****/ static PyModuleDef_Slot slots_bad_large[] = { - {_Py_mod_LAST_SLOT + 1, NULL}, + {Py_slot_invalid, NULL}, {0, NULL}, }; diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 19e5134d5cf26b..ba7fa3e25b4e45 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -14,6 +14,7 @@ #include "pycore_object.h" // _PyType_AllocNoTrack #include "pycore_pyerrors.h" // _PyErr_FormatFromCause() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_slots.h" // _PySlotIterator_Init #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() @@ -396,6 +397,8 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version) return (PyObject*)m; } +typedef PyObject *(*createfunc_t)(PyObject *, PyModuleDef*); + static PyObject * module_from_def_and_spec( PyModuleDef* def_like, /* not necessarily a valid Python object */ @@ -403,15 +406,11 @@ module_from_def_and_spec( int module_api_version, PyModuleDef* original_def /* NULL if not defined by a def */) { - PyModuleDef_Slot* cur_slot; - PyObject *(*create)(PyObject *, PyModuleDef*) = NULL; + createfunc_t create = NULL; PyObject *nameobj; PyObject *m = NULL; - int has_multiple_interpreters_slot = 0; - void *multiple_interpreters = (void *)0; - int has_gil_slot = 0; + uint64_t multiple_interpreters = (uint64_t)Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED; bool requires_gil = true; - int has_execution_slots = 0; const char *name; int ret; void *token = NULL; @@ -439,134 +438,97 @@ module_from_def_and_spec( goto error; } - bool seen_m_name_slot = false; - bool seen_m_doc_slot = false; - bool seen_m_size_slot = false; - bool seen_m_methods_slot = false; - bool seen_m_traverse_slot = false; - bool seen_m_clear_slot = false; - bool seen_m_free_slot = false; - bool seen_m_abi_slot = false; - for (cur_slot = def_like->m_slots; cur_slot && cur_slot->slot; cur_slot++) { - // Macro to copy a non-NULL, non-repeatable slot. -#define COPY_NONNULL_SLOT(SLOTNAME, TYPE, DEST) \ +#define COPY_NONNULL_SLOT(TYPE, SL_MEMBER, DEST) \ do { \ - if (!(TYPE)(cur_slot->value)) { \ + if (!(TYPE)(it.current.SL_MEMBER)) { \ PyErr_Format( \ PyExc_SystemError, \ "module %s: %s must not be NULL", \ - name, SLOTNAME); \ + name, it.info->name); \ goto error; \ } \ - DEST = (TYPE)(cur_slot->value); \ + DEST = (TYPE)(it.current.SL_MEMBER); \ } while (0); \ ///////////////////////////////////////////////////////////////// // Macro to copy a non-NULL, non-repeatable slot to def_like. -#define COPY_DEF_SLOT(SLOTNAME, TYPE, MEMBER) \ +#define COPY_DEF_SLOT(TYPE, SL_MEMBER, MEMBER) \ do { \ - if (seen_ ## MEMBER ## _slot) { \ - PyErr_Format( \ - PyExc_SystemError, \ - "module %s has more than one %s slot", \ - name, SLOTNAME); \ - goto error; \ - } \ - seen_ ## MEMBER ## _slot = true; \ if (original_def) { \ TYPE orig_value = (TYPE)original_def->MEMBER; \ - TYPE new_value = (TYPE)cur_slot->value; \ + TYPE new_value = (TYPE)it.current.SL_MEMBER; \ if (orig_value != new_value) { \ PyErr_Format( \ PyExc_SystemError, \ "module %s: %s conflicts with " \ "PyModuleDef." #MEMBER, \ - name, SLOTNAME); \ + name, it.info->name); \ goto error; \ } \ } \ - COPY_NONNULL_SLOT(SLOTNAME, TYPE, (def_like->MEMBER)) \ + COPY_NONNULL_SLOT(TYPE, SL_MEMBER, (def_like->MEMBER)) \ } while (0); \ ///////////////////////////////////////////////////////////////// // Macro to copy a non-NULL, non-repeatable slot without a // corresponding PyModuleDef member. // DEST must be initially NULL (so we don't need a seen_* flag). -#define COPY_NONDEF_SLOT(SLOTNAME, TYPE, DEST) \ +#define COPY_NONDEF_SLOT(TYPE, SL_MEMBER, DEST) \ do { \ if (DEST) { \ PyErr_Format( \ PyExc_SystemError, \ - "module %s has more than one %s slot", \ - name, SLOTNAME); \ + "module %s has multiple %s slots", \ + name, it.info->name); \ goto error; \ } \ - COPY_NONNULL_SLOT(SLOTNAME, TYPE, DEST) \ + COPY_NONNULL_SLOT(TYPE, SL_MEMBER, DEST) \ } while (0); \ ///////////////////////////////////////////////////////////////// // Define the whole common case -#define DEF_SLOT_CASE(SLOT, TYPE, MEMBER) \ +#define DEF_SLOT_CASE(SLOT, TYPE, SL_MEMBER, MEMBER) \ case SLOT: \ - COPY_DEF_SLOT(#SLOT, TYPE, MEMBER); \ + COPY_DEF_SLOT(TYPE, SL_MEMBER, MEMBER); \ break; \ ///////////////////////////////////////////////////////////////// - switch (cur_slot->slot) { + + _PySlotIterator it; + _PySlotIterator_InitWithKind(&it, def_like->m_slots, + _PySlot_KIND_MOD, _PySlot_KIND_MOD); + while (_PySlotIterator_Next(&it)) { + switch (it.current.sl_id) { + case Py_slot_invalid: + goto error; case Py_mod_create: - if (create) { - PyErr_Format( - PyExc_SystemError, - "module %s has multiple create slots", - name); - goto error; - } - create = cur_slot->value; + create = (createfunc_t)it.current.sl_func; break; case Py_mod_exec: - has_execution_slots = 1; if (!original_def) { - COPY_NONDEF_SLOT("Py_mod_exec", _Py_modexecfunc, m_exec); + COPY_NONDEF_SLOT(_Py_modexecfunc, sl_func, m_exec); } break; case Py_mod_multiple_interpreters: - if (has_multiple_interpreters_slot) { - PyErr_Format( - PyExc_SystemError, - "module %s has more than one 'multiple interpreters' " - "slots", - name); - goto error; - } - multiple_interpreters = cur_slot->value; - has_multiple_interpreters_slot = 1; + multiple_interpreters = it.current.sl_uint64; break; case Py_mod_gil: - if (has_gil_slot) { - PyErr_Format( - PyExc_SystemError, - "module %s has more than one 'gil' slot", - name); - goto error; - } - requires_gil = (cur_slot->value != Py_MOD_GIL_NOT_USED); - has_gil_slot = 1; + requires_gil = (it.current.sl_uint64 != (uint64_t)Py_MOD_GIL_NOT_USED); break; case Py_mod_abi: - if (PyABIInfo_Check((PyABIInfo *)cur_slot->value, name) < 0) { + if (PyABIInfo_Check(it.current.sl_ptr, name) < 0) { goto error; } - seen_m_abi_slot = true; break; - DEF_SLOT_CASE(Py_mod_name, char*, m_name) - DEF_SLOT_CASE(Py_mod_doc, char*, m_doc) - DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, m_size) - DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, m_methods) - DEF_SLOT_CASE(Py_mod_state_traverse, traverseproc, m_traverse) - DEF_SLOT_CASE(Py_mod_state_clear, inquiry, m_clear) - DEF_SLOT_CASE(Py_mod_state_free, freefunc, m_free) + DEF_SLOT_CASE(Py_mod_name, char*, sl_ptr, m_name) + DEF_SLOT_CASE(Py_mod_doc, char*, sl_ptr, m_doc) + DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, sl_size, m_size) + DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, sl_ptr, m_methods) + DEF_SLOT_CASE(Py_mod_state_traverse, traverseproc, sl_func, m_traverse) + DEF_SLOT_CASE(Py_mod_state_clear, inquiry, sl_func, m_clear) + DEF_SLOT_CASE(Py_mod_state_free, freefunc, sl_func, m_free) case Py_mod_token: - if (original_def && original_def != cur_slot->value) { + if (original_def && original_def != it.current.sl_ptr) { PyErr_Format( PyExc_SystemError, "module %s: arbitrary Py_mod_token not " @@ -574,22 +536,15 @@ module_from_def_and_spec( name); goto error; } - COPY_NONDEF_SLOT("Py_mod_token", void*, token); + COPY_NONDEF_SLOT(void*, sl_ptr, token); break; - default: - assert(cur_slot->slot < 0 || cur_slot->slot > _Py_mod_LAST_SLOT); - PyErr_Format( - PyExc_SystemError, - "module %s uses unknown slot ID %i", - name, cur_slot->slot); - goto error; } #undef DEF_SLOT_CASE #undef COPY_DEF_SLOT #undef COPY_NONDEF_SLOT #undef COPY_NONNULL_SLOT } - if (!original_def && !seen_m_abi_slot) { + if (!original_def && !_PySlotIterator_SawSlot(&it, Py_mod_abi)) { PyErr_Format( PyExc_SystemError, "module %s does not define Py_mod_abi," @@ -616,17 +571,14 @@ module_from_def_and_spec( /* By default, multi-phase init modules are expected to work under multiple interpreters. */ - if (!has_multiple_interpreters_slot) { - multiple_interpreters = Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED; - } - if (multiple_interpreters == Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) { + if (multiple_interpreters == (int64_t)(intptr_t)Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) { if (!_Py_IsMainInterpreter(interp) && _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0) { goto error; } } - else if (multiple_interpreters != Py_MOD_PER_INTERPRETER_GIL_SUPPORTED + else if (multiple_interpreters != (int64_t)(intptr_t)Py_MOD_PER_INTERPRETER_GIL_SUPPORTED && interp->ceval.own_gil && !_Py_IsMainInterpreter(interp) && _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0) @@ -688,7 +640,7 @@ module_from_def_and_spec( name); goto error; } - if (has_execution_slots) { + if (_PySlotIterator_SawSlot(&it, Py_mod_exec)) { PyErr_Format( PyExc_SystemError, "module %s specifies execution slots, but did not create " @@ -835,8 +787,6 @@ PyModule_Exec(PyObject *module) int PyModule_ExecDef(PyObject *module, PyModuleDef *def) { - PyModuleDef_Slot *cur_slot; - if (alloc_state(module) < 0) { return -1; } @@ -847,13 +797,18 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def) return 0; } - for (cur_slot = def->m_slots; cur_slot && cur_slot->slot; cur_slot++) { - if (cur_slot->slot == Py_mod_exec) { - int (*func)(PyObject *) = cur_slot->value; - if (run_exec_func(module, func) < 0) { + _PySlotIterator it; + _PySlotIterator_InitWithKind(&it, def->m_slots, + _PySlot_KIND_MOD, _PySlot_KIND_MOD); + while (_PySlotIterator_Next(&it)) { + switch (it.current.sl_id) { + case Py_slot_invalid: return -1; - } - continue; + case Py_mod_exec: + _Py_modexecfunc func = (_Py_modexecfunc)it.current.sl_func; + if (run_exec_func(module, func) < 0) { + return -1; + } } } return 0; diff --git a/Python/slots.c b/Python/slots.c index 0b563f6c7ecd73..2ed4f234bb262c 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -106,6 +106,7 @@ _PySlotIterator_Next(_PySlotIterator *it) MSG("next"); assert(it); assert(!it->is_at_end); + assert(!PyErr_Occurred()); it->current.sl_id = -1; @@ -235,7 +236,7 @@ _PySlotIterator_Next(_PySlotIterator *it) if (it->recursion_level >= _PySlot_MAX_NESTING) { MSG("error (too much nesting)"); PyErr_Format(PyExc_SystemError, - "Py_%s (slot %d): too many levels of nested slots", + "%s (slot %d): too many levels of nested slots", orig_info->name, orig_id); goto error; } @@ -273,6 +274,7 @@ _PySlotIterator_Next(_PySlotIterator *it) advance(it); MSG("result: %d (%s)", (int)result->sl_id, it->info->name); assert (result->sl_id > 0); + assert (result->sl_id <= _Py_slot_COUNT); assert (result->sl_id <= INT_MAX); if (it->is_first_run && validate_current_slot(it) < 0) { goto error; @@ -295,7 +297,7 @@ validate_current_slot(_PySlotIterator *it) if (it->info->kind != it->kind) { MSG("error (bad slot kind)"); PyErr_Format(PyExc_SystemError, - "Py_%s (slot %d) is not compatible with %ss", + "%s (slot %d) is not compatible with %ss", info->name, id, kind_name(it->kind)); @@ -328,7 +330,7 @@ validate_current_slot(_PySlotIterator *it) if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, - "NULL value in slot Py_%s is deprecated", + "NULL value in slot %s is deprecated", it->info->name) < 0) { return -1; @@ -343,7 +345,7 @@ validate_current_slot(_PySlotIterator *it) MSG("error (duplicate rejected)"); PyErr_Format( PyExc_SystemError, - "%s%s%s has multiple Py_%s (%d) slots", + "%s%s%s has multiple %s (%d) slots", kind_name(it->kind), it->name ? " " : "", it->name ? it->name : "", @@ -355,7 +357,7 @@ validate_current_slot(_PySlotIterator *it) if (PyErr_WarnFormat( PyExc_DeprecationWarning, 0, - "%s%s%s has multiple Py_%s (%d) slots. This is deprecated.", + "%s%s%s has multiple %s (%d) slots. This is deprecated.", kind_name(it->kind), it->name ? " " : "", it->name ? it->name : "", diff --git a/Python/slots.csv b/Python/slots.csv index 1c616f54f7a60e..91cf2fb6714886 100644 --- a/Python/slots.csv +++ b/Python/slots.csv @@ -151,5 +151,5 @@ id, name, kind, dtype 106, "mod_state_free", mod, func, added=3.14, null=reject, duplicates=no 107, "tp_metaclass", type, ptr, virtual=true, PyObject=true, added=3.14, null=reject, duplicates=no 108, "tp_module", type, ptr, virtual=true, PyObject=true, added=3.14, null=reject, duplicates=no -109, "mod_abi", mod, ptr, added=3.15, null=reject, duplicates=no +109, "mod_abi", mod, ptr, added=3.15, null=reject, duplicates=yes 110, "mod_token", mod, ptr, added=3.15, null=reject, duplicates=no diff --git a/Python/slots_generated.c b/Python/slots_generated.c index 9cecd53229be8b..d4272a9e18cfbc 100644 --- a/Python/slots_generated.c +++ b/Python/slots_generated.c @@ -7,13 +7,13 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { [0] = { - .name = "slot_end", + .name = "Py_slot_end", .dtype = _PySlot_TYPE_VOID, .kind = _PySlot_KIND_SLOT, .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [1] = { - .name = "bf_getbuffer/mod_create", + .name = "Py_bf_getbuffer/mod_create", .dtype = _PySlot_TYPE_VOID, .kind = _PySlot_KIND_COMPAT, .compat_info.mod_id = 84, @@ -21,7 +21,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [2] = { - .name = "bf_releasebuffer/mod_exec", + .name = "Py_bf_releasebuffer/mod_exec", .dtype = _PySlot_TYPE_VOID, .kind = _PySlot_KIND_COMPAT, .compat_info.mod_id = 85, @@ -29,7 +29,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [3] = { - .name = "mp_ass_subscript/mod_multiple_interpreters", + .name = "Py_mp_ass_subscript/mod_multiple_interpreters", .dtype = _PySlot_TYPE_VOID, .kind = _PySlot_KIND_COMPAT, .compat_info.mod_id = 86, @@ -37,7 +37,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [4] = { - .name = "mp_length/mod_gil", + .name = "Py_mp_length/mod_gil", .dtype = _PySlot_TYPE_VOID, .kind = _PySlot_KIND_COMPAT, .compat_info.mod_id = 87, @@ -45,7 +45,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [5] = { - .name = "mp_subscript", + .name = "Py_mp_subscript", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), @@ -54,7 +54,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [6] = { - .name = "nb_absolute", + .name = "Py_nb_absolute", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -63,7 +63,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [7] = { - .name = "nb_add", + .name = "Py_nb_add", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -72,7 +72,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [8] = { - .name = "nb_and", + .name = "Py_nb_and", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -81,7 +81,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [9] = { - .name = "nb_bool", + .name = "Py_nb_bool", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -90,7 +90,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [10] = { - .name = "nb_divmod", + .name = "Py_nb_divmod", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -99,7 +99,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [11] = { - .name = "nb_float", + .name = "Py_nb_float", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -108,7 +108,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [12] = { - .name = "nb_floor_divide", + .name = "Py_nb_floor_divide", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -117,7 +117,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [13] = { - .name = "nb_index", + .name = "Py_nb_index", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -126,7 +126,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [14] = { - .name = "nb_inplace_add", + .name = "Py_nb_inplace_add", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -135,7 +135,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [15] = { - .name = "nb_inplace_and", + .name = "Py_nb_inplace_and", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -144,7 +144,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [16] = { - .name = "nb_inplace_floor_divide", + .name = "Py_nb_inplace_floor_divide", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -153,7 +153,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [17] = { - .name = "nb_inplace_lshift", + .name = "Py_nb_inplace_lshift", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -162,7 +162,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [18] = { - .name = "nb_inplace_multiply", + .name = "Py_nb_inplace_multiply", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -171,7 +171,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [19] = { - .name = "nb_inplace_or", + .name = "Py_nb_inplace_or", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -180,7 +180,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [20] = { - .name = "nb_inplace_power", + .name = "Py_nb_inplace_power", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -189,7 +189,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [21] = { - .name = "nb_inplace_remainder", + .name = "Py_nb_inplace_remainder", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -198,7 +198,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [22] = { - .name = "nb_inplace_rshift", + .name = "Py_nb_inplace_rshift", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -207,7 +207,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [23] = { - .name = "nb_inplace_subtract", + .name = "Py_nb_inplace_subtract", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -216,7 +216,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [24] = { - .name = "nb_inplace_true_divide", + .name = "Py_nb_inplace_true_divide", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -225,7 +225,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [25] = { - .name = "nb_inplace_xor", + .name = "Py_nb_inplace_xor", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -234,7 +234,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [26] = { - .name = "nb_int", + .name = "Py_nb_int", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -243,7 +243,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [27] = { - .name = "nb_invert", + .name = "Py_nb_invert", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -252,7 +252,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [28] = { - .name = "nb_lshift", + .name = "Py_nb_lshift", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -261,7 +261,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [29] = { - .name = "nb_multiply", + .name = "Py_nb_multiply", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -270,7 +270,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [30] = { - .name = "nb_negative", + .name = "Py_nb_negative", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -279,7 +279,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [31] = { - .name = "nb_or", + .name = "Py_nb_or", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -288,7 +288,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [32] = { - .name = "nb_positive", + .name = "Py_nb_positive", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -297,7 +297,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [33] = { - .name = "nb_power", + .name = "Py_nb_power", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -306,7 +306,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [34] = { - .name = "nb_remainder", + .name = "Py_nb_remainder", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -315,7 +315,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [35] = { - .name = "nb_rshift", + .name = "Py_nb_rshift", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -324,7 +324,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [36] = { - .name = "nb_subtract", + .name = "Py_nb_subtract", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -333,7 +333,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [37] = { - .name = "nb_true_divide", + .name = "Py_nb_true_divide", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -342,7 +342,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [38] = { - .name = "nb_xor", + .name = "Py_nb_xor", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -351,7 +351,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [39] = { - .name = "sq_ass_item", + .name = "Py_sq_ass_item", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), @@ -360,7 +360,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [40] = { - .name = "sq_concat", + .name = "Py_sq_concat", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), @@ -369,7 +369,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [41] = { - .name = "sq_contains", + .name = "Py_sq_contains", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), @@ -378,7 +378,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [42] = { - .name = "sq_inplace_concat", + .name = "Py_sq_inplace_concat", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), @@ -387,7 +387,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [43] = { - .name = "sq_inplace_repeat", + .name = "Py_sq_inplace_repeat", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), @@ -396,7 +396,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [44] = { - .name = "sq_item", + .name = "Py_sq_item", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), @@ -405,7 +405,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [45] = { - .name = "sq_length", + .name = "Py_sq_length", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), @@ -414,7 +414,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [46] = { - .name = "sq_repeat", + .name = "Py_sq_repeat", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), @@ -423,7 +423,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [47] = { - .name = "tp_alloc", + .name = "Py_tp_alloc", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_alloc), @@ -432,7 +432,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [48] = { - .name = "tp_base", + .name = "Py_tp_base", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_base), @@ -441,7 +441,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [49] = { - .name = "tp_bases", + .name = "Py_tp_bases", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_bases), @@ -450,7 +450,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [50] = { - .name = "tp_call", + .name = "Py_tp_call", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_call), @@ -459,7 +459,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [51] = { - .name = "tp_clear", + .name = "Py_tp_clear", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_clear), @@ -468,7 +468,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [52] = { - .name = "tp_dealloc", + .name = "Py_tp_dealloc", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_dealloc), @@ -477,7 +477,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [53] = { - .name = "tp_del", + .name = "Py_tp_del", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_del), @@ -486,7 +486,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [54] = { - .name = "tp_descr_get", + .name = "Py_tp_descr_get", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_descr_get), @@ -495,7 +495,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [55] = { - .name = "tp_descr_set", + .name = "Py_tp_descr_set", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_descr_set), @@ -504,7 +504,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [56] = { - .name = "tp_doc", + .name = "Py_tp_doc", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_doc), @@ -513,7 +513,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [57] = { - .name = "tp_getattr", + .name = "Py_tp_getattr", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_getattr), @@ -522,7 +522,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [58] = { - .name = "tp_getattro", + .name = "Py_tp_getattro", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_getattro), @@ -531,7 +531,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [59] = { - .name = "tp_hash", + .name = "Py_tp_hash", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_hash), @@ -540,7 +540,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [60] = { - .name = "tp_init", + .name = "Py_tp_init", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_init), @@ -549,7 +549,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [61] = { - .name = "tp_is_gc", + .name = "Py_tp_is_gc", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_is_gc), @@ -558,7 +558,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [62] = { - .name = "tp_iter", + .name = "Py_tp_iter", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_iter), @@ -567,7 +567,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [63] = { - .name = "tp_iternext", + .name = "Py_tp_iternext", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_iternext), @@ -576,7 +576,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [64] = { - .name = "tp_methods", + .name = "Py_tp_methods", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_methods), @@ -585,7 +585,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [65] = { - .name = "tp_new", + .name = "Py_tp_new", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_new), @@ -594,7 +594,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [66] = { - .name = "tp_repr", + .name = "Py_tp_repr", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_repr), @@ -603,7 +603,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [67] = { - .name = "tp_richcompare", + .name = "Py_tp_richcompare", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_richcompare), @@ -612,7 +612,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [68] = { - .name = "tp_setattr", + .name = "Py_tp_setattr", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_setattr), @@ -621,7 +621,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [69] = { - .name = "tp_setattro", + .name = "Py_tp_setattro", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_setattro), @@ -630,7 +630,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [70] = { - .name = "tp_str", + .name = "Py_tp_str", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_str), @@ -639,7 +639,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [71] = { - .name = "tp_traverse", + .name = "Py_tp_traverse", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_traverse), @@ -648,7 +648,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [72] = { - .name = "tp_members", + .name = "Py_tp_members", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_members), @@ -657,7 +657,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [73] = { - .name = "tp_getset", + .name = "Py_tp_getset", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_getset), @@ -666,7 +666,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [74] = { - .name = "tp_free", + .name = "Py_tp_free", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_free), @@ -675,7 +675,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [75] = { - .name = "nb_matrix_multiply", + .name = "Py_nb_matrix_multiply", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -684,7 +684,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [76] = { - .name = "nb_inplace_matrix_multiply", + .name = "Py_nb_inplace_matrix_multiply", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), @@ -693,7 +693,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [77] = { - .name = "am_await", + .name = "Py_am_await", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), @@ -702,7 +702,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [78] = { - .name = "am_aiter", + .name = "Py_am_aiter", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), @@ -711,7 +711,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [79] = { - .name = "am_anext", + .name = "Py_am_anext", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), @@ -720,7 +720,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [80] = { - .name = "tp_finalize", + .name = "Py_tp_finalize", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_finalize), @@ -729,7 +729,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [81] = { - .name = "am_send", + .name = "Py_am_send", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), @@ -738,7 +738,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [82] = { - .name = "tp_vectorcall", + .name = "Py_tp_vectorcall", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_vectorcall), @@ -747,7 +747,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [83] = { - .name = "tp_token", + .name = "Py_tp_token", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyHeapTypeObject, ht_token), @@ -756,32 +756,32 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [84] = { - .name = "mod_create", + .name = "Py_mod_create", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_DEPRECATED, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [85] = { - .name = "mod_exec", + .name = "Py_mod_exec", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_DEPRECATED, }, [86] = { - .name = "mod_multiple_interpreters", + .name = "Py_mod_multiple_interpreters", .dtype = _PySlot_TYPE_UINT64, .kind = _PySlot_KIND_MOD, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [87] = { - .name = "mod_gil", + .name = "Py_mod_gil", .dtype = _PySlot_TYPE_UINT64, .kind = _PySlot_KIND_MOD, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [88] = { - .name = "bf_getbuffer", + .name = "Py_bf_getbuffer", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_buffer), @@ -790,7 +790,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [89] = { - .name = "bf_releasebuffer", + .name = "Py_bf_releasebuffer", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_buffer), @@ -799,7 +799,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [90] = { - .name = "mp_ass_subscript", + .name = "Py_mp_ass_subscript", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), @@ -808,7 +808,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [91] = { - .name = "mp_length", + .name = "Py_mp_length", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), @@ -817,7 +817,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [92] = { - .name = "slot_subslots", + .name = "Py_slot_subslots", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_SLOT, .is_subslots = true, @@ -825,7 +825,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [93] = { - .name = "tp_slots", + .name = "Py_tp_slots", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .is_subslots = true, @@ -833,7 +833,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [94] = { - .name = "mod_slots", + .name = "Py_mod_slots", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, .is_subslots = true, @@ -841,7 +841,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, }, [95] = { - .name = "tp_name", + .name = "Py_tp_name", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .null_handling = _PySlot_PROBLEM_REJECT, @@ -849,7 +849,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .is_name = true, }, [96] = { - .name = "tp_basicsize", + .name = "Py_tp_basicsize", .dtype = _PySlot_TYPE_SIZE, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_basicsize), @@ -857,13 +857,13 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [97] = { - .name = "tp_extra_basicsize", + .name = "Py_tp_extra_basicsize", .dtype = _PySlot_TYPE_SIZE, .kind = _PySlot_KIND_TYPE, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [98] = { - .name = "tp_itemsize", + .name = "Py_tp_itemsize", .dtype = _PySlot_TYPE_SIZE, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_itemsize), @@ -871,7 +871,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [99] = { - .name = "tp_flags", + .name = "Py_tp_flags", .dtype = _PySlot_TYPE_UINT64, .kind = _PySlot_KIND_TYPE, .type_info.slot_offset = offsetof(PyTypeObject, tp_flags), @@ -879,7 +879,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [100] = { - .name = "mod_name", + .name = "Py_mod_name", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_REJECT, @@ -887,69 +887,68 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .is_name = true, }, [101] = { - .name = "mod_doc", + .name = "Py_mod_doc", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_ALLOW, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [102] = { - .name = "mod_state_size", + .name = "Py_mod_state_size", .dtype = _PySlot_TYPE_SIZE, .kind = _PySlot_KIND_MOD, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [103] = { - .name = "mod_methods", + .name = "Py_mod_methods", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_REJECT, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [104] = { - .name = "mod_state_traverse", + .name = "Py_mod_state_traverse", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_REJECT, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [105] = { - .name = "mod_state_clear", + .name = "Py_mod_state_clear", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_REJECT, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [106] = { - .name = "mod_state_free", + .name = "Py_mod_state_free", .dtype = _PySlot_TYPE_FUNC, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_REJECT, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [107] = { - .name = "tp_metaclass", + .name = "Py_tp_metaclass", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .null_handling = _PySlot_PROBLEM_REJECT, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [108] = { - .name = "tp_module", + .name = "Py_tp_module", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_TYPE, .null_handling = _PySlot_PROBLEM_REJECT, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [109] = { - .name = "mod_abi", + .name = "Py_mod_abi", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [110] = { - .name = "mod_token", + .name = "Py_mod_token", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, .null_handling = _PySlot_PROBLEM_REJECT, diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index 1c2e23048ad6c2..62cc35c840f65c 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -105,7 +105,7 @@ def write_header(f, slots): out(f'#define _PY_HAVE_SLOTS_GENERATED_H') out() out(f'#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)') - out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD') + out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) NEW') out(f'#else') out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD') out(f'#endif') @@ -138,7 +138,7 @@ def write_c(f, slots): try: out(f" [{slot.id}] = {{") initializers = { - "name": f'"{slot.name}"', + "name": f'"Py_{slot.name}"', "dtype": f'_PySlot_TYPE_{slot.dtype.upper()}', "kind": f'_PySlot_KIND_{slot.kind.upper()}', } From 4660f2f9319ed730749d7d552e51f1dd8d5871ee Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 16 Jan 2026 18:03:19 +0100 Subject: [PATCH 08/36] Simplify internal API --- Include/internal/pycore_slots.h | 19 ++++++++----------- Objects/moduleobject.c | 6 ++---- Objects/typeobject.c | 3 +-- Python/slots.c | 21 +++++++++++++++++---- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index 4b55c1f348059e..df3bfda2ef61ac 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -111,18 +111,15 @@ typedef struct { char *name; } _PySlotIterator; -/* Initialize a iterator. Currently cannot fail. */ -PyAPI_FUNC(void) _PySlotIterator_InitWithKind( - _PySlotIterator *, void*, - _PySlot_KIND result_kind, _PySlot_KIND slot_struct_kind); - -static inline void +/* Initialize an iterator using a Py_Slot array */ +PyAPI_FUNC(void) _PySlotIterator_Init(_PySlotIterator *it, PySlot *slots, - _PySlot_KIND result_kind) -{ - return _PySlotIterator_InitWithKind(it, slots, result_kind, - _PySlot_KIND_SLOT); -} + _PySlot_KIND result_kind); + +/* Initialize an iterator using a legacy slot array */ +PyAPI_FUNC(void) +_PySlotIterator_InitLegacy(_PySlotIterator *it, void *slots, + _PySlot_KIND kind); /* Reset a *successfully exhausted* iterator to the beginning. * The *slots* must be the same as for the previous diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index ba7fa3e25b4e45..b1d548eace32a7 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -495,8 +495,7 @@ module_from_def_and_spec( ///////////////////////////////////////////////////////////////// _PySlotIterator it; - _PySlotIterator_InitWithKind(&it, def_like->m_slots, - _PySlot_KIND_MOD, _PySlot_KIND_MOD); + _PySlotIterator_InitLegacy(&it, def_like->m_slots, _PySlot_KIND_MOD); while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: @@ -798,8 +797,7 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def) } _PySlotIterator it; - _PySlotIterator_InitWithKind(&it, def->m_slots, - _PySlot_KIND_MOD, _PySlot_KIND_MOD); + _PySlotIterator_InitLegacy(&it, def->m_slots, _PySlot_KIND_MOD); while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: diff --git a/Objects/typeobject.c b/Objects/typeobject.c index b790095be28aca..9691c8968d6aab 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5285,8 +5285,7 @@ PyType_FromMetaclass( char *res_start; _PySlotIterator it; - _PySlotIterator_InitWithKind(&it, spec->slots, - _PySlot_KIND_TYPE, _PySlot_KIND_TYPE); + _PySlotIterator_InitLegacy(&it, spec->slots, _PySlot_KIND_TYPE); while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: diff --git a/Python/slots.c b/Python/slots.c index 2ed4f234bb262c..4edd4ee0edba9d 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -32,10 +32,10 @@ kind_name(_PySlot_KIND kind) return ""; } -void -_PySlotIterator_InitWithKind(_PySlotIterator *it, void *slots, - _PySlot_KIND result_kind, - _PySlot_KIND slot_struct_kind) +static void +init_with_kind(_PySlotIterator *it, void *slots, + _PySlot_KIND result_kind, + _PySlot_KIND slot_struct_kind) { MSG(""); MSG("init (%s slot iterator)", kind_name(result_kind)); @@ -52,6 +52,19 @@ _PySlotIterator_InitWithKind(_PySlotIterator *it, void *slots, memset(it->seen, 0, sizeof(it->seen)); } +void +_PySlotIterator_Init(_PySlotIterator *it, PySlot *slots, + _PySlot_KIND result_kind) +{ + init_with_kind(it, slots, result_kind, _PySlot_KIND_SLOT); +} + +void +_PySlotIterator_InitLegacy(_PySlotIterator *it, void *slots, _PySlot_KIND kind) +{ + init_with_kind(it, slots, kind, kind); +} + void _PySlotIterator_Rewind(_PySlotIterator *it, void *slots) { From 6c29000e040294d8a81cf9cb75f35111aaf94205 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Jan 2026 13:35:22 +0100 Subject: [PATCH 09/36] Switch modexport over to new slots --- Include/exports.h | 2 +- Include/internal/pycore_importdl.h | 2 +- Include/internal/pycore_slots.h | 14 +-- Include/moduleobject.h | 2 +- Include/slots.h | 22 ++-- Lib/test/test_cext/extension.c | 14 +-- Modules/_testcapi/module.c | 164 ++++++++++++++--------------- Modules/_testmultiphase.c | 86 +++++++-------- Objects/moduleobject.c | 43 +++++--- Python/import.c | 2 +- Python/slots.c | 11 +- 11 files changed, 188 insertions(+), 174 deletions(-) diff --git a/Include/exports.h b/Include/exports.h index a863ecb33078ab..18692283005e59 100644 --- a/Include/exports.h +++ b/Include/exports.h @@ -103,7 +103,7 @@ #define PyMODINIT_FUNC _PyINIT_FUNC_DECLSPEC PyObject* #endif #ifndef PyMODEXPORT_FUNC - #define PyMODEXPORT_FUNC _PyINIT_FUNC_DECLSPEC PyModuleDef_Slot* + #define PyMODEXPORT_FUNC _PyINIT_FUNC_DECLSPEC PySlot* #endif #endif /* Py_EXPORTS_H */ diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index f60c5510d20075..9ed87a544234c5 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -124,7 +124,7 @@ extern void _Py_ext_module_loader_result_apply_error( /* The module init function. */ typedef PyObject *(*PyModInitFunction)(void); -typedef PyModuleDef_Slot *(*PyModExportFunction)(void); +typedef PySlot *(*PyModExportFunction)(void); #ifdef HAVE_DYNAMIC_LOADING extern int _PyImport_GetModuleExportHooks( struct _Py_ext_module_loader_info *info, diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index df3bfda2ef61ac..ef056961733d1d 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -76,10 +76,10 @@ extern _PySlot_Info _PySlot_InfoTable[]; typedef struct _PySlotIterator_state { union { // tagged by slot_struct_kind: - PySlot *slot; // with _PySlot_KIND_SLOT - PyType_Slot *tp_slot; // with _PySlot_KIND_TYPE - PyModuleDef_Slot *mod_slot; // with _PySlot_KIND_MOD - void *any_slot; + const PySlot *slot; // with _PySlot_KIND_SLOT + const PyType_Slot *tp_slot; // with _PySlot_KIND_TYPE + const PyModuleDef_Slot *mod_slot; // with _PySlot_KIND_MOD + const void *any_slot; }; _PySlot_KIND slot_struct_kind; bool ignoring_fallbacks :1; @@ -113,12 +113,12 @@ typedef struct { /* Initialize an iterator using a Py_Slot array */ PyAPI_FUNC(void) -_PySlotIterator_Init(_PySlotIterator *it, PySlot *slots, +_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots, _PySlot_KIND result_kind); /* Initialize an iterator using a legacy slot array */ PyAPI_FUNC(void) -_PySlotIterator_InitLegacy(_PySlotIterator *it, void *slots, +_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots, _PySlot_KIND kind); /* Reset a *successfully exhausted* iterator to the beginning. @@ -126,7 +126,7 @@ _PySlotIterator_InitLegacy(_PySlotIterator *it, void *slots, * `_PySlotIterator_InitWithKind` call. * (Subsequent iterations skip some validation.) */ -PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, void *slots); +PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, const void *slots); /* Iteration function. * diff --git a/Include/moduleobject.h b/Include/moduleobject.h index 6b3cb959c35cf3..88c66672ff164a 100644 --- a/Include/moduleobject.h +++ b/Include/moduleobject.h @@ -95,7 +95,7 @@ PyAPI_FUNC(int) PyUnstable_Module_SetGIL(PyObject *module, void *gil); #endif #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15) -PyAPI_FUNC(PyObject *) PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, +PyAPI_FUNC(PyObject *) PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec); PyAPI_FUNC(int) PyModule_Exec(PyObject *module); PyAPI_FUNC(int) PyModule_GetStateSize(PyObject *module, Py_ssize_t *result); diff --git a/Include/slots.h b/Include/slots.h index 07633c112a8b95..8ff928f9bd4c03 100644 --- a/Include/slots.h +++ b/Include/slots.h @@ -1,6 +1,8 @@ #ifndef _Py_HAVE_SLOTS_H #define _Py_HAVE_SLOTS_H +typedef void (*_Py_funcptr_t)(void); + struct PySlot { uint16_t sl_id; uint16_t sl_flags; @@ -9,7 +11,7 @@ struct PySlot { }; _Py_ANONYMOUS union { void *sl_ptr; - void (*sl_func)(void); + _Py_funcptr_t sl_func; Py_ssize_t sl_size; int64_t sl_int64; uint64_t sl_uint64; @@ -24,30 +26,30 @@ struct PySlot { #define Py_slot_invalid 0xffff #define PySlot_DATA(NAME, VALUE) \ - {.sl_id=NAME, .sl_flags=PySlot_INTPTR, .sl_ptr=(void*)(VALUE)} + {.sl_id=(NAME), .sl_flags=PySlot_INTPTR, .sl_ptr=(void*)(VALUE)} #define PySlot_FUNC(NAME, VALUE) \ - {.sl_id=NAME, .sl_func=(VALUE)} + {.sl_id=(NAME), .sl_func=(_Py_funcptr_t)(VALUE)} #define PySlot_SIZE(NAME, VALUE) \ - {.sl_id=NAME, .sl_size=(Py_ssize_t)(VALUE)} + {.sl_id=(NAME), .sl_size=(Py_ssize_t)(VALUE)} #define PySlot_INT64(NAME, VALUE) \ - {.sl_id=NAME, .sl_int64=(int64_t)(VALUE)} + {.sl_id=(NAME), .sl_int64=(int64_t)(VALUE)} #define PySlot_UINT64(NAME, VALUE) \ - {.sl_id=NAME, .sl_uint64=(uint64_t)(VALUE)} + {.sl_id=(NAME), .sl_uint64=(uint64_t)(VALUE)} #define PySlot_STATIC_DATA(NAME, VALUE) \ - {.sl_id=NAME, .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)} + {.sl_id=(NAME), .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)} -#define PySlot_END {.sl_id=0} +#define PySlot_END {0} #define PySlot_PTR(NAME, VALUE) \ - {NAME, PySlot_INTPTR, {0}, {(void*)VALUE}} + {(NAME), PySlot_INTPTR, {0}, {(void*)(VALUE)}} #define PySlot_PTR_STATIC(NAME, VALUE) \ - {NAME, PySlot_INTPTR | PySlot_STATIC, {0}, {(void*)VALUE}} + {(NAME), PySlot_INTPTR | PySlot_STATIC, {0}, {(void*)(VALUE)}} #endif // _Py_HAVE_SLOTS_H diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index a880cb82811f78..53911e4d0f3eda 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -121,13 +121,13 @@ _Py_COMP_DIAG_PUSH PyDoc_STRVAR(_testcext_doc, "C test extension."); PyABIInfo_VAR(abi_info); -static PyModuleDef_Slot _testcext_slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, STR(MODULE_NAME)}, - {Py_mod_doc, (void*)(char*)_testcext_doc}, - {Py_mod_exec, (void*)_testcext_exec}, - {Py_mod_methods, _testcext_methods}, - {0, NULL} +static PySlot _testcext_slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, STR(MODULE_NAME)), + PySlot_DATA(Py_mod_doc, (void*)(char*)_testcext_doc), + PySlot_FUNC(Py_mod_exec, (void*)_testcext_exec), + PySlot_FUNC(Py_mod_methods, _testcext_methods), + PySlot_END, }; _Py_COMP_DIAG_POP diff --git a/Modules/_testcapi/module.c b/Modules/_testcapi/module.c index c7589b26d35bd6..3d43ea96fbc6d4 100644 --- a/Modules/_testcapi/module.c +++ b/Modules/_testcapi/module.c @@ -13,8 +13,8 @@ PyABIInfo_VAR(abi_info); static PyObject * module_from_slots_empty(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {0}, + PySlot slots[] = { + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -22,9 +22,9 @@ module_from_slots_empty(PyObject *self, PyObject *spec) static PyObject * module_from_slots_minimal(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -38,12 +38,12 @@ module_from_slots_null(PyObject *self, PyObject *spec) static PyObject * module_from_slots_name(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "currently ignored..."}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "currently ignored..."), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -51,12 +51,12 @@ module_from_slots_name(PyObject *self, PyObject *spec) static PyObject * module_from_slots_doc(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_doc, "the docstring"}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_doc, "the docstring"), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -64,12 +64,12 @@ module_from_slots_doc(PyObject *self, PyObject *spec) static PyObject * module_from_slots_size(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_state_size, (void*)123}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_SIZE(Py_mod_state_size, (void*)123), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec); if (!mod) { @@ -92,12 +92,12 @@ static PyMethodDef a_methoddef_array[] = { static PyObject * module_from_slots_methods(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_methods, a_methoddef_array}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_methods, a_methoddef_array), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -111,14 +111,14 @@ static void noop_free(void *self) { } static PyObject * module_from_slots_gc(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_state_traverse, noop_traverse}, - {Py_mod_state_clear, noop_clear}, - {Py_mod_state_free, noop_free}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_FUNC(Py_mod_state_traverse, noop_traverse), + PySlot_FUNC(Py_mod_state_clear, noop_clear), + PySlot_FUNC(Py_mod_state_free, noop_free), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec); if (!mod) { @@ -144,12 +144,12 @@ static const char test_token; static PyObject * module_from_slots_token(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_token, (void*)&test_token}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_token, (void*)&test_token), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec); if (!mod) { @@ -175,12 +175,12 @@ simple_exec(PyObject *module) static PyObject * module_from_slots_exec(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_exec, simple_exec}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_FUNC(Py_mod_exec, simple_exec), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec); if (!mod) { @@ -209,12 +209,12 @@ create_attr_from_spec(PyObject *spec, PyModuleDef *def) static PyObject * module_from_slots_create(PyObject *self, PyObject *spec) { - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_create, create_attr_from_spec}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + const PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_FUNC(Py_mod_create, create_attr_from_spec), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -241,13 +241,13 @@ module_from_slots_repeat_slot(PyObject *self, PyObject *spec) if (slot_id < 0) { return NULL; } - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {slot_id, "anything"}, - {slot_id, "anything_else"}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + const PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_PTR(slot_id, "anything"), + PySlot_PTR(slot_id, "anything_else"), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -259,12 +259,12 @@ module_from_slots_null_slot(PyObject *self, PyObject *spec) if (slot_id < 0) { return NULL; } - PyModuleDef_Slot slots[] = { - {slot_id, NULL}, - {Py_mod_abi, &abi_info}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + const PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_PTR(slot_id, NULL), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -347,12 +347,12 @@ module_from_bad_abiinfo(PyObject *self, PyObject *spec) 1, 0, .abi_version=0x02080000, }; - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_abi, &bad_abi_info}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_abi, &bad_abi_info), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } @@ -365,14 +365,14 @@ module_from_multiple_abiinfo(PyObject *self, PyObject *spec) .flags=PyABIInfo_STABLE | PyABIInfo_FREETHREADING_AGNOSTIC, .abi_version=0x03040000, }; - PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_abi, &abi_info}, - {Py_mod_abi, &extra_abi_info}, - {Py_mod_abi, &extra_abi_info}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_abi, &extra_abi_info), + PySlot_DATA(Py_mod_abi, &extra_abi_info), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return PyModule_FromSlotsAndSpec(slots, spec); } diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index a87ba6e90236b5..fc00dddb34f65e 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -1051,12 +1051,12 @@ PyABIInfo_VAR(abi_info); PyMODEXPORT_FUNC PyModExport__test_from_modexport(void) { - static PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "_test_from_modexport"}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport"), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED), + PySlot_END, }; return slots; } @@ -1064,12 +1064,12 @@ PyModExport__test_from_modexport(void) PyMODEXPORT_FUNC PyModExport__test_from_modexport_gil_used(void) { - static PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "_test_from_modexport_gil_used"}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_USED}, - {0}, + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport_gil_used"), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED), + PySlot_END, }; return slots; } @@ -1115,13 +1115,13 @@ modexport_create_string(PyObject *spec, PyModuleDef *def) PyMODEXPORT_FUNC PyModExport__test_from_modexport_create_nonmodule(void) { - static PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "_test_from_modexport_create_nonmodule"}, - {Py_mod_create, modexport_create_string}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport_create_nonmodule"), + PySlot_FUNC(Py_mod_create, modexport_create_string), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return slots; } @@ -1129,19 +1129,19 @@ PyModExport__test_from_modexport_create_nonmodule(void) PyMODEXPORT_FUNC PyModExport__test_from_modexport_create_nonmodule_gil_used(void) { - static PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "_test_from_modexport_create_nonmodule"}, - {Py_mod_create, modexport_create_string}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_USED}, - {0}, + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport_create_nonmodule"), + PySlot_FUNC(Py_mod_create, modexport_create_string), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED), + PySlot_END, }; return slots; } -static PyModuleDef_Slot modexport_empty_slots[] = { - {0}, +static PySlot modexport_empty_slots[] = { + PySlot_END, }; PyMODEXPORT_FUNC @@ -1151,9 +1151,9 @@ PyModExport__test_from_modexport_empty_slots(void) } -static PyModuleDef_Slot modexport_minimal_slots[] = { - {Py_mod_abi, &abi_info}, - {0}, +static PySlot modexport_minimal_slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_END, }; PyMODEXPORT_FUNC @@ -1234,18 +1234,18 @@ PyModExport__test_from_modexport_smoke(void) {"get_modexport_minimal_slots", modexport_get_minimal_slots, METH_NOARGS}, {0}, }; - static PyModuleDef_Slot slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "_test_from_modexport_smoke"}, - {Py_mod_doc, "the expected docstring"}, - {Py_mod_exec, modexport_smoke_exec}, - {Py_mod_state_size, (void*)sizeof(int)}, - {Py_mod_methods, methods}, - {Py_mod_state_free, modexport_smoke_free}, - {Py_mod_token, (void*)&modexport_smoke_test_token}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0}, + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport_smoke"), + PySlot_DATA(Py_mod_doc, "the expected docstring"), + PySlot_FUNC(Py_mod_exec, modexport_smoke_exec), + PySlot_SIZE(Py_mod_state_size, (void*)sizeof(int)), + PySlot_FUNC(Py_mod_methods, methods), + PySlot_FUNC(Py_mod_state_free, modexport_smoke_free), + PySlot_DATA(Py_mod_token, (void*)&modexport_smoke_test_token), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; return slots; } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index b1d548eace32a7..39c71f54880cff 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -400,8 +400,8 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version) typedef PyObject *(*createfunc_t)(PyObject *, PyModuleDef*); static PyObject * -module_from_def_and_spec( - PyModuleDef* def_like, /* not necessarily a valid Python object */ +module_from_slots_and_spec( + const PySlot *slots, PyObject *spec, int module_api_version, PyModuleDef* original_def /* NULL if not defined by a def */) @@ -430,12 +430,19 @@ module_from_def_and_spec( goto error; } - if (def_like->m_size < 0) { - PyErr_Format( - PyExc_SystemError, - "module %s: m_size may not be negative for multi-phase initialization", - name); - goto error; + _PySlotIterator it; + + PyModuleDef _dummy_def = {0}; + PyModuleDef* def_like; + if (slots) { + assert(!original_def); + def_like = &_dummy_def; + _PySlotIterator_Init(&it, slots, _PySlot_KIND_MOD); + } + else { + assert(original_def); + def_like = original_def; + _PySlotIterator_InitLegacy(&it, def_like->m_slots, _PySlot_KIND_MOD); } // Macro to copy a non-NULL, non-repeatable slot. @@ -494,8 +501,6 @@ module_from_def_and_spec( break; \ ///////////////////////////////////////////////////////////////// - _PySlotIterator it; - _PySlotIterator_InitLegacy(&it, def_like->m_slots, _PySlot_KIND_MOD); while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: @@ -568,6 +573,14 @@ module_from_def_and_spec( } #endif + if (def_like->m_size < 0) { + PyErr_Format( + PyExc_SystemError, + "module %s: m_size may not be negative for multi-phase initialization", + name); + goto error; + } + /* By default, multi-phase init modules are expected to work under multiple interpreters. */ if (multiple_interpreters == (int64_t)(intptr_t)Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) { @@ -684,11 +697,11 @@ PyObject * PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_version) { PyModuleDef_Init(def); - return module_from_def_and_spec(def, spec, module_api_version, def); + return module_from_slots_and_spec(NULL, spec, module_api_version, def); } PyObject * -PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec) +PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec) { if (!slots) { PyErr_SetString( @@ -696,11 +709,9 @@ PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec) "PyModule_FromSlotsAndSpec called with NULL slots"); return NULL; } - // Fill in enough of a PyModuleDef to pass to common machinery - PyModuleDef def_like = {.m_slots = (PyModuleDef_Slot *)slots}; - return module_from_def_and_spec(&def_like, spec, PYTHON_API_VERSION, - NULL); + return module_from_slots_and_spec(slots, spec, PYTHON_API_VERSION, + NULL); } #ifdef Py_GIL_DISABLED diff --git a/Python/import.c b/Python/import.c index 7aa96196ec1e10..60a5ee6e770f59 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2059,7 +2059,7 @@ import_run_modexport(PyThreadState *tstate, PyModExportFunction ex0, /* This is like import_run_extension, but avoids interpreter switching * and code for for single-phase modules. */ - PyModuleDef_Slot *slots = ex0(); + PySlot *slots = ex0(); if (!slots) { if (!PyErr_Occurred()) { PyErr_Format( diff --git a/Python/slots.c b/Python/slots.c index 4edd4ee0edba9d..7c5bb2a5dcd7a4 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -33,7 +33,7 @@ kind_name(_PySlot_KIND kind) } static void -init_with_kind(_PySlotIterator *it, void *slots, +init_with_kind(_PySlotIterator *it, const void *slots, _PySlot_KIND result_kind, _PySlot_KIND slot_struct_kind) { @@ -53,20 +53,21 @@ init_with_kind(_PySlotIterator *it, void *slots, } void -_PySlotIterator_Init(_PySlotIterator *it, PySlot *slots, +_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots, _PySlot_KIND result_kind) { init_with_kind(it, slots, result_kind, _PySlot_KIND_SLOT); } void -_PySlotIterator_InitLegacy(_PySlotIterator *it, void *slots, _PySlot_KIND kind) +_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots, + _PySlot_KIND kind) { init_with_kind(it, slots, kind, kind); } void -_PySlotIterator_Rewind(_PySlotIterator *it, void *slots) +_PySlotIterator_Rewind(_PySlotIterator *it, const void *slots) { MSG(""); MSG("rewind (%s slot iterator)", kind_name(it->kind)); @@ -335,7 +336,7 @@ validate_current_slot(_PySlotIterator *it) if (it->info->null_handling == _PySlot_PROBLEM_REJECT) { MSG("error (NULL rejected)"); PyErr_Format(PyExc_SystemError, - "NULL not allowed for slot %s", + "NULL not allowed for slot Py_%s", it->info->name); return -1; } From dfb342c5c8bc960d316a972bca42fbc1897e74eb Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Jan 2026 14:24:53 +0100 Subject: [PATCH 10/36] Fix up NULL handling --- Lib/test/test_capi/test_module.py | 2 +- Objects/moduleobject.c | 14 ++++++++++++++ Python/slots.csv | 2 +- Python/slots_generated.c | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_module.py b/Lib/test/test_capi/test_module.py index 93efb43857c4df..29e1ce5b9af87f 100644 --- a/Lib/test/test_capi/test_module.py +++ b/Lib/test/test_capi/test_module.py @@ -156,7 +156,7 @@ def test_repeated_def_slot(self): def test_null_def_slot(self): """Slots that replace PyModuleDef fields can't be NULL""" - for name in (*DEF_SLOTS, 'Py_mod_exec'): + for name in {*DEF_SLOTS, 'Py_mod_exec'} - {'Py_mod_state_size'}: with self.subTest(name): spec = FakeSpec() spec._test_slot_id = getattr(_testcapi, name) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 39c71f54880cff..9eb02673363838 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -510,6 +510,20 @@ module_from_slots_and_spec( break; case Py_mod_exec: if (!original_def) { + if (m_exec) { + PyErr_Format( + PyExc_SystemError, + "module %s has multiple Py_mod_exec slots", + name); + goto error; + } + if (!it.current.sl_func) { + PyErr_Format( + PyExc_SystemError, + "module %s: Py_mod_exec slot must not be NULL", + name); + goto error; + } COPY_NONDEF_SLOT(_Py_modexecfunc, sl_func, m_exec); } break; diff --git a/Python/slots.csv b/Python/slots.csv index 91cf2fb6714886..cc5c380d3b0c86 100644 --- a/Python/slots.csv +++ b/Python/slots.csv @@ -143,7 +143,7 @@ id, name, kind, dtype 98, "tp_itemsize", type, size, added=3.14, duplicates=no 99, "tp_flags", type, uint64, added=3.14, duplicates=no 100, "mod_name", mod, ptr, added=3.14, null=reject, duplicates=no, is_name=true -101, "mod_doc", mod, ptr, added=3.14, null=allow, duplicates=no +101, "mod_doc", mod, ptr, added=3.14, null=reject, duplicates=no 102, "mod_state_size", mod, size, added=3.14, duplicates=no 103, "mod_methods", mod, ptr, added=3.14, null=reject, duplicates=no 104, "mod_state_traverse", mod, func, added=3.14, null=reject, duplicates=no diff --git a/Python/slots_generated.c b/Python/slots_generated.c index d4272a9e18cfbc..8c2d61dbd96a14 100644 --- a/Python/slots_generated.c +++ b/Python/slots_generated.c @@ -890,7 +890,7 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .name = "Py_mod_doc", .dtype = _PySlot_TYPE_PTR, .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_ALLOW, + .null_handling = _PySlot_PROBLEM_REJECT, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [102] = { From 110464d53452767e2947e6cae47469e0a72e9f7a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Jan 2026 16:47:32 +0100 Subject: [PATCH 11/36] Move PyType_FromMetaclass to slots --- Lib/test/test_capi/test_misc.py | 21 ++- Objects/typeobject.c | 280 ++++++++++++++++++++++---------- Python/slots.c | 32 +++- Python/slots.csv | 4 +- Python/slots_generated.c | 4 - 5 files changed, 245 insertions(+), 96 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 4c16bbd4cb0acf..f613bae1ce3352 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1055,13 +1055,13 @@ def test_heaptype_relative_members(self): def test_heaptype_relative_members_errors(self): with self.assertRaisesRegex( SystemError, - r"With Py_RELATIVE_OFFSET, basicsize must be negative"): + r"With Py_RELATIVE_OFFSET, basicsize must be extended"): _testlimitedcapi.make_heaptype_with_member(0, 1234, 0, True) with self.assertRaisesRegex( - SystemError, r"Member offset out of range \(0\.\.-basicsize\)"): + SystemError, r"Member offset out of range \(0\.\.extra_basicsize\)"): _testlimitedcapi.make_heaptype_with_member(0, -8, 1234, True) with self.assertRaisesRegex( - SystemError, r"Member offset out of range \(0\.\.-basicsize\)"): + SystemError, r"Member offset must not be negative"): _testlimitedcapi.make_heaptype_with_member(0, -8, -1, True) Sub = _testlimitedcapi.make_heaptype_with_member(0, -8, 0, True) @@ -1078,7 +1078,7 @@ def test_heaptype_relative_special_members_errors(self): with self.subTest(member_name=member_name): with self.assertRaisesRegex( SystemError, - r"With Py_RELATIVE_OFFSET, basicsize must be negative."): + r"With Py_RELATIVE_OFFSET, basicsize must be extended"): _testlimitedcapi.make_heaptype_with_member( basicsize=sys.getsizeof(object()) + 100, add_relative_flag=True, @@ -1089,7 +1089,7 @@ def test_heaptype_relative_special_members_errors(self): ) with self.assertRaisesRegex( SystemError, - r"Member offset out of range \(0\.\.-basicsize\)"): + r"Member offset must not be negative"): _testlimitedcapi.make_heaptype_with_member( basicsize=-8, add_relative_flag=True, @@ -1098,6 +1098,17 @@ def test_heaptype_relative_special_members_errors(self): member_type=_testlimitedcapi.Py_T_PYSSIZET, member_flags=_testlimitedcapi.Py_READONLY, ) + with self.assertRaisesRegex( + SystemError, + r"Member offset out of range \(0\.\.extra_basicsize\)"): + _testlimitedcapi.make_heaptype_with_member( + basicsize=-8, + add_relative_flag=True, + member_name=member_name, + member_offset=1234, + member_type=_testlimitedcapi.Py_T_PYSSIZET, + member_flags=_testlimitedcapi.Py_READONLY, + ) with self.assertRaisesRegex( SystemError, r"type of %s must be Py_T_PYSSIZET" % member_name): diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 9691c8968d6aab..184c87edf70f08 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5117,43 +5117,6 @@ _align_up(Py_ssize_t size) return (size + ALIGNOF_MAX_ALIGN_T - 1) & ~(ALIGNOF_MAX_ALIGN_T - 1); } -/* Given a PyType_FromMetaclass `bases` argument (NULL, type, or tuple of - * types), return a tuple of types. - */ -inline static PyObject * -get_bases_tuple(PyObject *bases_in, PyType_Spec *spec) -{ - if (!bases_in) { - /* Default: look in the spec, fall back to (type,). */ - PyTypeObject *base = &PyBaseObject_Type; // borrowed ref - PyObject *bases = NULL; // borrowed ref - const PyType_Slot *slot; - for (slot = spec->slots; slot->slot; slot++) { - switch (slot->slot) { - case Py_tp_base: - base = slot->pfunc; - break; - case Py_tp_bases: - bases = slot->pfunc; - break; - } - } - if (!bases) { - return PyTuple_Pack(1, base); - } - if (PyTuple_Check(bases)) { - return Py_NewRef(bases); - } - PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple"); - return NULL; - } - if (PyTuple_Check(bases_in)) { - return Py_NewRef(bases_in); - } - // Not a tuple, should be a single type - return PyTuple_Pack(1, bases_in); -} - static inline int check_basicsize_includes_size_and_offsets(PyTypeObject* type) { @@ -5255,10 +5218,9 @@ special_offset_from_member( return -1; } + PyObject * -PyType_FromMetaclass( - PyTypeObject *metaclass, PyObject *module, - PyType_Spec *spec, PyObject *bases_in) +type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) { /* Invariant: A non-NULL value in one of these means this function holds * a strong reference or owns allocated memory. @@ -5273,39 +5235,95 @@ PyType_FromMetaclass( int r; - /* Prepare slots that need special handling. - * Keep in mind that a slot can be given multiple times: - * if that would cause trouble (leaks, UB, ...), raise an exception. - */ + /* First pass of slots */ Py_ssize_t nmembers = 0; const PyMemberDef *weaklistoffset_member = NULL; const PyMemberDef *dictoffset_member = NULL; const PyMemberDef *vectorcalloffset_member = NULL; char *res_start; + Py_ssize_t basicsize = 0; + Py_ssize_t extra_basicsize = 0; + Py_ssize_t itemsize = 0; + + bool have_relative_members = false; + Py_ssize_t max_relative_offset = 0; + + /* The following are borrowed from the slots. */ + PyTypeObject *metaclass = NULL; + PyObject *module = NULL; + PyObject *bases_slot = NULL; + int flags = 0; _PySlotIterator it; - _PySlotIterator_InitLegacy(&it, spec->slots, _PySlot_KIND_TYPE); + _PySlotIterator_Init(&it, slots, _PySlot_KIND_TYPE); while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: goto finally; + case Py_tp_metaclass: + metaclass = it.current.sl_ptr; + break; + case Py_tp_module: + module = it.current.sl_ptr; + break; + case Py_tp_bases: + bases_slot = it.current.sl_ptr; + if (!PyTuple_Check(bases_slot)) { + PyErr_SetString( + PyExc_SystemError, + "Py_tp_bases is not a tuple"); + goto finally; + } + break; + case Py_tp_base: + if (!_PySlotIterator_SawSlot(&it, Py_tp_bases)) { + bases_slot = it.current.sl_ptr; + } + break; + case Py_tp_basicsize: + basicsize = it.current.sl_size; + if (basicsize <= 0) { + PyErr_SetString( + PyExc_SystemError, + "Py_tp_basicsize must be positive"); + goto finally; + } + break; + case Py_tp_extra_basicsize: + extra_basicsize = it.current.sl_size; + if (extra_basicsize <= 0) { + PyErr_SetString( + PyExc_SystemError, + "Py_tp_extra_basicsize must be positive"); + goto finally; + } + break; + case Py_tp_itemsize: + itemsize = it.current.sl_size; + if (itemsize <= 0) { + PyErr_SetString( + PyExc_SystemError, + "Py_tp_itemsize must be positive"); + goto finally; + } + break; + case Py_tp_flags: + flags = it.current.sl_uint64; + break; case Py_tp_members: for (const PyMemberDef *memb = it.current.sl_ptr; memb->name != NULL; memb++) { nmembers++; if (memb->flags & Py_RELATIVE_OFFSET) { - if (spec->basicsize > 0) { - PyErr_SetString( - PyExc_SystemError, - "With Py_RELATIVE_OFFSET, basicsize must be negative."); - goto finally; - } - if (memb->offset < 0 || memb->offset >= -spec->basicsize) { + if (memb->offset < 0) { PyErr_SetString( PyExc_SystemError, - "Member offset out of range (0..-basicsize)"); + "Member offset must not be negative"); goto finally; } + have_relative_members = true; + max_relative_offset = Py_MAX(max_relative_offset, + memb->offset); } if (strcmp(memb->name, "__weaklistoffset__") == 0) { weaklistoffset_member = memb; @@ -5318,6 +5336,14 @@ PyType_FromMetaclass( } } break; + case Py_tp_token: + if (!spec_for_token && it.current.sl_ptr == Py_TP_USE_SPEC) { + PyErr_SetString( + PyExc_SystemError, + "Py_TP_USE_SPEC and NULL can only be used with PyType_Spec"); + goto finally; + } + break; case Py_tp_doc: /* For the docstring slot, which usually points to a static string literal, we need to make a copy */ @@ -5344,17 +5370,52 @@ PyType_FromMetaclass( } } - /* Prepare the type name and qualname */ + /* Required slots & bad combinations */ - if (spec->name == NULL) { - PyErr_SetString(PyExc_SystemError, - "Type spec does not define the name field."); + if (it.name == NULL) { + if (spec_for_token) { + PyErr_SetString(PyExc_SystemError, + "Type spec does not define the name field."); + } + else { + PyErr_SetString(PyExc_SystemError, + "Py_tp_name slot is required."); + } goto finally; } - const char *s = strrchr(spec->name, '.'); + if (_PySlotIterator_SawSlot(&it, Py_tp_basicsize) + && _PySlotIterator_SawSlot(&it, Py_tp_extra_basicsize)) + { + PyErr_Format( + PyExc_SystemError, + "type %s: Py_tp_basicsize and Py_tp_extra_basicsize are " + "mutually exclusive", + it.name); + goto finally; + } + + if (have_relative_members) { + if (_PySlotIterator_SawSlot(&it, Py_tp_basicsize)) { + PyErr_SetString( + PyExc_SystemError, + "With Py_RELATIVE_OFFSET, basicsize must be extended"); + goto finally; + } + if (max_relative_offset >= extra_basicsize) { + PyErr_SetString( + PyExc_SystemError, + "Member offset out of range (0..extra_basicsize)"); + goto finally; + } + } + + /* Prepare the type name and qualname */ + + assert(it.name); + const char *s = strrchr(it.name, '.'); if (s == NULL) { - s = spec->name; + s = it.name; } else { s++; @@ -5365,7 +5426,7 @@ PyType_FromMetaclass( goto finally; } - /* Copy spec->name to a buffer we own. + /* Copy the name to a buffer we own. * * Unfortunately, we can't use tp_name directly (with some * flag saying that it should be deallocated with the type), @@ -5374,17 +5435,41 @@ PyType_FromMetaclass( * So, we use a separate buffer, _ht_tpname, that's always * deallocated with the type (if it's non-NULL). */ - Py_ssize_t name_buf_len = strlen(spec->name) + 1; + Py_ssize_t name_buf_len = strlen(it.name) + 1; _ht_tpname = PyMem_Malloc(name_buf_len); if (_ht_tpname == NULL) { goto finally; } - memcpy(_ht_tpname, spec->name, name_buf_len); + memcpy(_ht_tpname, it.name, name_buf_len); /* Get a tuple of bases. * bases is a strong reference (unlike bases_in). */ - bases = get_bases_tuple(bases_in, spec); + if ((bases_arg ? 1 : 0) + + (_PySlotIterator_SawSlot(&it, Py_tp_bases) ? 1 : 0) + + (_PySlotIterator_SawSlot(&it, Py_tp_base) ? 1 : 0) + > 1) + { + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "type %s specifies multiple of: bases argument, Py_tp_bases, " + "and Py_tp_base. This will become an error in Python 3.20.", + it.name)) + goto finally; + } + if (!bases_arg) { + bases_arg = bases_slot; + } + if (bases_arg) { + if (PyTuple_Check(bases_arg)) { + bases = Py_NewRef(bases_arg); + } + else { + bases = PyTuple_Pack(1, bases_arg); + } + } + else { + bases = PyTuple_Pack(1, &PyBaseObject_Type); + } if (!bases) { goto finally; } @@ -5394,8 +5479,8 @@ PyType_FromMetaclass( * (This isn't necessary for static types: those can't have heap bases, * and only heap types can be mutable.) */ - if (spec->flags & Py_TPFLAGS_IMMUTABLETYPE) { - if (check_immutable_bases(spec->name, bases, 0) < 0) { + if (flags & Py_TPFLAGS_IMMUTABLETYPE) { + if (check_immutable_bases(it.name, bases, 0) < 0) { goto finally; } } @@ -5433,20 +5518,16 @@ PyType_FromMetaclass( /* Calculate sizes */ - Py_ssize_t basicsize = spec->basicsize; - Py_ssize_t type_data_offset = spec->basicsize; - if (basicsize == 0) { - /* Inherit */ - basicsize = base->tp_basicsize; - } - else if (basicsize < 0) { + Py_ssize_t type_data_offset = basicsize; + if (extra_basicsize) { /* Extend */ + assert(basicsize == 0); type_data_offset = _align_up(base->tp_basicsize); - basicsize = type_data_offset + _align_up(-spec->basicsize); + basicsize = type_data_offset + _align_up(extra_basicsize); /* Inheriting variable-sized types is limited */ if (base->tp_itemsize - && !((base->tp_flags | spec->flags) & Py_TPFLAGS_ITEMS_AT_END)) + && !((base->tp_flags | flags) & Py_TPFLAGS_ITEMS_AT_END)) { PyErr_SetString( PyExc_SystemError, @@ -5454,8 +5535,10 @@ PyType_FromMetaclass( goto finally; } } - - Py_ssize_t itemsize = spec->itemsize; + if (basicsize == 0) { + /* Inherit */ + basicsize = base->tp_basicsize; + } /* Compute special offsets */ @@ -5491,7 +5574,7 @@ PyType_FromMetaclass( type = &res->ht_type; /* The flags must be initialized early, before the GC traverses us */ - type_set_flags(type, spec->flags | Py_TPFLAGS_HEAPTYPE); + type_set_flags(type, flags | Py_TPFLAGS_HEAPTYPE); res->ht_module = Py_XNewRef(module); @@ -5525,9 +5608,9 @@ PyType_FromMetaclass( type->tp_basicsize = basicsize; type->tp_itemsize = itemsize; - /* Copy all the ordinary slots */ + /* Second pass of slots: copy most of them into the type */ - _PySlotIterator_Rewind(&it, spec->slots); + _PySlotIterator_Rewind(&it, slots); while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: @@ -5558,7 +5641,7 @@ PyType_FromMetaclass( case Py_tp_token: { if (it.current.sl_ptr == Py_TP_USE_SPEC) { - res->ht_token = spec; + res->ht_token = spec_for_token; } else { res->ht_token = it.current.sl_ptr; @@ -5647,10 +5730,10 @@ PyType_FromMetaclass( goto finally; } if (r == 0) { - s = strrchr(spec->name, '.'); + s = strrchr(it.name, '.'); if (s != NULL) { PyObject *modname = PyUnicode_FromStringAndSize( - spec->name, (Py_ssize_t)(s - spec->name)); + it.name, (Py_ssize_t)(s - it.name)); if (modname == NULL) { goto finally; } @@ -5663,7 +5746,7 @@ PyType_FromMetaclass( else { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, "builtin type %.200s has no __module__ attribute", - spec->name)) + it.name)) goto finally; } } @@ -5685,6 +5768,37 @@ PyType_FromMetaclass( return (PyObject*)res; } +PyObject * +PyType_FromMetaclass( + PyTypeObject *metaclass, PyObject *module, + PyType_Spec *spec, PyObject *bases) +{ + static const PySlot nop = {Py_slot_invalid, .sl_flags=PySlot_OPTIONAL}; + PySlot slots[] = { + PySlot_DATA(Py_tp_name, spec->name), + (spec->basicsize > 0 + ? (PySlot)PySlot_SIZE(Py_tp_basicsize, spec->basicsize) + : spec->basicsize < 0 + ? (PySlot)PySlot_SIZE(Py_tp_extra_basicsize, -spec->basicsize) + : nop), + (spec->itemsize + ? (PySlot)PySlot_SIZE(Py_tp_itemsize, spec->itemsize) + : nop), + PySlot_UINT64(Py_tp_flags, spec->flags), + PySlot_DATA(Py_tp_slots, spec->slots), + (metaclass + ? (PySlot)PySlot_PTR(Py_tp_metaclass, metaclass) + : nop), + (module + ? (PySlot)PySlot_PTR(Py_tp_module, module) + : nop), + /* We pass *bases* separately for deprecation and error reporting; + * eventually we can convert it to Py_tp_bases. */ + PySlot_END, + }; + return type_from_slots(slots, spec, bases); +} + PyObject * PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) { diff --git a/Python/slots.c b/Python/slots.c index 7c5bb2a5dcd7a4..54c44ff768e030 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -213,7 +213,7 @@ _PySlotIterator_Next(_PySlotIterator *it) MSG("slot %d: %s", (int)result->sl_id, it->info->name); if (it->is_first_run && it->info->is_name) { - MSG("setting name for error messages"); + MSG("setting name: %s", (char*)result->sl_ptr); assert(it->info->dtype == _PySlot_TYPE_PTR); it->name = result->sl_ptr; } @@ -286,7 +286,35 @@ _PySlotIterator_Next(_PySlotIterator *it) } advance(it); - MSG("result: %d (%s)", (int)result->sl_id, it->info->name); + switch (it->info->dtype) { + case _PySlot_TYPE_VOID: + case _PySlot_TYPE_PTR: + MSG("result: %d (%s): %p", + (int)result->sl_id, it->info->name, + (void*)result->sl_ptr); + break; + case _PySlot_TYPE_FUNC: + MSG("result: %d (%s): %p", + (int)result->sl_id, it->info->name, + (void*)result->sl_func); + break; + case _PySlot_TYPE_SIZE: + MSG("result: %d (%s): %zd", + (int)result->sl_id, it->info->name, + (Py_ssize_t)result->sl_size); + break; + case _PySlot_TYPE_INT64: + MSG("result: %d (%s): %ld", + (int)result->sl_id, it->info->name, + (long)result->sl_int64); + break; + case _PySlot_TYPE_UINT64: + MSG("result: %d (%s): %lu (0x%lx)", + (int)result->sl_id, it->info->name, + (unsigned long)result->sl_int64, + (unsigned long)result->sl_int64); + break; + } assert (result->sl_id > 0); assert (result->sl_id <= _Py_slot_COUNT); assert (result->sl_id <= INT_MAX); diff --git a/Python/slots.csv b/Python/slots.csv index cc5c380d3b0c86..facf0586d44226 100644 --- a/Python/slots.csv +++ b/Python/slots.csv @@ -140,8 +140,8 @@ id, name, kind, dtype 95, "tp_name", type, ptr, virtual=true, added=3.14, null=reject, duplicates=no, is_name=true 96, "tp_basicsize", type, size, added=3.14, duplicates=no 97, "tp_extra_basicsize", type, size, virtual=true, added=3.14, duplicates=no -98, "tp_itemsize", type, size, added=3.14, duplicates=no -99, "tp_flags", type, uint64, added=3.14, duplicates=no +98, "tp_itemsize", type, size, virtual=true, added=3.14, duplicates=no +99, "tp_flags", type, uint64, virtual=true, added=3.14, duplicates=no 100, "mod_name", mod, ptr, added=3.14, null=reject, duplicates=no, is_name=true 101, "mod_doc", mod, ptr, added=3.14, null=reject, duplicates=no 102, "mod_state_size", mod, size, added=3.14, duplicates=no diff --git a/Python/slots_generated.c b/Python/slots_generated.c index 8c2d61dbd96a14..66a37f6974f5c8 100644 --- a/Python/slots_generated.c +++ b/Python/slots_generated.c @@ -866,16 +866,12 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .name = "Py_tp_itemsize", .dtype = _PySlot_TYPE_SIZE, .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_itemsize), - .type_info.subslot_offset = -1, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [99] = { .name = "Py_tp_flags", .dtype = _PySlot_TYPE_UINT64, .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_flags), - .type_info.subslot_offset = -1, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [100] = { From 6fb004687bd81d052fc6e8d2445662472aa823f3 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 14:48:26 +0200 Subject: [PATCH 12/36] PyType_FromSlots --- Include/object.h | 3 +++ Objects/typeobject.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/Include/object.h b/Include/object.h index d51132be1a6656..c7e2a9c6e0764d 100644 --- a/Include/object.h +++ b/Include/object.h @@ -364,6 +364,9 @@ PyAPI_FUNC(Py_ssize_t) PyType_GetTypeDataSize(PyTypeObject *cls); PyAPI_FUNC(int) PyType_GetBaseByToken(PyTypeObject *, void *, PyTypeObject **); #define Py_TP_USE_SPEC NULL #endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 14) +PyAPI_FUNC(PyObject *) PyType_FromSlots(struct PySlot *); +#endif /* Generic type check */ PyAPI_FUNC(int) PyType_IsSubtype(PyTypeObject *, PyTypeObject *); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 184c87edf70f08..cf02e8359a5a88 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5768,6 +5768,12 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) return (PyObject*)res; } +PyObject * +PyType_FromSlots(PySlot *slots) +{ + return type_from_slots(slots, NULL, NULL); +} + PyObject * PyType_FromMetaclass( PyTypeObject *metaclass, PyObject *module, From 866eafc0e0717d481d28a41259a9bcd40329aba3 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 14:49:30 +0200 Subject: [PATCH 13/36] slots.csv update --- Python/slots.csv | 74 +++++++++++++++++++++------------------- Python/slots_generated.c | 2 -- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/Python/slots.csv b/Python/slots.csv index facf0586d44226..7a959d27941a54 100644 --- a/Python/slots.csv +++ b/Python/slots.csv @@ -28,21 +28,25 @@ id, name, kind, dtype #, with '='; everything after that is the value. No spaces around '='. #, #, For all types: -#, - 'added': Python version that added the slot *to the limited API* #, - 'compat': slot number before 3.14 #, - 'subslots=true': contains an array of slots to be merged in +#, - 'duplicates': behavior if multiple slots of this type are given +#, - 'yes': duplicates allowed +#, - 'no': duplicates rejected +#, - default: warn #, For pointers: -#, - 'PyObject=true': This is a Python object (or NULL) #, - 'null': Behavior for NULL values: #, - 'reject': NULLs not allowed #, - 'allow': NULLs allowed #, - default is that NULL is deprecated +#, - 'is_name': this is the name slot (used automatically for error messages) #, For type slots: #, - 'table': Slot table like 'nb' or 'tp'. If not specified: use the first -#, part of the name +#, part of the name (except virtual slots) +#, - 'field': Field name. If not specified: use the name #, - 'virtual=true': Does not directly correspond to a PyTypeObject (sub)field -0, "slot_end", slot, void, added=3.14 +0, "slot_end", slot, void 1, "bf_getbuffer/mod_create", compat, void 2, "bf_releasebuffer/mod_exec", compat, void 3, "mp_ass_subscript/mod_multiple_interpreters", compat, void @@ -90,8 +94,8 @@ id, name, kind, dtype 45, "sq_length", type, func 46, "sq_repeat", type, func 47, "tp_alloc", type, func -48, "tp_base", type, ptr, PyObject=true -49, "tp_bases", type, ptr, PyObject=true +48, "tp_base", type, ptr +49, "tp_bases", type, ptr 50, "tp_call", type, func 51, "tp_clear", type, func 52, "tp_dealloc", type, func @@ -122,34 +126,34 @@ id, name, kind, dtype 77, "am_await", type, func 78, "am_aiter", type, func 79, "am_anext", type, func -80, "tp_finalize", type, func, added=3.5 -81, "am_send", type, func, added=3.10 -82, "tp_vectorcall", type, func, added=3.14 -83, "tp_token", type, ptr, added=3.14, table=ht, field=ht_token, null=allow, duplicates=no +80, "tp_finalize", type, func +81, "am_send", type, func +82, "tp_vectorcall", type, func +83, "tp_token", type, ptr, table=ht, field=ht_token, null=allow, duplicates=no 84, "mod_create", mod, func, compat=1, duplicates=no 85, "mod_exec", mod, func, compat=2, duplicates=yes -86, "mod_multiple_interpreters", mod, uint64, compat=3, added=3.12, duplicates=no -87, "mod_gil", mod, uint64, compat=4, added=3.13, duplicates=no -88, "bf_getbuffer", type, func, compat=1, added=3.14 -89, "bf_releasebuffer", type, func, compat=2, added=3.14 -90, "mp_ass_subscript", type, func, compat=3, added=3.14 -91, "mp_length", type, func, compat=4, added=3.14 -92, "slot_subslots", slot, ptr, added=3.14, subslots=true, null=allow -93, "tp_slots", type, ptr, virtual=true, added=3.14, subslots=true, null=allow -94, "mod_slots", mod, ptr, added=3.14, subslots=true, null=allow -95, "tp_name", type, ptr, virtual=true, added=3.14, null=reject, duplicates=no, is_name=true -96, "tp_basicsize", type, size, added=3.14, duplicates=no -97, "tp_extra_basicsize", type, size, virtual=true, added=3.14, duplicates=no -98, "tp_itemsize", type, size, virtual=true, added=3.14, duplicates=no -99, "tp_flags", type, uint64, virtual=true, added=3.14, duplicates=no -100, "mod_name", mod, ptr, added=3.14, null=reject, duplicates=no, is_name=true -101, "mod_doc", mod, ptr, added=3.14, null=reject, duplicates=no -102, "mod_state_size", mod, size, added=3.14, duplicates=no -103, "mod_methods", mod, ptr, added=3.14, null=reject, duplicates=no -104, "mod_state_traverse", mod, func, added=3.14, null=reject, duplicates=no -105, "mod_state_clear", mod, func, added=3.14, null=reject, duplicates=no -106, "mod_state_free", mod, func, added=3.14, null=reject, duplicates=no -107, "tp_metaclass", type, ptr, virtual=true, PyObject=true, added=3.14, null=reject, duplicates=no -108, "tp_module", type, ptr, virtual=true, PyObject=true, added=3.14, null=reject, duplicates=no -109, "mod_abi", mod, ptr, added=3.15, null=reject, duplicates=yes -110, "mod_token", mod, ptr, added=3.15, null=reject, duplicates=no +86, "mod_multiple_interpreters", mod, uint64, compat=3, duplicates=no +87, "mod_gil", mod, uint64, compat=4, duplicates=no +88, "bf_getbuffer", type, func, compat=1 +89, "bf_releasebuffer", type, func, compat=2 +90, "mp_ass_subscript", type, func, compat=3 +91, "mp_length", type, func, compat=4 +92, "slot_subslots", slot, ptr, subslots=true, null=allow +93, "tp_slots", type, ptr, virtual=true, subslots=true, null=allow +94, "mod_slots", mod, ptr, subslots=true, null=allow +95, "tp_name", type, ptr, virtual=true, null=reject, duplicates=no, is_name=true +96, "tp_basicsize", type, size, virtual=true, duplicates=no +97, "tp_extra_basicsize", type, size, virtual=true, duplicates=no +98, "tp_itemsize", type, size, virtual=true, duplicates=no +99, "tp_flags", type, uint64, virtual=true, duplicates=no +100, "mod_name", mod, ptr, null=reject, duplicates=no, is_name=true +101, "mod_doc", mod, ptr, null=reject, duplicates=no +102, "mod_state_size", mod, size, duplicates=no +103, "mod_methods", mod, ptr, null=reject, duplicates=no +104, "mod_state_traverse", mod, func, null=reject, duplicates=no +105, "mod_state_clear", mod, func, null=reject, duplicates=no +106, "mod_state_free", mod, func, null=reject, duplicates=no +107, "tp_metaclass", type, ptr, virtual=true, null=reject, duplicates=no +108, "tp_module", type, ptr, virtual=true, null=reject, duplicates=no +109, "mod_abi", mod, ptr, null=reject, duplicates=yes +110, "mod_token", mod, ptr, null=reject, duplicates=no diff --git a/Python/slots_generated.c b/Python/slots_generated.c index 66a37f6974f5c8..76222986e43082 100644 --- a/Python/slots_generated.c +++ b/Python/slots_generated.c @@ -852,8 +852,6 @@ _PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { .name = "Py_tp_basicsize", .dtype = _PySlot_TYPE_SIZE, .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_basicsize), - .type_info.subslot_offset = -1, .duplicate_handling = _PySlot_PROBLEM_REJECT, }, [97] = { From 325f1a8d5f07ab43ec30eae77011c75255bd5f76 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 14:52:43 +0200 Subject: [PATCH 14/36] squash up --- Include/slots_generated.h | 2 +- Tools/build/generate_slots.py | 127 +++++++++++----------------------- 2 files changed, 41 insertions(+), 88 deletions(-) diff --git a/Include/slots_generated.h b/Include/slots_generated.h index 9a6024f057710c..42edd3ad4c69ff 100644 --- a/Include/slots_generated.h +++ b/Include/slots_generated.h @@ -118,4 +118,4 @@ #define Py_mod_token 110 #define _Py_slot_COUNT 111 -#endif +#endif /* _PY_HAVE_SLOTS_GENERATED_H */ diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index 62cc35c840f65c..4362855b30099b 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -3,9 +3,9 @@ """ import io -import csv import sys import json +import tomllib import argparse import functools import contextlib @@ -14,86 +14,23 @@ GENERATED_BY = 'Generated by Tools/build/generate_slots.py' REPO_ROOT = Path(__file__).parent.parent.parent -DEFAULT_INPUT_PATH = REPO_ROOT / 'Python/slots.csv' +DEFAULT_INPUT_PATH = REPO_ROOT / 'Python/slots.toml' DEFAULT_HEADER_PATH = REPO_ROOT / 'Include/slots_generated.h' DEFAULT_C_PATH = REPO_ROOT / 'Python/slots_generated.c' -class SlotInfo: - def __init__(self, **info_dict): - self._d = info_dict - self.id = int(self['id']) - assert self.id >= 0 - self.name = self['name'] - self.kind = self['kind'] - self.dtype = self['dtype'] - - def to_dict(self): - return self._d - - def __getitem__(self, name): - return self._d[name] - - def get(self, name, default=None): - return self._d.get(name, default) - - def get_int(self, name, default=None): - try: - return int(self._d[name]) - except KeyError: - return default - - def get_bool(self, name, default=None): - try: - value = self._d[name] - except KeyError: - return default - return {'true': True, 'false': False}[value] - - def __repr__(self): - return f'<{type(self).__name__} {self._d}>' - - @functools.cached_property - def added(self): - added = self._d.get('added') - if added is None: - return None - x, y = added.split('.') - return int(x), int(y) - - @functools.cached_property - def type_table(self): +def parse_slots(file): + toml_contents = tomllib.load(file) + result = [None] * len(toml_contents) + for key, value in toml_contents.items(): + slot_id = int(key) try: - return self['table'] - except KeyError: - return self.name.partition('_')[0] - - @functools.cached_property - def initializers_for_duplicates(self): - match self.get('duplicates'): - case 'no': - return {'duplicate_handling': '_PySlot_PROBLEM_REJECT'} - case 'yes': - return {} - return {'duplicate_handling': '_PySlot_PROBLEM_DEPRECATED'} - - -def parse_slots(lines): - result = [] - reader = csv.DictReader(lines, restkey='named', skipinitialspace=True) - expected_id = 0 - for info_dict in reader: - if info_dict.get('id', '#') == '#': - continue - for kv in info_dict.pop('named', ()): - key, eq, value = kv.partition('=') - info_dict[key] = value - slot = SlotInfo(**info_dict) - if slot.id != expected_id: - raise ValueError( - 'for now, slots must be defined in order with no gaps ' - + f'({expected_id=}, got={info_dict}') - expected_id += 1 - result.append(slot) + if result[slot_id]: + raise ValueError(f'slot ID {slot_id} repeated') + kind = value['kind'] + result[slot_id] = {'id': slot_id, **value} + except Exception as e: + e.add_note(f'handling slot {slot_id}') + raise return result @@ -110,16 +47,22 @@ def write_header(f, slots): out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD') out(f'#endif') out() + compat_ids = {new_name: slot['id'] + for slot in slots + for new_name in slot.get('equivalents', {}).values() + } + print(compat_ids) for slot in slots: - if slot.kind == 'compat': + if slot['kind'] == 'compat': continue - slot_id = slot.id - if compat := slot.get('compat'): - slot_id = f'_Py_SLOT_COMPAT_VALUE({compat}, {slot.id})' - out(f'#define Py_{slot.name} {slot_id}') + slot_id = slot["id"] + name = slot["name"] + if compat := compat_ids.get(name): + slot_id = f'_Py_SLOT_COMPAT_VALUE({compat}, {slot_id})' + out(f'#define {name} {slot_id}') out() out(f'#define _Py_slot_COUNT {len(slots)}') - out(f'#endif') + out(f'#endif /* _PY_HAVE_SLOTS_GENERATED_H */') def write_c(f, slots): @@ -153,6 +96,8 @@ def write_c(f, slots): case 'type': field = slot.get('field', slot.name) if not slot.get_bool('virtual'): + if slot.id > FIRST_3_15_SLOT: + raise ValueError('new slots should be virtual') tpo = 'PyTypeObject' typeobj, subtable, tabletype = { 'tp': (tpo, None, None), @@ -185,13 +130,21 @@ def write_c(f, slots): initializers['null_handling'] = '_PySlot_PROBLEM_ALLOW' case None: if slot.dtype in {'func', 'ptr'}: - if slot.id > 92: + if slot.id > FIRST_3_15_SLOT: raise ValueError( f'slot {slot.name} needs explicit NULL ' + 'handling specification') initializers['null_handling'] = ( '_PySlot_PROBLEM_DEPRECATED') - initializers.update(slot.initializers_for_duplicates) + match slot.get('duplicates'): + case 'no': + initializers['duplicate_handling'] = '_PySlot_PROBLEM_REJECT' + case 'yes': + pass + case None: + initializers['duplicate_handling'] = '_PySlot_PROBLEM_DEPRECATED' + case _: + raise ValueError(slot['duplicates']) if slot.get_bool('is_name'): initializers['is_name'] = 'true' for name, initializer in initializers.items(): @@ -199,7 +152,7 @@ def write_c(f, slots): out(f" }},") except Exception as e: e.add_note(f'handling slot {slot}') - raise e + raise out(f" [{len(slots)}] = {{0}}") out(f"}};") @@ -255,7 +208,7 @@ def main(argv): if args.cfile is None: args.cfile = DEFAULT_C_PATH - with open(args.input) as f: + with open(args.input, 'rb') as f: slots = parse_slots(f) if args.jsonl: From e22e3af854b33cdf2eadc4490395e908428d77c2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 15:12:13 +0200 Subject: [PATCH 15/36] Put slot info in generated switches --- Include/internal/pycore_slots.h | 98 +-- Include/internal/pycore_slots_generated.h | 454 ++++++++++ Makefile.pre.in | 1 + Modules/_testcapi/type.c | 4 +- Objects/moduleobject.c | 9 +- Objects/typeobject.c | 56 +- PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 + Python/slots.c | 176 ++-- Python/slots.toml | 806 ++++++++++++++++++ Python/slots_generated.c | 982 +--------------------- Tools/build/generate_slots.py | 302 +++++-- 12 files changed, 1646 insertions(+), 1246 deletions(-) create mode 100644 Include/internal/pycore_slots_generated.h create mode 100644 Python/slots.toml diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index ef056961733d1d..bfd5c107fe4e9b 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -7,14 +7,14 @@ #include -typedef enum _PySlot_TYPE { - _PySlot_TYPE_VOID, - _PySlot_TYPE_FUNC, - _PySlot_TYPE_PTR, - _PySlot_TYPE_SIZE, - _PySlot_TYPE_INT64, - _PySlot_TYPE_UINT64, -}_PySlot_TYPE; +typedef enum _PySlot_DTYPE { + _PySlot_DTYPE_VOID, + _PySlot_DTYPE_FUNC, + _PySlot_DTYPE_PTR, + _PySlot_DTYPE_SIZE, + _PySlot_DTYPE_INT64, + _PySlot_DTYPE_UINT64, +}_PySlot_DTYPE; typedef enum _PySlot_KIND { _PySlot_KIND_TYPE, @@ -29,46 +29,7 @@ typedef enum _PySlot_PROBLEM_HANDLING { _PySlot_PROBLEM_REJECT, } _PySlot_PROBLEM_HANDLING; -/* Internal information about a slot */ -typedef struct _PySlot_Info { - const char *name; /* without the Py_ prefix */ - _PySlot_TYPE dtype; - _PySlot_KIND kind; - _PySlot_PROBLEM_HANDLING null_handling; - _PySlot_PROBLEM_HANDLING duplicate_handling; - bool is_subslots :1; - bool is_name :1; - union { - struct { - /* For type slots (_PySlot_KIND_TYPE): - * Most slots corresponds to members in PyTypeObject & similar - * structs. - * Each entry has two offsets, "slot_offset" and "subslot_offset". - * If is subslot_offset is -1, slot_offset is an offset within the - * PyTypeObject struct. - * Otherwise slot_offset is an offset to a pointer to a sub-slots - * struct (such as "tp_as_number"), and subslot_offset is the - * offset within that struct. - * - * If both are zero, the slot needs special handling; that is, - * the offset mechanism isn't used. - */ - short subslot_offset; - short slot_offset; - } type_info; - struct { - /* For compat slots (_PySlot_KIND_COMPAT): - * these slots were renumbered; `type_id` is the ID of the new - * type slot; `mod_id` of the module slot. - */ - uint8_t type_id; - uint8_t mod_id; - } compat_info; - }; -} _PySlot_Info; - -// The actual table is generated by a script. -extern _PySlot_Info _PySlot_InfoTable[]; +PyAPI_DATA(char *) _PySlot_names[]; #define _PySlot_MAX_NESTING 5 @@ -100,15 +61,12 @@ typedef struct { // The slot. Always a copy; may be modified by caller of the iterator. PySlot current; - // Information about the slot - const _PySlot_Info *info; - // Name of the object (type/module) being defined, NULL if unknown. // Set by _PySlotIterator_Next as soon as it sees a tp_name/mod_name slot; // used for internal error messages but available to the caller too. // This points to the slot; must be copied for longer usage. // The name is not reset by rewinding. - char *name; + const char *name; } _PySlotIterator; /* Initialize an iterator using a Py_Slot array */ @@ -142,15 +100,35 @@ PyAPI_FUNC(bool) _PySlotIterator_Next(_PySlotIterator *it); */ PyAPI_FUNC(bool) _PySlotIterator_SawSlot(_PySlotIterator *, int); -static inline bool -_PySlot_IsStatic(PySlot *slot, const _PySlot_Info *info) +static inline const char * +_PySlot_GetName(uint16_t id) { - return (slot->sl_flags & PySlot_STATIC) - || (info->dtype == _PySlot_TYPE_VOID) - || (info->dtype == _PySlot_TYPE_FUNC) - || (info->dtype == _PySlot_TYPE_SIZE) - || (info->dtype == _PySlot_TYPE_INT64) - || (info->dtype == _PySlot_TYPE_UINT64); + if (id >= _Py_slot_COUNT) { + return ""; + } + if (id == Py_slot_invalid) { + return "Py_slot_invalid"; + } + return _PySlot_names[id]; } +static inline void +_PySlot_err_bad_slot(char *kind, uint16_t id) +{ + if (id < _Py_slot_COUNT) { + PyErr_Format(PyExc_SystemError, "invalid %s slot %d (%s)", + kind, (unsigned int)id, _PySlot_names[id]); + } + else if (id == Py_slot_invalid) { + PyErr_Format(PyExc_SystemError, "invalid slot (Py_slot_invalid, %u)", + (unsigned int)id); + } + else { + PyErr_Format(PyExc_SystemError, "unknown %s slot ID %u", + kind, (unsigned int)id); + } +} + +#include "internal/pycore_slots_generated.h" + #endif // _Py_PYCORE_SLOTS_H diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h new file mode 100644 index 00000000000000..19cda06ad89072 --- /dev/null +++ b/Include/internal/pycore_slots_generated.h @@ -0,0 +1,454 @@ +/* Generated by Tools/build/generate_slots.py */ + +#ifndef _PY_HAVE_INTERNAL_SLOTS_GENERATED_H +#define _PY_HAVE_INTERNAL_SLOTS_GENERATED_H + +static inline uint16_t +_PySlot_resolve_type_slot(uint16_t slot_id) +{ + switch (slot_id) { + case 1: return 88; + case 2: return 89; + case 3: return 90; + case 4: return 91; + case 0: case 5: case 6: case 7: case 8: case 9: case 10: case 11: + case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: + case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: + case 28: case 29: case 30: case 31: case 32: case 33: case 34: case 35: + case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: + case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: + case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: + case 60: case 61: case 62: case 63: case 64: case 65: case 66: case 67: + case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: + case 76: case 77: case 78: case 79: case 80: case 81: case 82: case 83: + case 88: case 89: case 90: case 91: case 92: case 93: case 95: case 96: + case 97: case 98: case 99: case 107: case 108: + return slot_id; + default: + return Py_slot_invalid; + } +} + +static inline uint16_t +_PySlot_resolve_mod_slot(uint16_t slot_id) +{ + switch (slot_id) { + case 1: return 84; + case 2: return 85; + case 3: return 86; + case 4: return 87; + case 0: case 84: case 85: case 86: case 87: case 92: case 94: case 100: + case 101: case 102: case 103: case 104: case 105: case 106: case 109: + case 110: + return slot_id; + default: + return Py_slot_invalid; + } +} + +static inline void* +_PySlot_type_ptr(PyTypeObject *tp, uint16_t slot_id) +{ + switch (slot_id) { + case 5: return (tp->tp_as_mapping) + ? &tp->tp_as_mapping->mp_subscript : NULL; + case 6: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_absolute : NULL; + case 7: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_add : NULL; + case 8: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_and : NULL; + case 9: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_bool : NULL; + case 10: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_divmod : NULL; + case 11: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_float : NULL; + case 12: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_floor_divide : NULL; + case 13: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_index : NULL; + case 14: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_add : NULL; + case 15: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_and : NULL; + case 16: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_floor_divide : NULL; + case 17: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_lshift : NULL; + case 18: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_multiply : NULL; + case 19: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_or : NULL; + case 20: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_power : NULL; + case 21: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_remainder : NULL; + case 22: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_rshift : NULL; + case 23: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_subtract : NULL; + case 24: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_true_divide : NULL; + case 25: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_xor : NULL; + case 26: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_int : NULL; + case 27: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_invert : NULL; + case 28: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_lshift : NULL; + case 29: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_multiply : NULL; + case 30: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_negative : NULL; + case 31: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_or : NULL; + case 32: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_positive : NULL; + case 33: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_power : NULL; + case 34: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_remainder : NULL; + case 35: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_rshift : NULL; + case 36: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_subtract : NULL; + case 37: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_true_divide : NULL; + case 38: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_xor : NULL; + case 39: return (tp->tp_as_sequence) + ? &tp->tp_as_sequence->sq_ass_item : NULL; + case 40: return (tp->tp_as_sequence) + ? &tp->tp_as_sequence->sq_concat : NULL; + case 41: return (tp->tp_as_sequence) + ? &tp->tp_as_sequence->sq_contains : NULL; + case 42: return (tp->tp_as_sequence) + ? &tp->tp_as_sequence->sq_inplace_concat : NULL; + case 43: return (tp->tp_as_sequence) + ? &tp->tp_as_sequence->sq_inplace_repeat : NULL; + case 44: return (tp->tp_as_sequence) + ? &tp->tp_as_sequence->sq_item : NULL; + case 45: return (tp->tp_as_sequence) + ? &tp->tp_as_sequence->sq_length : NULL; + case 46: return (tp->tp_as_sequence) + ? &tp->tp_as_sequence->sq_repeat : NULL; + case 47: return &tp->tp_alloc; + case 48: return &tp->tp_base; + case 49: return &tp->tp_bases; + case 50: return &tp->tp_call; + case 51: return &tp->tp_clear; + case 52: return &tp->tp_dealloc; + case 53: return &tp->tp_del; + case 54: return &tp->tp_descr_get; + case 55: return &tp->tp_descr_set; + case 56: return &tp->tp_doc; + case 57: return &tp->tp_getattr; + case 58: return &tp->tp_getattro; + case 59: return &tp->tp_hash; + case 60: return &tp->tp_init; + case 61: return &tp->tp_is_gc; + case 62: return &tp->tp_iter; + case 63: return &tp->tp_iternext; + case 64: return &tp->tp_methods; + case 65: return &tp->tp_new; + case 66: return &tp->tp_repr; + case 67: return &tp->tp_richcompare; + case 68: return &tp->tp_setattr; + case 69: return &tp->tp_setattro; + case 70: return &tp->tp_str; + case 71: return &tp->tp_traverse; + case 72: return &tp->tp_members; + case 73: return &tp->tp_getset; + case 74: return &tp->tp_free; + case 75: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_matrix_multiply : NULL; + case 76: return (tp->tp_as_number) + ? &tp->tp_as_number->nb_inplace_matrix_multiply : NULL; + case 77: return (tp->tp_as_async) + ? &tp->tp_as_async->am_await : NULL; + case 78: return (tp->tp_as_async) + ? &tp->tp_as_async->am_aiter : NULL; + case 79: return (tp->tp_as_async) + ? &tp->tp_as_async->am_anext : NULL; + case 80: return &tp->tp_finalize; + case 81: return (tp->tp_as_async) + ? &tp->tp_as_async->am_send : NULL; + case 82: return &tp->tp_vectorcall; + case 83: return (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) + ? &((PyHeapTypeObject*)tp)->ht_token : NULL; + case 88: return (tp->tp_as_buffer) + ? &tp->tp_as_buffer->bf_getbuffer : NULL; + case 89: return (tp->tp_as_buffer) + ? &tp->tp_as_buffer->bf_releasebuffer : NULL; + case 90: return (tp->tp_as_mapping) + ? &tp->tp_as_mapping->mp_ass_subscript : NULL; + case 91: return (tp->tp_as_mapping) + ? &tp->tp_as_mapping->mp_length : NULL; + } + _PySlot_err_bad_slot("type", slot_id); + return NULL; +} + +static inline void +_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht, PySlot slot) +{ + switch (slot.sl_id) { + case 5: ht->as_mapping.mp_subscript = (binaryfunc)slot.sl_func; break; + case 6: ht->as_number.nb_absolute = (unaryfunc)slot.sl_func; break; + case 7: ht->as_number.nb_add = (binaryfunc)slot.sl_func; break; + case 8: ht->as_number.nb_and = (binaryfunc)slot.sl_func; break; + case 9: ht->as_number.nb_bool = (inquiry)slot.sl_func; break; + case 10: ht->as_number.nb_divmod = (binaryfunc)slot.sl_func; break; + case 11: ht->as_number.nb_float = (unaryfunc)slot.sl_func; break; + case 12: ht->as_number.nb_floor_divide = (binaryfunc)slot.sl_func; break; + case 13: ht->as_number.nb_index = (unaryfunc)slot.sl_func; break; + case 14: ht->as_number.nb_inplace_add = (binaryfunc)slot.sl_func; break; + case 15: ht->as_number.nb_inplace_and = (binaryfunc)slot.sl_func; break; + case 16: ht->as_number.nb_inplace_floor_divide = (binaryfunc)slot.sl_func; break; + case 17: ht->as_number.nb_inplace_lshift = (binaryfunc)slot.sl_func; break; + case 18: ht->as_number.nb_inplace_multiply = (binaryfunc)slot.sl_func; break; + case 19: ht->as_number.nb_inplace_or = (binaryfunc)slot.sl_func; break; + case 20: ht->as_number.nb_inplace_power = (ternaryfunc)slot.sl_func; break; + case 21: ht->as_number.nb_inplace_remainder = (binaryfunc)slot.sl_func; break; + case 22: ht->as_number.nb_inplace_rshift = (binaryfunc)slot.sl_func; break; + case 23: ht->as_number.nb_inplace_subtract = (binaryfunc)slot.sl_func; break; + case 24: ht->as_number.nb_inplace_true_divide = (binaryfunc)slot.sl_func; break; + case 25: ht->as_number.nb_inplace_xor = (binaryfunc)slot.sl_func; break; + case 26: ht->as_number.nb_int = (unaryfunc)slot.sl_func; break; + case 27: ht->as_number.nb_invert = (unaryfunc)slot.sl_func; break; + case 28: ht->as_number.nb_lshift = (binaryfunc)slot.sl_func; break; + case 29: ht->as_number.nb_multiply = (binaryfunc)slot.sl_func; break; + case 30: ht->as_number.nb_negative = (unaryfunc)slot.sl_func; break; + case 31: ht->as_number.nb_or = (binaryfunc)slot.sl_func; break; + case 32: ht->as_number.nb_positive = (unaryfunc)slot.sl_func; break; + case 33: ht->as_number.nb_power = (ternaryfunc)slot.sl_func; break; + case 34: ht->as_number.nb_remainder = (binaryfunc)slot.sl_func; break; + case 35: ht->as_number.nb_rshift = (binaryfunc)slot.sl_func; break; + case 36: ht->as_number.nb_subtract = (binaryfunc)slot.sl_func; break; + case 37: ht->as_number.nb_true_divide = (binaryfunc)slot.sl_func; break; + case 38: ht->as_number.nb_xor = (binaryfunc)slot.sl_func; break; + case 39: ht->as_sequence.sq_ass_item = (ssizeobjargproc)slot.sl_func; break; + case 40: ht->as_sequence.sq_concat = (binaryfunc)slot.sl_func; break; + case 41: ht->as_sequence.sq_contains = (objobjproc)slot.sl_func; break; + case 42: ht->as_sequence.sq_inplace_concat = (binaryfunc)slot.sl_func; break; + case 43: ht->as_sequence.sq_inplace_repeat = (ssizeargfunc)slot.sl_func; break; + case 44: ht->as_sequence.sq_item = (ssizeargfunc)slot.sl_func; break; + case 45: ht->as_sequence.sq_length = (lenfunc)slot.sl_func; break; + case 46: ht->as_sequence.sq_repeat = (ssizeargfunc)slot.sl_func; break; + case 47: ht->ht_type.tp_alloc = (allocfunc)slot.sl_func; break; + case 48: ht->ht_type.tp_base = slot.sl_ptr; break; + case 49: ht->ht_type.tp_bases = slot.sl_ptr; break; + case 50: ht->ht_type.tp_call = (ternaryfunc)slot.sl_func; break; + case 51: ht->ht_type.tp_clear = (inquiry)slot.sl_func; break; + case 52: ht->ht_type.tp_dealloc = (destructor)slot.sl_func; break; + case 53: ht->ht_type.tp_del = (destructor)slot.sl_func; break; + case 54: ht->ht_type.tp_descr_get = (descrgetfunc)slot.sl_func; break; + case 55: ht->ht_type.tp_descr_set = (descrsetfunc)slot.sl_func; break; + case 56: ht->ht_type.tp_doc = slot.sl_ptr; break; + case 57: ht->ht_type.tp_getattr = (getattrfunc)slot.sl_func; break; + case 58: ht->ht_type.tp_getattro = (getattrofunc)slot.sl_func; break; + case 59: ht->ht_type.tp_hash = (hashfunc)slot.sl_func; break; + case 60: ht->ht_type.tp_init = (initproc)slot.sl_func; break; + case 61: ht->ht_type.tp_is_gc = (inquiry)slot.sl_func; break; + case 62: ht->ht_type.tp_iter = (getiterfunc)slot.sl_func; break; + case 63: ht->ht_type.tp_iternext = (iternextfunc)slot.sl_func; break; + case 64: ht->ht_type.tp_methods = slot.sl_ptr; break; + case 65: ht->ht_type.tp_new = (newfunc)slot.sl_func; break; + case 66: ht->ht_type.tp_repr = (reprfunc)slot.sl_func; break; + case 67: ht->ht_type.tp_richcompare = (richcmpfunc)slot.sl_func; break; + case 68: ht->ht_type.tp_setattr = (setattrfunc)slot.sl_func; break; + case 69: ht->ht_type.tp_setattro = (setattrofunc)slot.sl_func; break; + case 70: ht->ht_type.tp_str = (reprfunc)slot.sl_func; break; + case 71: ht->ht_type.tp_traverse = (traverseproc)slot.sl_func; break; + case 72: ht->ht_type.tp_members = slot.sl_ptr; break; + case 73: ht->ht_type.tp_getset = slot.sl_ptr; break; + case 74: ht->ht_type.tp_free = (freefunc)slot.sl_func; break; + case 75: ht->as_number.nb_matrix_multiply = (binaryfunc)slot.sl_func; break; + case 76: ht->as_number.nb_inplace_matrix_multiply = (binaryfunc)slot.sl_func; break; + case 77: ht->as_async.am_await = (unaryfunc)slot.sl_func; break; + case 78: ht->as_async.am_aiter = (unaryfunc)slot.sl_func; break; + case 79: ht->as_async.am_anext = (unaryfunc)slot.sl_func; break; + case 80: ht->ht_type.tp_finalize = (destructor)slot.sl_func; break; + case 81: ht->as_async.am_send = (sendfunc)slot.sl_func; break; + case 82: ht->ht_type.tp_vectorcall = (vectorcallfunc)slot.sl_func; break; + case 88: ht->as_buffer.bf_getbuffer = (getbufferproc)slot.sl_func; break; + case 89: ht->as_buffer.bf_releasebuffer = (releasebufferproc)slot.sl_func; break; + case 90: ht->as_mapping.mp_ass_subscript = (objobjargproc)slot.sl_func; break; + case 91: ht->as_mapping.mp_length = (lenfunc)slot.sl_func; break; + } +} + +static inline _PySlot_DTYPE +_PySlot_get_dtype(uint16_t slot_id) +{ + switch (slot_id) { + case Py_slot_end: return _PySlot_DTYPE_VOID; + case Py_mp_subscript: return _PySlot_DTYPE_FUNC; + case Py_nb_absolute: return _PySlot_DTYPE_FUNC; + case Py_nb_add: return _PySlot_DTYPE_FUNC; + case Py_nb_and: return _PySlot_DTYPE_FUNC; + case Py_nb_bool: return _PySlot_DTYPE_FUNC; + case Py_nb_divmod: return _PySlot_DTYPE_FUNC; + case Py_nb_float: return _PySlot_DTYPE_FUNC; + case Py_nb_floor_divide: return _PySlot_DTYPE_FUNC; + case Py_nb_index: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_add: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_and: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_floor_divide: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_lshift: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_multiply: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_or: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_power: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_remainder: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_rshift: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_subtract: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_true_divide: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_xor: return _PySlot_DTYPE_FUNC; + case Py_nb_int: return _PySlot_DTYPE_FUNC; + case Py_nb_invert: return _PySlot_DTYPE_FUNC; + case Py_nb_lshift: return _PySlot_DTYPE_FUNC; + case Py_nb_multiply: return _PySlot_DTYPE_FUNC; + case Py_nb_negative: return _PySlot_DTYPE_FUNC; + case Py_nb_or: return _PySlot_DTYPE_FUNC; + case Py_nb_positive: return _PySlot_DTYPE_FUNC; + case Py_nb_power: return _PySlot_DTYPE_FUNC; + case Py_nb_remainder: return _PySlot_DTYPE_FUNC; + case Py_nb_rshift: return _PySlot_DTYPE_FUNC; + case Py_nb_subtract: return _PySlot_DTYPE_FUNC; + case Py_nb_true_divide: return _PySlot_DTYPE_FUNC; + case Py_nb_xor: return _PySlot_DTYPE_FUNC; + case Py_sq_ass_item: return _PySlot_DTYPE_FUNC; + case Py_sq_concat: return _PySlot_DTYPE_FUNC; + case Py_sq_contains: return _PySlot_DTYPE_FUNC; + case Py_sq_inplace_concat: return _PySlot_DTYPE_FUNC; + case Py_sq_inplace_repeat: return _PySlot_DTYPE_FUNC; + case Py_sq_item: return _PySlot_DTYPE_FUNC; + case Py_sq_length: return _PySlot_DTYPE_FUNC; + case Py_sq_repeat: return _PySlot_DTYPE_FUNC; + case Py_tp_alloc: return _PySlot_DTYPE_FUNC; + case Py_tp_base: return _PySlot_DTYPE_PTR; + case Py_tp_bases: return _PySlot_DTYPE_PTR; + case Py_tp_call: return _PySlot_DTYPE_FUNC; + case Py_tp_clear: return _PySlot_DTYPE_FUNC; + case Py_tp_dealloc: return _PySlot_DTYPE_FUNC; + case Py_tp_del: return _PySlot_DTYPE_FUNC; + case Py_tp_descr_get: return _PySlot_DTYPE_FUNC; + case Py_tp_descr_set: return _PySlot_DTYPE_FUNC; + case Py_tp_doc: return _PySlot_DTYPE_PTR; + case Py_tp_getattr: return _PySlot_DTYPE_FUNC; + case Py_tp_getattro: return _PySlot_DTYPE_FUNC; + case Py_tp_hash: return _PySlot_DTYPE_FUNC; + case Py_tp_init: return _PySlot_DTYPE_FUNC; + case Py_tp_is_gc: return _PySlot_DTYPE_FUNC; + case Py_tp_iter: return _PySlot_DTYPE_FUNC; + case Py_tp_iternext: return _PySlot_DTYPE_FUNC; + case Py_tp_methods: return _PySlot_DTYPE_PTR; + case Py_tp_new: return _PySlot_DTYPE_FUNC; + case Py_tp_repr: return _PySlot_DTYPE_FUNC; + case Py_tp_richcompare: return _PySlot_DTYPE_FUNC; + case Py_tp_setattr: return _PySlot_DTYPE_FUNC; + case Py_tp_setattro: return _PySlot_DTYPE_FUNC; + case Py_tp_str: return _PySlot_DTYPE_FUNC; + case Py_tp_traverse: return _PySlot_DTYPE_FUNC; + case Py_tp_members: return _PySlot_DTYPE_PTR; + case Py_tp_getset: return _PySlot_DTYPE_PTR; + case Py_tp_free: return _PySlot_DTYPE_FUNC; + case Py_nb_matrix_multiply: return _PySlot_DTYPE_FUNC; + case Py_nb_inplace_matrix_multiply: return _PySlot_DTYPE_FUNC; + case Py_am_await: return _PySlot_DTYPE_FUNC; + case Py_am_aiter: return _PySlot_DTYPE_FUNC; + case Py_am_anext: return _PySlot_DTYPE_FUNC; + case Py_tp_finalize: return _PySlot_DTYPE_FUNC; + case Py_am_send: return _PySlot_DTYPE_FUNC; + case Py_tp_vectorcall: return _PySlot_DTYPE_FUNC; + case Py_tp_token: return _PySlot_DTYPE_PTR; + case Py_mod_create: return _PySlot_DTYPE_FUNC; + case Py_mod_exec: return _PySlot_DTYPE_FUNC; + case Py_mod_multiple_interpreters: return _PySlot_DTYPE_UINT64; + case Py_mod_gil: return _PySlot_DTYPE_UINT64; + case Py_bf_getbuffer: return _PySlot_DTYPE_FUNC; + case Py_bf_releasebuffer: return _PySlot_DTYPE_FUNC; + case Py_mp_ass_subscript: return _PySlot_DTYPE_FUNC; + case Py_mp_length: return _PySlot_DTYPE_FUNC; + case Py_slot_subslots: return _PySlot_DTYPE_PTR; + case Py_tp_slots: return _PySlot_DTYPE_PTR; + case Py_mod_slots: return _PySlot_DTYPE_PTR; + case Py_tp_name: return _PySlot_DTYPE_PTR; + case Py_tp_basicsize: return _PySlot_DTYPE_SIZE; + case Py_tp_extra_basicsize: return _PySlot_DTYPE_SIZE; + case Py_tp_itemsize: return _PySlot_DTYPE_SIZE; + case Py_tp_flags: return _PySlot_DTYPE_UINT64; + case Py_mod_name: return _PySlot_DTYPE_PTR; + case Py_mod_doc: return _PySlot_DTYPE_PTR; + case Py_mod_state_size: return _PySlot_DTYPE_SIZE; + case Py_mod_methods: return _PySlot_DTYPE_PTR; + case Py_mod_state_traverse: return _PySlot_DTYPE_FUNC; + case Py_mod_state_clear: return _PySlot_DTYPE_FUNC; + case Py_mod_state_free: return _PySlot_DTYPE_FUNC; + case Py_tp_metaclass: return _PySlot_DTYPE_PTR; + case Py_tp_module: return _PySlot_DTYPE_PTR; + case Py_mod_abi: return _PySlot_DTYPE_PTR; + case Py_mod_token: return _PySlot_DTYPE_PTR; + default: return _PySlot_DTYPE_VOID; + } +} + +static inline _PySlot_PROBLEM_HANDLING +_PySlot_get_duplicate_handling(uint16_t slot_id) +{ + switch (slot_id) { + case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: + case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: + case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: + case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: + case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: + case 62: case 63: case 65: case 66: case 67: case 68: case 69: case 70: + case 71: case 74: case 75: case 76: case 77: case 78: case 79: case 80: + case 81: case 82: case 88: case 89: case 90: case 91: + return _PySlot_PROBLEM_DEPRECATED; + case 85: + return _PySlot_PROBLEM_ALLOW; + default: + return _PySlot_PROBLEM_REJECT; + } +} + +static inline _PySlot_PROBLEM_HANDLING +_PySlot_get_null_handling(uint16_t slot_id) +{ + switch (slot_id) { + case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: + case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: + case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: + case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: + case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: + case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: + case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: + case 78: case 79: case 80: case 81: case 82: case 84: case 85: case 88: + case 89: case 90: case 91: + return _PySlot_PROBLEM_DEPRECATED; + case 0: case 56: case 83: case 86: case 87: case 92: case 93: case 96: + case 97: case 98: case 99: case 102: + return _PySlot_PROBLEM_ALLOW; + default: + return _PySlot_PROBLEM_REJECT; + } +} + +static inline bool +_PySlot_is_name(uint16_t slot_id) +{ + switch (slot_id) { + case Py_tp_name: return true; + case Py_mod_name: return true; + default: return false; + } +} + +#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */ diff --git a/Makefile.pre.in b/Makefile.pre.in index 7218183124d792..c32b8af5da0d2a 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1433,6 +1433,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_signal.h \ $(srcdir)/Include/internal/pycore_sliceobject.h \ $(srcdir)/Include/internal/pycore_slots.h \ + $(srcdir)/Include/internal/pycore_slots_generated.h \ $(srcdir)/Include/internal/pycore_stats.h \ $(srcdir)/Include/internal/pycore_strhex.h \ $(srcdir)/Include/internal/pycore_stackref.h \ diff --git a/Modules/_testcapi/type.c b/Modules/_testcapi/type.c index 9bef58d1f83668..f566efa0ca15ae 100644 --- a/Modules/_testcapi/type.c +++ b/Modules/_testcapi/type.c @@ -110,9 +110,9 @@ test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored)) return NULL; } - void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1); + void *over_value = PyType_GetSlot(&PyLong_Type, Py_mod_name + 1); if (over_value != NULL) { - PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long"); + PyErr_SetString(PyExc_AssertionError, "mismatch: mod_name of long"); return NULL; } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 9eb02673363838..26dafa037f63dd 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -444,6 +444,7 @@ module_from_slots_and_spec( def_like = original_def; _PySlotIterator_InitLegacy(&it, def_like->m_slots, _PySlot_KIND_MOD); } + it.name = name; // Macro to copy a non-NULL, non-repeatable slot. #define COPY_NONNULL_SLOT(TYPE, SL_MEMBER, DEST) \ @@ -452,7 +453,7 @@ module_from_slots_and_spec( PyErr_Format( \ PyExc_SystemError, \ "module %s: %s must not be NULL", \ - name, it.info->name); \ + name, _PySlot_GetName(it.current.sl_id)); \ goto error; \ } \ DEST = (TYPE)(it.current.SL_MEMBER); \ @@ -470,7 +471,7 @@ module_from_slots_and_spec( PyExc_SystemError, \ "module %s: %s conflicts with " \ "PyModuleDef." #MEMBER, \ - name, it.info->name); \ + name, _PySlot_GetName(it.current.sl_id)); \ goto error; \ } \ } \ @@ -486,8 +487,8 @@ module_from_slots_and_spec( if (DEST) { \ PyErr_Format( \ PyExc_SystemError, \ - "module %s has multiple %s slots", \ - name, it.info->name); \ + "module %s has multiple %s slots", \ + name, _PySlot_GetName(it.current.sl_id)); \ goto error; \ } \ COPY_NONNULL_SLOT(TYPE, SL_MEMBER, DEST) \ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index cf02e8359a5a88..be74ab758980e5 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5241,7 +5241,6 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) const PyMemberDef *weaklistoffset_member = NULL; const PyMemberDef *dictoffset_member = NULL; const PyMemberDef *vectorcalloffset_member = NULL; - char *res_start; Py_ssize_t basicsize = 0; Py_ssize_t extra_basicsize = 0; Py_ssize_t itemsize = 0; @@ -5570,7 +5569,6 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) if (res == NULL) { goto finally; } - res_start = (char*)res; type = &res->ht_type; /* The flags must be initialized early, before the GC traverses us */ @@ -5651,19 +5649,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) default: { /* Copy other slots directly */ - short slot_offset = it.info->type_info.slot_offset; - short subslot_offset = it.info->type_info.subslot_offset; - if (slot_offset == 0 && subslot_offset == 0) { - /* slot should have been processed above */ - } - else if (subslot_offset == -1) { - /* Set a slot in the main PyTypeObject */ - *(void**)((char*)res_start + slot_offset) = it.current.sl_func; - } - else { - void *procs = *(void**)((char*)res_start + slot_offset); - *(void**)((char*)procs + subslot_offset) = it.current.sl_func; - } + _PySlot_heaptype_apply_field_slot(res, it.current); } break; } @@ -5842,45 +5828,17 @@ PyType_GetModuleName(PyTypeObject *type) } void * -PyType_GetSlot(PyTypeObject *type, int slot) +PyType_GetSlot(PyTypeObject *type, int slot_in) { - void *parent_slot; - if (slot <= 0 || slot >= _Py_slot_COUNT) { - PyErr_BadInternalCall(); - return NULL; - } - _PySlot_Info *slot_info = &_PySlot_InfoTable[slot]; - if (slot_info->kind != _PySlot_KIND_TYPE) { - if (slot_info->kind != _PySlot_KIND_COMPAT) { - PyErr_BadInternalCall(); - return NULL; - } - slot = slot_info->compat_info.type_id; - slot_info = &_PySlot_InfoTable[slot]; - assert(slot_info->kind == _PySlot_KIND_TYPE); - } - short slot_offset = slot_info->type_info.slot_offset; - short subslot_offset = slot_info->type_info.subslot_offset; - if ((slot_offset == 0) && (subslot_offset == 0)) { - PyErr_BadInternalCall(); + uint16_t slot = _PySlot_resolve_type_slot(slot_in); + if (slot == Py_slot_invalid) { return NULL; } - - if (slot_offset >= (int)sizeof(PyTypeObject)) { - if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { - return NULL; - } - } - - parent_slot = *(void**)((char*)type + slot_offset); - if (parent_slot == NULL) { + void **ptr = _PySlot_type_ptr(type, slot); + if (ptr == NULL) { return NULL; } - /* Return slot directly if we have no sub slot. */ - if (subslot_offset == -1) { - return parent_slot; - } - return *(void**)((char*)parent_slot + subslot_offset); + return *ptr; } PyObject * diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 52bb9e1b691591..c1207665691479 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -316,6 +316,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index aeab93e7ed3a64..a6dad119e129d0 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -852,6 +852,9 @@ Include\internal + + Include\internal + Include\internal diff --git a/Python/slots.c b/Python/slots.c index 54c44ff768e030..4320a8e4c23e39 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -3,7 +3,7 @@ #include "Python.h" -#include "pycore_slots.h" // _PySlot_Info +#include "pycore_slots.h" #include @@ -17,8 +17,6 @@ #define MSG(...) #endif -static int validate_current_slot(_PySlotIterator *it); - static char* kind_name(_PySlot_KIND kind) { @@ -114,6 +112,8 @@ advance(_PySlotIterator *it) } } +static int handle_first_run(_PySlotIterator *it); + bool _PySlotIterator_Next(_PySlotIterator *it) { @@ -138,7 +138,6 @@ _PySlotIterator_Next(_PySlotIterator *it) continue; } - /* Convert legacy structure */ switch (it->state->slot_struct_kind) { case _PySlot_KIND_SLOT: { MSG("copying PySlot structure"); @@ -147,14 +146,14 @@ _PySlotIterator_Next(_PySlotIterator *it) case _PySlot_KIND_TYPE: { MSG("converting PyType_Slot structure"); memset(&it->current, 0, sizeof(it->current)); - it->current.sl_id = it->state->tp_slot->slot; + it->current.sl_id = (uint16_t)it->state->tp_slot->slot; it->current.sl_flags = PySlot_INTPTR; it->current.sl_ptr = (void*)it->state->tp_slot->pfunc; } break; case _PySlot_KIND_MOD: { MSG("converting PyModuleDef_Slot structure"); memset(&it->current, 0, sizeof(it->current)); - it->current.sl_id = it->state->mod_slot->slot; + it->current.sl_id = (uint16_t)it->state->mod_slot->slot; it->current.sl_flags = PySlot_INTPTR; it->current.sl_ptr = (void*)it->state->mod_slot->value; } break; @@ -167,11 +166,8 @@ _PySlotIterator_Next(_PySlotIterator *it) PySlot *const result = &it->current; uint16_t flags = result->sl_flags; - MSG("slot %d, flags 0x%x, from %p", - (int)result->sl_id, (unsigned)flags, it->state->slot); - if (it->state->ignoring_fallbacks) { - if (!(flags & PySlot_HAS_FALLBACK)) { + if (!(it->state->slot->sl_flags & PySlot_HAS_FALLBACK)) { MSG("stopping to ignore fallbacks"); it->state->ignoring_fallbacks = false; } @@ -179,21 +175,37 @@ _PySlotIterator_Next(_PySlotIterator *it) advance(it); continue; } - if (result->sl_id >= _Py_slot_COUNT) { + + MSG("slot %d, flags 0x%x, from %p", + (int)result->sl_id, (unsigned)flags, it->state->slot); + + uint16_t orig_id = result->sl_id; + switch (it->kind) { + case _PySlot_KIND_TYPE: + result->sl_id = _PySlot_resolve_type_slot(result->sl_id); + break; + case _PySlot_KIND_MOD: + result->sl_id = _PySlot_resolve_mod_slot(result->sl_id); + break; + default: + Py_UNREACHABLE(); + } + MSG("resolved to slot %s (%d)", + (int)result->sl_id, _PySlot_GetName(result->sl_id)); + + if (result->sl_id == Py_slot_invalid) { if (flags & (PySlot_OPTIONAL | PySlot_HAS_FALLBACK)) { - MSG("skipped (unknown slot)"); + MSG("skipped (unknown/invalid slot)"); advance(it); continue; } - MSG("error (unknown slot)"); - PyErr_Format(PyExc_SystemError, - "unknown slot ID %u", (unsigned int)result->sl_id); + MSG("error (unknown/invalid slot)"); + _PySlot_err_bad_slot(kind_name(it->kind), orig_id); goto error; } if (result->sl_id == Py_slot_end) { - flags &= ~PySlot_INTPTR; MSG("sentinel slot, flags %x", (unsigned)flags); - if (flags == PySlot_OPTIONAL) { + if (flags & PySlot_OPTIONAL) { MSG("skipped (optional sentinel)"); advance(it); continue; @@ -209,37 +221,11 @@ _PySlotIterator_Next(_PySlotIterator *it) it->state->slot = NULL; continue; } - it->info = &_PySlot_InfoTable[result->sl_id]; - MSG("slot %d: %s", (int)result->sl_id, it->info->name); - - if (it->is_first_run && it->info->is_name) { - MSG("setting name: %s", (char*)result->sl_ptr); - assert(it->info->dtype == _PySlot_TYPE_PTR); - it->name = result->sl_ptr; - } - // Resolve a legacy ambiguous slot number. - // Save the original slot info for error messages. - uint16_t orig_id = result->sl_id; - _PySlot_Info *orig_info = &_PySlot_InfoTable[result->sl_id]; - if (it->info->kind == _PySlot_KIND_COMPAT) { - MSG("resolving compat slot"); - switch (it->kind) { - case _PySlot_KIND_TYPE: { - result->sl_id = it->info->compat_info.type_id; - } break; - case _PySlot_KIND_MOD: { - result->sl_id = it->info->compat_info.mod_id; - } break; - default: { - Py_UNREACHABLE(); - } break; - } - it->info = &_PySlot_InfoTable[result->sl_id]; - MSG("slot %d: %s", (int)result->sl_id, it->info->name); - } - - if (it->info->is_subslots) { + if (result->sl_id == Py_slot_subslots + || result->sl_id == Py_tp_slots + || result->sl_id == Py_mod_slots + ) { if (result->sl_ptr == NULL) { MSG("NULL subslots; skipping"); advance(it); @@ -251,31 +237,39 @@ _PySlotIterator_Next(_PySlotIterator *it) MSG("error (too much nesting)"); PyErr_Format(PyExc_SystemError, "%s (slot %d): too many levels of nested slots", - orig_info->name, orig_id); + _PySlot_GetName(result->sl_id), orig_id); goto error; } it->state = &it->states[it->recursion_level]; memset(it->state, 0, sizeof(_PySlotIterator_state)); it->state->slot = result->sl_ptr; - it->state->slot_struct_kind = it->info->kind; + switch (result->sl_id) { + case Py_slot_subslots: + it->state->slot_struct_kind = _PySlot_KIND_SLOT; break; + case Py_tp_slots: + it->state->slot_struct_kind = _PySlot_KIND_TYPE; break; + case Py_mod_slots: + it->state->slot_struct_kind = _PySlot_KIND_MOD; break; + } continue; } if (flags & PySlot_INTPTR) { MSG("casting from intptr"); - switch (it->info->dtype) { - case _PySlot_TYPE_SIZE: { + /* this should compile to nothing on common architectures */ + switch (_PySlot_get_dtype(result->sl_id)) { + case _PySlot_DTYPE_SIZE: { result->sl_size = (Py_ssize_t)(intptr_t)result->sl_ptr; } break; - case _PySlot_TYPE_INT64: { + case _PySlot_DTYPE_INT64: { result->sl_int64 = (int64_t)(intptr_t)result->sl_ptr; } break; - case _PySlot_TYPE_UINT64: { + case _PySlot_DTYPE_UINT64: { result->sl_uint64 = (uint64_t)(intptr_t)result->sl_ptr; } break; - case _PySlot_TYPE_PTR: - case _PySlot_TYPE_FUNC: - case _PySlot_TYPE_VOID: + case _PySlot_DTYPE_PTR: + case _PySlot_DTYPE_FUNC: + case _PySlot_DTYPE_VOID: break; } } @@ -286,31 +280,31 @@ _PySlotIterator_Next(_PySlotIterator *it) } advance(it); - switch (it->info->dtype) { - case _PySlot_TYPE_VOID: - case _PySlot_TYPE_PTR: + switch (_PySlot_get_dtype(result->sl_id)) { + case _PySlot_DTYPE_VOID: + case _PySlot_DTYPE_PTR: MSG("result: %d (%s): %p", - (int)result->sl_id, it->info->name, + (int)result->sl_id, _PySlot_GetName(result->sl_id), (void*)result->sl_ptr); break; - case _PySlot_TYPE_FUNC: + case _PySlot_DTYPE_FUNC: MSG("result: %d (%s): %p", - (int)result->sl_id, it->info->name, + (int)result->sl_id, _PySlot_GetName(result->sl_id), (void*)result->sl_func); break; - case _PySlot_TYPE_SIZE: + case _PySlot_DTYPE_SIZE: MSG("result: %d (%s): %zd", - (int)result->sl_id, it->info->name, + (int)result->sl_id, _PySlot_GetName(result->sl_id), (Py_ssize_t)result->sl_size); break; - case _PySlot_TYPE_INT64: + case _PySlot_DTYPE_INT64: MSG("result: %d (%s): %ld", - (int)result->sl_id, it->info->name, + (int)result->sl_id, _PySlot_GetName(result->sl_id), (long)result->sl_int64); break; - case _PySlot_TYPE_UINT64: + case _PySlot_DTYPE_UINT64: MSG("result: %d (%s): %lu (0x%lx)", - (int)result->sl_id, it->info->name, + (int)result->sl_id, _PySlot_GetName(result->sl_id), (unsigned long)result->sl_int64, (unsigned long)result->sl_int64); break; @@ -318,7 +312,7 @@ _PySlotIterator_Next(_PySlotIterator *it) assert (result->sl_id > 0); assert (result->sl_id <= _Py_slot_COUNT); assert (result->sl_id <= INT_MAX); - if (it->is_first_run && validate_current_slot(it) < 0) { + if (it->is_first_run && handle_first_run(it) < 0) { goto error; } return result->sl_id != Py_slot_end; @@ -330,42 +324,39 @@ _PySlotIterator_Next(_PySlotIterator *it) return true; } +/* Validate current slot, and do bookkeeping */ static int -validate_current_slot(_PySlotIterator *it) +handle_first_run(_PySlotIterator *it) { - const _PySlot_Info *info = it->info; int id = it->current.sl_id; - if (it->info->kind != it->kind) { - MSG("error (bad slot kind)"); - PyErr_Format(PyExc_SystemError, - "%s (slot %d) is not compatible with %ss", - info->name, - id, - kind_name(it->kind)); - return -1; + if (it->name == NULL && _PySlot_is_name(id)) { + MSG("setting name: %s", (char*)it->current.sl_ptr); + assert(_PySlot_get_dtype(it->current.sl_id) == _PySlot_DTYPE_PTR); + it->name = it->current.sl_ptr; } - if (it->info->null_handling != _PySlot_PROBLEM_ALLOW) { + _PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id); + if (null_handling != _PySlot_PROBLEM_ALLOW) { bool is_null = false; - switch (it->info->dtype) { - case _PySlot_TYPE_PTR: { + switch (_PySlot_get_dtype(id)) { + case _PySlot_DTYPE_PTR: { is_null = it->current.sl_ptr == NULL; } break; - case _PySlot_TYPE_FUNC: { + case _PySlot_DTYPE_FUNC: { is_null = it->current.sl_func == NULL; } break; default: { - Py_UNREACHABLE(); + //Py_UNREACHABLE(); } break; } if (is_null) { MSG("slot is NULL but shouldn't"); - if (it->info->null_handling == _PySlot_PROBLEM_REJECT) { + if (null_handling == _PySlot_PROBLEM_REJECT) { MSG("error (NULL rejected)"); PyErr_Format(PyExc_SystemError, - "NULL not allowed for slot Py_%s", - it->info->name); + "NULL not allowed for slot %s", + _PySlot_GetName(id)); return -1; } MSG("deprecated NULL"); @@ -373,17 +364,18 @@ validate_current_slot(_PySlotIterator *it) PyExc_DeprecationWarning, 1, "NULL value in slot %s is deprecated", - it->info->name) < 0) + _PySlot_GetName(id)) < 0) { return -1; } } } - if (info->duplicate_handling != _PySlot_PROBLEM_ALLOW) { + _PySlot_PROBLEM_HANDLING duplicate_handling = _PySlot_get_duplicate_handling(id); + if (duplicate_handling != _PySlot_PROBLEM_ALLOW) { if (_PySlotIterator_SawSlot(it, id)) { MSG("slot was seen before but shouldn't be duplicated"); - if (info->duplicate_handling == _PySlot_PROBLEM_REJECT) { + if (duplicate_handling == _PySlot_PROBLEM_REJECT) { MSG("error (duplicate rejected)"); PyErr_Format( PyExc_SystemError, @@ -391,7 +383,7 @@ validate_current_slot(_PySlotIterator *it) kind_name(it->kind), it->name ? " " : "", it->name ? it->name : "", - info->name, + _PySlot_GetName(id), (int)it->current.sl_id); return -1; } @@ -403,7 +395,7 @@ validate_current_slot(_PySlotIterator *it) kind_name(it->kind), it->name ? " " : "", it->name ? it->name : "", - info->name, + _PySlot_GetName(id), (int)it->current.sl_id) < 0) { return -1; } diff --git a/Python/slots.toml b/Python/slots.toml new file mode 100644 index 00000000000000..98f14d7f0018b0 --- /dev/null +++ b/Python/slots.toml @@ -0,0 +1,806 @@ +# This file lists all PySlot values + +[0] +name = 'Py_slot_end' +kind = 'slot' +dtype = 'void' + +[1] +kind = 'compat' +equivalents = {type='Py_bf_getbuffer', mod='Py_mod_create'} + +[2] +kind = 'compat' +equivalents = {type='Py_bf_releasebuffer', mod='Py_mod_exec'} + +[3] +kind = 'compat' +equivalents = {type='Py_mp_ass_subscript', mod='Py_mod_multiple_interpreters'} + +[4] +kind = 'compat' +equivalents = {type='Py_mp_length', mod='Py_mod_gil'} + +[5] +name = 'Py_mp_subscript' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[6] +name = 'Py_nb_absolute' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[7] +name = 'Py_nb_add' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[8] +name = 'Py_nb_and' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[9] +name = 'Py_nb_bool' +kind = 'type' +is_type_field = true +functype = 'inquiry' +duplicates = 'deprecated' +nulls = 'deprecated' + +[10] +name = 'Py_nb_divmod' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[11] +name = 'Py_nb_float' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[12] +name = 'Py_nb_floor_divide' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[13] +name = 'Py_nb_index' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[14] +name = 'Py_nb_inplace_add' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[15] +name = 'Py_nb_inplace_and' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[16] +name = 'Py_nb_inplace_floor_divide' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[17] +name = 'Py_nb_inplace_lshift' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[18] +name = 'Py_nb_inplace_multiply' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[19] +name = 'Py_nb_inplace_or' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[20] +name = 'Py_nb_inplace_power' +kind = 'type' +is_type_field = true +functype = 'ternaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[21] +name = 'Py_nb_inplace_remainder' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[22] +name = 'Py_nb_inplace_rshift' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[23] +name = 'Py_nb_inplace_subtract' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[24] +name = 'Py_nb_inplace_true_divide' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[25] +name = 'Py_nb_inplace_xor' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[26] +name = 'Py_nb_int' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[27] +name = 'Py_nb_invert' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[28] +name = 'Py_nb_lshift' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[29] +name = 'Py_nb_multiply' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[30] +name = 'Py_nb_negative' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[31] +name = 'Py_nb_or' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[32] +name = 'Py_nb_positive' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[33] +name = 'Py_nb_power' +kind = 'type' +is_type_field = true +functype = 'ternaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[34] +name = 'Py_nb_remainder' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[35] +name = 'Py_nb_rshift' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[36] +name = 'Py_nb_subtract' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[37] +name = 'Py_nb_true_divide' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[38] +name = 'Py_nb_xor' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[39] +name = 'Py_sq_ass_item' +kind = 'type' +is_type_field = true +functype = 'ssizeobjargproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[40] +name = 'Py_sq_concat' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[41] +name = 'Py_sq_contains' +kind = 'type' +is_type_field = true +functype = 'objobjproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[42] +name = 'Py_sq_inplace_concat' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[43] +name = 'Py_sq_inplace_repeat' +kind = 'type' +is_type_field = true +functype = 'ssizeargfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[44] +name = 'Py_sq_item' +kind = 'type' +is_type_field = true +functype = 'ssizeargfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[45] +name = 'Py_sq_length' +kind = 'type' +is_type_field = true +functype = 'lenfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[46] +name = 'Py_sq_repeat' +kind = 'type' +is_type_field = true +functype = 'ssizeargfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[47] +name = 'Py_tp_alloc' +kind = 'type' +is_type_field = true +functype = 'allocfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[48] +name = 'Py_tp_base' +kind = 'type' +is_type_field = true +dtype = 'ptr' +duplicates = 'deprecated' +nulls = 'deprecated' + +[49] +name = 'Py_tp_bases' +kind = 'type' +is_type_field = true +dtype = 'ptr' +duplicates = 'deprecated' +nulls = 'deprecated' + +[50] +name = 'Py_tp_call' +kind = 'type' +is_type_field = true +functype = 'ternaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[51] +name = 'Py_tp_clear' +kind = 'type' +is_type_field = true +functype = 'inquiry' +duplicates = 'deprecated' +nulls = 'deprecated' + +[52] +name = 'Py_tp_dealloc' +kind = 'type' +is_type_field = true +functype = 'destructor' +duplicates = 'deprecated' +nulls = 'deprecated' + +[53] +name = 'Py_tp_del' +kind = 'type' +is_type_field = true +functype = 'destructor' +duplicates = 'deprecated' +nulls = 'deprecated' + +[54] +name = 'Py_tp_descr_get' +kind = 'type' +is_type_field = true +functype = 'descrgetfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[55] +name = 'Py_tp_descr_set' +kind = 'type' +is_type_field = true +functype = 'descrsetfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[56] +name = 'Py_tp_doc' +kind = 'type' +is_type_field = true +dtype = 'ptr' +nulls = 'allow' + +[57] +name = 'Py_tp_getattr' +kind = 'type' +is_type_field = true +functype = 'getattrfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[58] +name = 'Py_tp_getattro' +kind = 'type' +is_type_field = true +functype = 'getattrofunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[59] +name = 'Py_tp_hash' +kind = 'type' +is_type_field = true +functype = 'hashfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[60] +name = 'Py_tp_init' +kind = 'type' +is_type_field = true +functype = 'initproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[61] +name = 'Py_tp_is_gc' +kind = 'type' +is_type_field = true +functype = 'inquiry' +duplicates = 'deprecated' +nulls = 'deprecated' + +[62] +name = 'Py_tp_iter' +kind = 'type' +is_type_field = true +functype = 'getiterfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[63] +name = 'Py_tp_iternext' +kind = 'type' +is_type_field = true +functype = 'iternextfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[64] +name = 'Py_tp_methods' +kind = 'type' +is_type_field = true +dtype = 'ptr' +nulls = 'deprecated' + +[65] +name = 'Py_tp_new' +kind = 'type' +is_type_field = true +functype = 'newfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[66] +name = 'Py_tp_repr' +kind = 'type' +is_type_field = true +functype = 'reprfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[67] +name = 'Py_tp_richcompare' +kind = 'type' +is_type_field = true +functype = 'richcmpfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[68] +name = 'Py_tp_setattr' +kind = 'type' +is_type_field = true +functype = 'setattrfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[69] +name = 'Py_tp_setattro' +kind = 'type' +is_type_field = true +functype = 'setattrofunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[70] +name = 'Py_tp_str' +kind = 'type' +is_type_field = true +functype = 'reprfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[71] +name = 'Py_tp_traverse' +kind = 'type' +is_type_field = true +functype = 'traverseproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[72] +name = 'Py_tp_members' +kind = 'type' +is_type_field = true +dtype = 'ptr' +nulls = 'deprecated' + +[73] +name = 'Py_tp_getset' +kind = 'type' +is_type_field = true +dtype = 'ptr' +nulls = 'deprecated' + +[74] +name = 'Py_tp_free' +kind = 'type' +is_type_field = true +functype = 'freefunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[75] +name = 'Py_nb_matrix_multiply' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[76] +name = 'Py_nb_inplace_matrix_multiply' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[77] +name = 'Py_am_await' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[78] +name = 'Py_am_aiter' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[79] +name = 'Py_am_anext' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[80] +name = 'Py_tp_finalize' +kind = 'type' +is_type_field = true +functype = 'destructor' +duplicates = 'deprecated' +nulls = 'deprecated' + +[81] +name = 'Py_am_send' +kind = 'type' +is_type_field = true +functype = 'sendfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[82] +name = 'Py_tp_vectorcall' +kind = 'type' +is_type_field = true +functype = 'vectorcallfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[83] +name = 'Py_tp_token' +kind = 'type' +is_type_field = true +dtype = 'ptr' +field = 'ht_token' +nulls = 'allow' + +[84] +name = 'Py_mod_create' +kind = 'mod' +dtype = 'func' +nulls = 'deprecated' + +[85] +name = 'Py_mod_exec' +kind = 'mod' +dtype = 'func' +duplicates = 'allow' +nulls = 'deprecated' + +[86] +name = 'Py_mod_multiple_interpreters' +kind = 'mod' +dtype = 'uint64' + +[87] +name = 'Py_mod_gil' +kind = 'mod' +dtype = 'uint64' + +[88] +name = 'Py_bf_getbuffer' +kind = 'type' +is_type_field = true +functype = 'getbufferproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[89] +name = 'Py_bf_releasebuffer' +kind = 'type' +is_type_field = true +functype = 'releasebufferproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[90] +name = 'Py_mp_ass_subscript' +kind = 'type' +is_type_field = true +functype = 'objobjargproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[91] +name = 'Py_mp_length' +kind = 'type' +is_type_field = true +functype = 'lenfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[92] +name = 'Py_slot_subslots' +kind = 'slot' +dtype = 'ptr' +nulls = 'allow' + +[93] +name = 'Py_tp_slots' +kind = 'type' +dtype = 'ptr' +nulls = 'allow' + +[94] +name = 'Py_mod_slots' +kind = 'mod' +dtype = 'ptr' +n.ulls = 'allow' + +[95] +name = 'Py_tp_name' +is_name = true +kind = 'type' +dtype = 'ptr' + +[96] +name = 'Py_tp_basicsize' +kind = 'type' +dtype = 'size' + +[97] +name = 'Py_tp_extra_basicsize' +kind = 'type' +dtype = 'size' + +[98] +name = 'Py_tp_itemsize' +kind = 'type' +dtype = 'size' + +[99] +name = 'Py_tp_flags' +kind = 'type' +dtype = 'uint64' + +[100] +name = 'Py_mod_name' +is_name = true +kind = 'mod' +dtype = 'ptr' + +[101] +name = 'Py_mod_doc' +kind = 'mod' +dtype = 'ptr' + +[102] +name = 'Py_mod_state_size' +kind = 'mod' +dtype = 'size' + +[103] +name = 'Py_mod_methods' +kind = 'mod' +dtype = 'ptr' + +[104] +name = 'Py_mod_state_traverse' +kind = 'mod' +dtype = 'func' + +[105] +name = 'Py_mod_state_clear' +kind = 'mod' +dtype = 'func' + +[106] +name = 'Py_mod_state_free' +kind = 'mod' +dtype = 'func' + +[107] +name = 'Py_tp_metaclass' +kind = 'type' +dtype = 'ptr' + +[108] +name = 'Py_tp_module' +kind = 'type' +dtype = 'ptr' + +[109] +name = 'Py_mod_abi' +kind = 'mod' +dtype = 'ptr' + +[110] +name = 'Py_mod_token' +kind = 'mod' +dtype = 'ptr' + diff --git a/Python/slots_generated.c b/Python/slots_generated.c index 76222986e43082..f1c369f3a22bba 100644 --- a/Python/slots_generated.c +++ b/Python/slots_generated.c @@ -1,952 +1,40 @@ /* Generated by Tools/build/generate_slots.py */ #include "Python.h" -#include "pycore_slots.h" // _PySlot_Info -#include // offsetof -#include // true +#include "pycore_slots.h" // _PySlot_names -_PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = { - [0] = { - .name = "Py_slot_end", - .dtype = _PySlot_TYPE_VOID, - .kind = _PySlot_KIND_SLOT, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [1] = { - .name = "Py_bf_getbuffer/mod_create", - .dtype = _PySlot_TYPE_VOID, - .kind = _PySlot_KIND_COMPAT, - .compat_info.mod_id = 84, - .compat_info.type_id = 88, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [2] = { - .name = "Py_bf_releasebuffer/mod_exec", - .dtype = _PySlot_TYPE_VOID, - .kind = _PySlot_KIND_COMPAT, - .compat_info.mod_id = 85, - .compat_info.type_id = 89, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [3] = { - .name = "Py_mp_ass_subscript/mod_multiple_interpreters", - .dtype = _PySlot_TYPE_VOID, - .kind = _PySlot_KIND_COMPAT, - .compat_info.mod_id = 86, - .compat_info.type_id = 90, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [4] = { - .name = "Py_mp_length/mod_gil", - .dtype = _PySlot_TYPE_VOID, - .kind = _PySlot_KIND_COMPAT, - .compat_info.mod_id = 87, - .compat_info.type_id = 91, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [5] = { - .name = "Py_mp_subscript", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), - .type_info.subslot_offset = offsetof(PyMappingMethods, mp_subscript), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [6] = { - .name = "Py_nb_absolute", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_absolute), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [7] = { - .name = "Py_nb_add", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_add), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [8] = { - .name = "Py_nb_and", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_and), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [9] = { - .name = "Py_nb_bool", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_bool), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [10] = { - .name = "Py_nb_divmod", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_divmod), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [11] = { - .name = "Py_nb_float", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_float), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [12] = { - .name = "Py_nb_floor_divide", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_floor_divide), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [13] = { - .name = "Py_nb_index", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_index), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [14] = { - .name = "Py_nb_inplace_add", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_add), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [15] = { - .name = "Py_nb_inplace_and", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_and), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [16] = { - .name = "Py_nb_inplace_floor_divide", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_floor_divide), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [17] = { - .name = "Py_nb_inplace_lshift", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_lshift), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [18] = { - .name = "Py_nb_inplace_multiply", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_multiply), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [19] = { - .name = "Py_nb_inplace_or", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_or), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [20] = { - .name = "Py_nb_inplace_power", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_power), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [21] = { - .name = "Py_nb_inplace_remainder", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_remainder), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [22] = { - .name = "Py_nb_inplace_rshift", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_rshift), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [23] = { - .name = "Py_nb_inplace_subtract", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_subtract), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [24] = { - .name = "Py_nb_inplace_true_divide", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_true_divide), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [25] = { - .name = "Py_nb_inplace_xor", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_xor), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [26] = { - .name = "Py_nb_int", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_int), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [27] = { - .name = "Py_nb_invert", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_invert), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [28] = { - .name = "Py_nb_lshift", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_lshift), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [29] = { - .name = "Py_nb_multiply", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_multiply), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [30] = { - .name = "Py_nb_negative", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_negative), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [31] = { - .name = "Py_nb_or", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_or), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [32] = { - .name = "Py_nb_positive", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_positive), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [33] = { - .name = "Py_nb_power", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_power), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [34] = { - .name = "Py_nb_remainder", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_remainder), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [35] = { - .name = "Py_nb_rshift", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_rshift), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [36] = { - .name = "Py_nb_subtract", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_subtract), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [37] = { - .name = "Py_nb_true_divide", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_true_divide), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [38] = { - .name = "Py_nb_xor", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_xor), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [39] = { - .name = "Py_sq_ass_item", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), - .type_info.subslot_offset = offsetof(PySequenceMethods, sq_ass_item), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [40] = { - .name = "Py_sq_concat", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), - .type_info.subslot_offset = offsetof(PySequenceMethods, sq_concat), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [41] = { - .name = "Py_sq_contains", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), - .type_info.subslot_offset = offsetof(PySequenceMethods, sq_contains), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [42] = { - .name = "Py_sq_inplace_concat", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), - .type_info.subslot_offset = offsetof(PySequenceMethods, sq_inplace_concat), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [43] = { - .name = "Py_sq_inplace_repeat", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), - .type_info.subslot_offset = offsetof(PySequenceMethods, sq_inplace_repeat), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [44] = { - .name = "Py_sq_item", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), - .type_info.subslot_offset = offsetof(PySequenceMethods, sq_item), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [45] = { - .name = "Py_sq_length", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), - .type_info.subslot_offset = offsetof(PySequenceMethods, sq_length), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [46] = { - .name = "Py_sq_repeat", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_sequence), - .type_info.subslot_offset = offsetof(PySequenceMethods, sq_repeat), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [47] = { - .name = "Py_tp_alloc", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_alloc), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [48] = { - .name = "Py_tp_base", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_base), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [49] = { - .name = "Py_tp_bases", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_bases), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [50] = { - .name = "Py_tp_call", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_call), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [51] = { - .name = "Py_tp_clear", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_clear), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [52] = { - .name = "Py_tp_dealloc", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_dealloc), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [53] = { - .name = "Py_tp_del", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_del), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [54] = { - .name = "Py_tp_descr_get", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_descr_get), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [55] = { - .name = "Py_tp_descr_set", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_descr_set), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [56] = { - .name = "Py_tp_doc", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_doc), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_ALLOW, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [57] = { - .name = "Py_tp_getattr", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_getattr), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [58] = { - .name = "Py_tp_getattro", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_getattro), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [59] = { - .name = "Py_tp_hash", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_hash), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [60] = { - .name = "Py_tp_init", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_init), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [61] = { - .name = "Py_tp_is_gc", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_is_gc), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [62] = { - .name = "Py_tp_iter", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_iter), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [63] = { - .name = "Py_tp_iternext", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_iternext), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [64] = { - .name = "Py_tp_methods", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_methods), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [65] = { - .name = "Py_tp_new", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_new), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [66] = { - .name = "Py_tp_repr", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_repr), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [67] = { - .name = "Py_tp_richcompare", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_richcompare), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [68] = { - .name = "Py_tp_setattr", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_setattr), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [69] = { - .name = "Py_tp_setattro", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_setattro), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [70] = { - .name = "Py_tp_str", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_str), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [71] = { - .name = "Py_tp_traverse", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_traverse), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [72] = { - .name = "Py_tp_members", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_members), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [73] = { - .name = "Py_tp_getset", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_getset), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [74] = { - .name = "Py_tp_free", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_free), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [75] = { - .name = "Py_nb_matrix_multiply", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_matrix_multiply), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [76] = { - .name = "Py_nb_inplace_matrix_multiply", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_number), - .type_info.subslot_offset = offsetof(PyNumberMethods, nb_inplace_matrix_multiply), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [77] = { - .name = "Py_am_await", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), - .type_info.subslot_offset = offsetof(PyAsyncMethods, am_await), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [78] = { - .name = "Py_am_aiter", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), - .type_info.subslot_offset = offsetof(PyAsyncMethods, am_aiter), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [79] = { - .name = "Py_am_anext", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), - .type_info.subslot_offset = offsetof(PyAsyncMethods, am_anext), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [80] = { - .name = "Py_tp_finalize", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_finalize), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [81] = { - .name = "Py_am_send", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_async), - .type_info.subslot_offset = offsetof(PyAsyncMethods, am_send), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [82] = { - .name = "Py_tp_vectorcall", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_vectorcall), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [83] = { - .name = "Py_tp_token", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyHeapTypeObject, ht_token), - .type_info.subslot_offset = -1, - .null_handling = _PySlot_PROBLEM_ALLOW, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [84] = { - .name = "Py_mod_create", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [85] = { - .name = "Py_mod_exec", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [86] = { - .name = "Py_mod_multiple_interpreters", - .dtype = _PySlot_TYPE_UINT64, - .kind = _PySlot_KIND_MOD, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [87] = { - .name = "Py_mod_gil", - .dtype = _PySlot_TYPE_UINT64, - .kind = _PySlot_KIND_MOD, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [88] = { - .name = "Py_bf_getbuffer", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_buffer), - .type_info.subslot_offset = offsetof(PyBufferProcs, bf_getbuffer), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [89] = { - .name = "Py_bf_releasebuffer", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_buffer), - .type_info.subslot_offset = offsetof(PyBufferProcs, bf_releasebuffer), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [90] = { - .name = "Py_mp_ass_subscript", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), - .type_info.subslot_offset = offsetof(PyMappingMethods, mp_ass_subscript), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [91] = { - .name = "Py_mp_length", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_TYPE, - .type_info.slot_offset = offsetof(PyTypeObject, tp_as_mapping), - .type_info.subslot_offset = offsetof(PyMappingMethods, mp_length), - .null_handling = _PySlot_PROBLEM_DEPRECATED, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [92] = { - .name = "Py_slot_subslots", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_SLOT, - .is_subslots = true, - .null_handling = _PySlot_PROBLEM_ALLOW, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [93] = { - .name = "Py_tp_slots", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .is_subslots = true, - .null_handling = _PySlot_PROBLEM_ALLOW, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [94] = { - .name = "Py_mod_slots", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_MOD, - .is_subslots = true, - .null_handling = _PySlot_PROBLEM_ALLOW, - .duplicate_handling = _PySlot_PROBLEM_DEPRECATED, - }, - [95] = { - .name = "Py_tp_name", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - .is_name = true, - }, - [96] = { - .name = "Py_tp_basicsize", - .dtype = _PySlot_TYPE_SIZE, - .kind = _PySlot_KIND_TYPE, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [97] = { - .name = "Py_tp_extra_basicsize", - .dtype = _PySlot_TYPE_SIZE, - .kind = _PySlot_KIND_TYPE, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [98] = { - .name = "Py_tp_itemsize", - .dtype = _PySlot_TYPE_SIZE, - .kind = _PySlot_KIND_TYPE, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [99] = { - .name = "Py_tp_flags", - .dtype = _PySlot_TYPE_UINT64, - .kind = _PySlot_KIND_TYPE, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [100] = { - .name = "Py_mod_name", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - .is_name = true, - }, - [101] = { - .name = "Py_mod_doc", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [102] = { - .name = "Py_mod_state_size", - .dtype = _PySlot_TYPE_SIZE, - .kind = _PySlot_KIND_MOD, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [103] = { - .name = "Py_mod_methods", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [104] = { - .name = "Py_mod_state_traverse", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [105] = { - .name = "Py_mod_state_clear", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [106] = { - .name = "Py_mod_state_free", - .dtype = _PySlot_TYPE_FUNC, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [107] = { - .name = "Py_tp_metaclass", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [108] = { - .name = "Py_tp_module", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_TYPE, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [109] = { - .name = "Py_mod_abi", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_REJECT, - }, - [110] = { - .name = "Py_mod_token", - .dtype = _PySlot_TYPE_PTR, - .kind = _PySlot_KIND_MOD, - .null_handling = _PySlot_PROBLEM_REJECT, - .duplicate_handling = _PySlot_PROBLEM_REJECT, - }, - [111] = {0} +char *_PySlot_names[] = { + "Py_slot_end", "Py_bf_getbuffer/Py_mod_create", + "Py_bf_releasebuffer/Py_mod_exec", + "Py_mp_ass_subscript/Py_mod_multiple_interpreters", + "Py_mp_length/Py_mod_gil", "Py_mp_subscript", "Py_nb_absolute", + "Py_nb_add", "Py_nb_and", "Py_nb_bool", "Py_nb_divmod", "Py_nb_float", + "Py_nb_floor_divide", "Py_nb_index", "Py_nb_inplace_add", + "Py_nb_inplace_and", "Py_nb_inplace_floor_divide", "Py_nb_inplace_lshift", + "Py_nb_inplace_multiply", "Py_nb_inplace_or", "Py_nb_inplace_power", + "Py_nb_inplace_remainder", "Py_nb_inplace_rshift", + "Py_nb_inplace_subtract", "Py_nb_inplace_true_divide", "Py_nb_inplace_xor", + "Py_nb_int", "Py_nb_invert", "Py_nb_lshift", "Py_nb_multiply", + "Py_nb_negative", "Py_nb_or", "Py_nb_positive", "Py_nb_power", + "Py_nb_remainder", "Py_nb_rshift", "Py_nb_subtract", "Py_nb_true_divide", + "Py_nb_xor", "Py_sq_ass_item", "Py_sq_concat", "Py_sq_contains", + "Py_sq_inplace_concat", "Py_sq_inplace_repeat", "Py_sq_item", + "Py_sq_length", "Py_sq_repeat", "Py_tp_alloc", "Py_tp_base", "Py_tp_bases", + "Py_tp_call", "Py_tp_clear", "Py_tp_dealloc", "Py_tp_del", + "Py_tp_descr_get", "Py_tp_descr_set", "Py_tp_doc", "Py_tp_getattr", + "Py_tp_getattro", "Py_tp_hash", "Py_tp_init", "Py_tp_is_gc", "Py_tp_iter", + "Py_tp_iternext", "Py_tp_methods", "Py_tp_new", "Py_tp_repr", + "Py_tp_richcompare", "Py_tp_setattr", "Py_tp_setattro", "Py_tp_str", + "Py_tp_traverse", "Py_tp_members", "Py_tp_getset", "Py_tp_free", + "Py_nb_matrix_multiply", "Py_nb_inplace_matrix_multiply", "Py_am_await", + "Py_am_aiter", "Py_am_anext", "Py_tp_finalize", "Py_am_send", + "Py_tp_vectorcall", "Py_tp_token", "Py_mod_create", "Py_mod_exec", + "Py_mod_multiple_interpreters", "Py_mod_gil", "Py_bf_getbuffer", + "Py_bf_releasebuffer", "Py_mp_ass_subscript", "Py_mp_length", + "Py_slot_subslots", "Py_tp_slots", "Py_mod_slots", "Py_tp_name", + "Py_tp_basicsize", "Py_tp_extra_basicsize", "Py_tp_itemsize", + "Py_tp_flags", "Py_mod_name", "Py_mod_doc", "Py_mod_state_size", + "Py_mod_methods", "Py_mod_state_traverse", "Py_mod_state_clear", + "Py_mod_state_free", "Py_tp_metaclass", "Py_tp_module", "Py_mod_abi", + "Py_mod_token", NULL }; diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index 4362855b30099b..379ff37f262fe3 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -15,9 +15,31 @@ REPO_ROOT = Path(__file__).parent.parent.parent DEFAULT_INPUT_PATH = REPO_ROOT / 'Python/slots.toml' -DEFAULT_HEADER_PATH = REPO_ROOT / 'Include/slots_generated.h' +INCLUDE_PATH = REPO_ROOT / 'Include' +DEFAULT_PUBLIC_HEADER_PATH = INCLUDE_PATH / 'slots_generated.h' +DEFAULT_PRIVATE_HEADER_PATH = INCLUDE_PATH / 'internal/pycore_slots_generated.h' DEFAULT_C_PATH = REPO_ROOT / 'Python/slots_generated.c' +FIRST_3_15_SLOT = 83 + +TABLE_FIELDS = { + 'tp': 'ht_type', + 'am': 'as_async', + 'nb': 'as_number', + 'mp': 'as_mapping', + 'sq': 'as_sequence', + 'bf': 'as_buffer', +} +TABLES = { + 'tp': ('PyTypeObject', None, None), + 'mp': ('PyTypeObject', 'tp_as_mapping', 'PyMappingMethods'), + 'nb': ('PyTypeObject', 'tp_as_number', 'PyNumberMethods'), + 'sq': ('PyTypeObject', 'tp_as_sequence', 'PySequenceMethods'), + 'am': ('PyTypeObject', 'tp_as_async', 'PyAsyncMethods'), + 'bf': ('PyTypeObject', 'tp_as_buffer', 'PyBufferProcs'), + 'ht': ('PyHeapTypeObject', None, None), +} + def parse_slots(file): toml_contents = tomllib.load(file) result = [None] * len(toml_contents) @@ -34,7 +56,7 @@ def parse_slots(file): return result -def write_header(f, slots): +def write_public_header(f, slots): out = functools.partial(print, file=f) out(f'/* {GENERATED_BY} */') out() @@ -51,7 +73,6 @@ def write_header(f, slots): for slot in slots for new_name in slot.get('equivalents', {}).values() } - print(compat_ids) for slot in slots: if slot['kind'] == 'compat': continue @@ -65,97 +86,185 @@ def write_header(f, slots): out(f'#endif /* _PY_HAVE_SLOTS_GENERATED_H */') +def spam_lines(segments, indent=' ' * 8, sep=' ', maxlen=79): + current_line = '' + maxlen -= len(indent) + for segment in segments: + if len(current_line) + len(sep) + len(segment) > maxlen: + yield indent + current_line + current_line = segment + else: + if current_line: + current_line += sep + current_line += segment + if current_line != indent: + yield indent + current_line + + +def write_private_header(f, slots): + out = functools.partial(print, file=f) + + def add_case(slot): + out(out(f' case {slot['id']}:')) + + slots_by_name = {slot['name']: slot for slot in slots if 'name' in slot} + + out(f'/* {GENERATED_BY} */') + out() + out(f'#ifndef _PY_HAVE_INTERNAL_SLOTS_GENERATED_H') + out(f'#define _PY_HAVE_INTERNAL_SLOTS_GENERATED_H') + for kind in 'type', 'mod': + out() + out(f'static inline uint16_t') + out(f'_PySlot_resolve_{kind}_slot(uint16_t slot_id)') + out(f'{{') + out(f' switch (slot_id) {{') + good_slots = [] + bad_slots = [] + for slot in slots: + if slot['kind'] == 'compat': + new_slot = slots_by_name[slot['equivalents'][kind]] + out(f' case {slot['id']}: return {new_slot['id']};') + elif slot['kind'] in {kind, 'slot'}: + good_slots.append(f'case {slot['id']}:') + for line in spam_lines(good_slots): + out(line) + out(f' return slot_id;') + out(f' default:') + out(f' return Py_slot_invalid;') + out(f' }}') + out(f'}}') + out() + out(f'static inline void*') + out(f'_PySlot_type_ptr(PyTypeObject *tp, uint16_t slot_id)') + out(f'{{') + out(f' switch (slot_id) {{') + rest = [] + for slot in slots: + if slot.get('is_type_field'): + field = slot.get('field', slot['name'].removeprefix('Py_')) + table_ident = slot.get('table', field[:2]) + _, table, _ = TABLES[table_ident] + if table_ident == 'tp': + out(f' case {slot["id"]}: return &tp->{field};') + else: + if table_ident == 'ht': + cond = 'tp->tp_flags & Py_TPFLAGS_HEAPTYPE' + val = f'&((PyHeapTypeObject*)tp)->{field}' + else: + cond = f'tp->{table}' + val = f'&tp->{table}->{field}' + out(f' case {slot["id"]}: return ({cond})') + out(f' ? {val} : NULL;') + out(f' }}') + out(f' _PySlot_err_bad_slot("type", slot_id);') + out(f' return NULL;') + out(f'}}') + out() + out(f'static inline void') + out(f'_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht, PySlot slot)') + out(f'{{') + out(f' switch (slot.sl_id) {{') + rest = [] + for slot in slots: + if slot.get('is_type_field'): + field = slot.get('field', slot['name'].removeprefix('Py_')) + table_ident = slot.get('table', field[:2]) + if table_ident == 'ht': + continue + table = TABLE_FIELDS[table_ident] + slot_field = slot.get('dtype', 'func') + functype = slot.get('functype', None) + if functype: + functype = f'({functype})' + else: + functype = '' + out(f' case {slot["id"]}: ht->{table}.{field} = {functype}slot.sl_{slot_field}; break;') + out(f' }}') + out(f'}}') + out() + out(f'static inline _PySlot_DTYPE') + out(f'_PySlot_get_dtype(uint16_t slot_id)') + out(f'{{') + out(f' switch (slot_id) {{') + for slot in slots: + if slot['kind'] == 'compat': + continue + try: + dtype = slot['dtype'] + except KeyError: + if slot.get('is_type_field'): + dtype = 'func' + assert 'functype' in slot + else: + dtype = '???' + name = slot.get("name", slot["id"]) + out(f' case {name}: return _PySlot_DTYPE_{dtype.upper()};') + out(f' default: return _PySlot_DTYPE_VOID;') + out(f' }}') + out(f'}}') + out() + for problem in 'duplicate', 'null': + out(f'static inline _PySlot_PROBLEM_HANDLING') + out(f'_PySlot_get_{problem}_handling(uint16_t slot_id)') + out(f'{{') + out(f' switch (slot_id) {{') + results = {'DEPRECATED': [], 'ALLOW': []} + for slot in slots: + if problem == 'null': + if slot.get('dtype', 'ptr') in {'ptr', 'func'}: + default = 'reject' + else: + default = 'allow' + else: + default = 'reject' + handling = slot.get(problem + 's', default) + if handling == 'reject': + continue + results[handling.upper()].append(f'case {slot["id"]}:') + for handling, cases in results.items(): + for line in spam_lines(cases): + out(line) + out(f' return _PySlot_PROBLEM_{handling};') + out(f' default:') + out(f' return _PySlot_PROBLEM_REJECT;') + out(f' }}') + out(f'}}') + out() + out(f'static inline bool') + out(f'_PySlot_is_name(uint16_t slot_id)') + out(f'{{') + out(f' switch (slot_id) {{') + for slot in slots: + if slot.get('is_name'): + out(f' case {slot["name"]}: return true;') + out(f' default: return false;') + out(f' }}') + out(f'}}') + out() + out(f'#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */') + + def write_c(f, slots): out = functools.partial(print, file=f) out(f'/* {GENERATED_BY} */') out() out('#include "Python.h"') - out('#include "pycore_slots.h" // _PySlot_Info') - out('#include // offsetof') - out('#include // true') + out('#include "pycore_slots.h" // _PySlot_names') out() - out(f'_PySlot_Info _PySlot_InfoTable[_Py_slot_COUNT + 1] = {{') - - assert len(slots) == max(slot.id for slot in slots) + 1 + out(f'char *_PySlot_names[] = {{') + names = [] for slot in slots: try: - out(f" [{slot.id}] = {{") - initializers = { - "name": f'"Py_{slot.name}"', - "dtype": f'_PySlot_TYPE_{slot.dtype.upper()}', - "kind": f'_PySlot_KIND_{slot.kind.upper()}', - } - match slot.kind: - case 'slot': - pass - case 'compat': - for newslot in slots: - if newslot.get_int('compat') == slot.id: - key = f'compat_info.{newslot.kind}_id' - initializers[key] = newslot.id - case 'type': - field = slot.get('field', slot.name) - if not slot.get_bool('virtual'): - if slot.id > FIRST_3_15_SLOT: - raise ValueError('new slots should be virtual') - tpo = 'PyTypeObject' - typeobj, subtable, tabletype = { - 'tp': (tpo, None, None), - 'mp': (tpo, 'tp_as_mapping', 'PyMappingMethods'), - 'nb': (tpo, 'tp_as_number', 'PyNumberMethods'), - 'sq': (tpo, 'tp_as_sequence', 'PySequenceMethods'), - 'am': (tpo, 'tp_as_async', 'PyAsyncMethods'), - 'bf': (tpo, 'tp_as_buffer', 'PyBufferProcs'), - 'ht': ('PyHeapTypeObject', None, None), - }[slot.type_table] - if subtable: - initializers['type_info.slot_offset'] = ( - f'offsetof({typeobj}, {subtable})') - initializers['type_info.subslot_offset'] = ( - f'offsetof({tabletype}, {field})') - else: - initializers['type_info.slot_offset'] = ( - f'offsetof({typeobj}, {field})') - initializers['type_info.subslot_offset'] = '-1' - case 'mod': - pass - case _: - raise ValueError(slot.kind) - if slot.get_bool('subslots'): - initializers['is_subslots'] = 'true' - match slot.get('null'): - case 'reject': - initializers['null_handling'] = '_PySlot_PROBLEM_REJECT' - case 'allow': - initializers['null_handling'] = '_PySlot_PROBLEM_ALLOW' - case None: - if slot.dtype in {'func', 'ptr'}: - if slot.id > FIRST_3_15_SLOT: - raise ValueError( - f'slot {slot.name} needs explicit NULL ' - + 'handling specification') - initializers['null_handling'] = ( - '_PySlot_PROBLEM_DEPRECATED') - match slot.get('duplicates'): - case 'no': - initializers['duplicate_handling'] = '_PySlot_PROBLEM_REJECT' - case 'yes': - pass - case None: - initializers['duplicate_handling'] = '_PySlot_PROBLEM_DEPRECATED' - case _: - raise ValueError(slot['duplicates']) - if slot.get_bool('is_name'): - initializers['is_name'] = 'true' - for name, initializer in initializers.items(): - out(f' .{name} = {initializer},') - out(f" }},") - except Exception as e: - e.add_note(f'handling slot {slot}') - raise - out(f" [{len(slots)}] = {{0}}") - out(f"}};") - + names.append(slot["name"]) + except KeyError: + names.append('/'.join(slot["equivalents"].values())) + for line in spam_lines( + [f'"{name}",' for name in names] + ['NULL'], + indent=' ', + ): + out(line) + out(f'}};') @contextlib.contextmanager @@ -195,16 +304,21 @@ def main(argv): description='By default, no files are generated. Use --generate-all ' + 'or the options below to generate them.') outfile_group.add_argument( - '-H', '--header', + '-H', '--public-header', help='file into which to write the public header') + outfile_group.add_argument( + '-I', '--private-header', + help='file into which to write the private header') outfile_group.add_argument( '-C', '--cfile', help='file into which to write internal C code') args = parser.parse_args(argv[1:]) if args.generate_all: - if args.header is None: - args.header = DEFAULT_HEADER_PATH + if args.public_header is None: + args.public_header = DEFAULT_PUBLIC_HEADER_PATH + if args.private_header is None: + args.private_header = DEFAULT_PRIVATE_HEADER_PATH if args.cfile is None: args.cfile = DEFAULT_C_PATH @@ -215,9 +329,13 @@ def main(argv): for slot in slots: print(json.dumps(slot.to_dict())) - if args.header: - with replace_file(args.header) as f: - write_header(f, slots) + if args.public_header: + with replace_file(args.public_header) as f: + write_public_header(f, slots) + + if args.private_header: + with replace_file(args.private_header) as f: + write_private_header(f, slots) if args.cfile: with replace_file(args.cfile) as f: From fea6e21e1ef60c5ba451eb5edac05995b8e35828 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Jan 2026 10:49:44 +0100 Subject: [PATCH 16/36] Handle PyType_Spec specially --- Include/internal/pycore_slots.h | 10 +- Include/internal/pycore_slots_generated.h | 182 ++++++++++------------ Objects/typeobject.c | 163 +++++++++---------- Python/slots.c | 6 - Python/slots.toml | 3 +- Tools/build/generate_slots.py | 23 +-- 6 files changed, 179 insertions(+), 208 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index bfd5c107fe4e9b..1b496ecea4853c 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -56,17 +56,15 @@ typedef struct { bool is_at_end :1; bool is_first_run :1; + // Name of the object (type/module) being defined, NULL if unknown. + // Must be set by the callers as soon as it's known. + const char *name; + /* Output information: */ // The slot. Always a copy; may be modified by caller of the iterator. PySlot current; - // Name of the object (type/module) being defined, NULL if unknown. - // Set by _PySlotIterator_Next as soon as it sees a tp_name/mod_name slot; - // used for internal error messages but available to the caller too. - // This points to the slot; must be copied for longer usage. - // The name is not reset by rewinding. - const char *name; } _PySlotIterator; /* Initialize an iterator using a Py_Slot array */ diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index 19cda06ad89072..e8d44e321d6914 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -47,147 +47,147 @@ _PySlot_resolve_mod_slot(uint16_t slot_id) } static inline void* -_PySlot_type_ptr(PyTypeObject *tp, uint16_t slot_id) +_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id) { switch (slot_id) { case 5: return (tp->tp_as_mapping) - ? &tp->tp_as_mapping->mp_subscript : NULL; + ? tp->tp_as_mapping->mp_subscript : NULL; case 6: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_absolute : NULL; + ? tp->tp_as_number->nb_absolute : NULL; case 7: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_add : NULL; + ? tp->tp_as_number->nb_add : NULL; case 8: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_and : NULL; + ? tp->tp_as_number->nb_and : NULL; case 9: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_bool : NULL; + ? tp->tp_as_number->nb_bool : NULL; case 10: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_divmod : NULL; + ? tp->tp_as_number->nb_divmod : NULL; case 11: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_float : NULL; + ? tp->tp_as_number->nb_float : NULL; case 12: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_floor_divide : NULL; + ? tp->tp_as_number->nb_floor_divide : NULL; case 13: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_index : NULL; + ? tp->tp_as_number->nb_index : NULL; case 14: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_add : NULL; + ? tp->tp_as_number->nb_inplace_add : NULL; case 15: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_and : NULL; + ? tp->tp_as_number->nb_inplace_and : NULL; case 16: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_floor_divide : NULL; + ? tp->tp_as_number->nb_inplace_floor_divide : NULL; case 17: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_lshift : NULL; + ? tp->tp_as_number->nb_inplace_lshift : NULL; case 18: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_multiply : NULL; + ? tp->tp_as_number->nb_inplace_multiply : NULL; case 19: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_or : NULL; + ? tp->tp_as_number->nb_inplace_or : NULL; case 20: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_power : NULL; + ? tp->tp_as_number->nb_inplace_power : NULL; case 21: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_remainder : NULL; + ? tp->tp_as_number->nb_inplace_remainder : NULL; case 22: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_rshift : NULL; + ? tp->tp_as_number->nb_inplace_rshift : NULL; case 23: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_subtract : NULL; + ? tp->tp_as_number->nb_inplace_subtract : NULL; case 24: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_true_divide : NULL; + ? tp->tp_as_number->nb_inplace_true_divide : NULL; case 25: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_xor : NULL; + ? tp->tp_as_number->nb_inplace_xor : NULL; case 26: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_int : NULL; + ? tp->tp_as_number->nb_int : NULL; case 27: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_invert : NULL; + ? tp->tp_as_number->nb_invert : NULL; case 28: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_lshift : NULL; + ? tp->tp_as_number->nb_lshift : NULL; case 29: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_multiply : NULL; + ? tp->tp_as_number->nb_multiply : NULL; case 30: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_negative : NULL; + ? tp->tp_as_number->nb_negative : NULL; case 31: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_or : NULL; + ? tp->tp_as_number->nb_or : NULL; case 32: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_positive : NULL; + ? tp->tp_as_number->nb_positive : NULL; case 33: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_power : NULL; + ? tp->tp_as_number->nb_power : NULL; case 34: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_remainder : NULL; + ? tp->tp_as_number->nb_remainder : NULL; case 35: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_rshift : NULL; + ? tp->tp_as_number->nb_rshift : NULL; case 36: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_subtract : NULL; + ? tp->tp_as_number->nb_subtract : NULL; case 37: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_true_divide : NULL; + ? tp->tp_as_number->nb_true_divide : NULL; case 38: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_xor : NULL; + ? tp->tp_as_number->nb_xor : NULL; case 39: return (tp->tp_as_sequence) - ? &tp->tp_as_sequence->sq_ass_item : NULL; + ? tp->tp_as_sequence->sq_ass_item : NULL; case 40: return (tp->tp_as_sequence) - ? &tp->tp_as_sequence->sq_concat : NULL; + ? tp->tp_as_sequence->sq_concat : NULL; case 41: return (tp->tp_as_sequence) - ? &tp->tp_as_sequence->sq_contains : NULL; + ? tp->tp_as_sequence->sq_contains : NULL; case 42: return (tp->tp_as_sequence) - ? &tp->tp_as_sequence->sq_inplace_concat : NULL; + ? tp->tp_as_sequence->sq_inplace_concat : NULL; case 43: return (tp->tp_as_sequence) - ? &tp->tp_as_sequence->sq_inplace_repeat : NULL; + ? tp->tp_as_sequence->sq_inplace_repeat : NULL; case 44: return (tp->tp_as_sequence) - ? &tp->tp_as_sequence->sq_item : NULL; + ? tp->tp_as_sequence->sq_item : NULL; case 45: return (tp->tp_as_sequence) - ? &tp->tp_as_sequence->sq_length : NULL; + ? tp->tp_as_sequence->sq_length : NULL; case 46: return (tp->tp_as_sequence) - ? &tp->tp_as_sequence->sq_repeat : NULL; - case 47: return &tp->tp_alloc; - case 48: return &tp->tp_base; - case 49: return &tp->tp_bases; - case 50: return &tp->tp_call; - case 51: return &tp->tp_clear; - case 52: return &tp->tp_dealloc; - case 53: return &tp->tp_del; - case 54: return &tp->tp_descr_get; - case 55: return &tp->tp_descr_set; - case 56: return &tp->tp_doc; - case 57: return &tp->tp_getattr; - case 58: return &tp->tp_getattro; - case 59: return &tp->tp_hash; - case 60: return &tp->tp_init; - case 61: return &tp->tp_is_gc; - case 62: return &tp->tp_iter; - case 63: return &tp->tp_iternext; - case 64: return &tp->tp_methods; - case 65: return &tp->tp_new; - case 66: return &tp->tp_repr; - case 67: return &tp->tp_richcompare; - case 68: return &tp->tp_setattr; - case 69: return &tp->tp_setattro; - case 70: return &tp->tp_str; - case 71: return &tp->tp_traverse; - case 72: return &tp->tp_members; - case 73: return &tp->tp_getset; - case 74: return &tp->tp_free; + ? tp->tp_as_sequence->sq_repeat : NULL; + case 47: return (void*)tp->tp_alloc; + case 48: return (void*)tp->tp_base; + case 49: return (void*)tp->tp_bases; + case 50: return (void*)tp->tp_call; + case 51: return (void*)tp->tp_clear; + case 52: return (void*)tp->tp_dealloc; + case 53: return (void*)tp->tp_del; + case 54: return (void*)tp->tp_descr_get; + case 55: return (void*)tp->tp_descr_set; + case 56: return (void*)tp->tp_doc; + case 57: return (void*)tp->tp_getattr; + case 58: return (void*)tp->tp_getattro; + case 59: return (void*)tp->tp_hash; + case 60: return (void*)tp->tp_init; + case 61: return (void*)tp->tp_is_gc; + case 62: return (void*)tp->tp_iter; + case 63: return (void*)tp->tp_iternext; + case 64: return (void*)tp->tp_methods; + case 65: return (void*)tp->tp_new; + case 66: return (void*)tp->tp_repr; + case 67: return (void*)tp->tp_richcompare; + case 68: return (void*)tp->tp_setattr; + case 69: return (void*)tp->tp_setattro; + case 70: return (void*)tp->tp_str; + case 71: return (void*)tp->tp_traverse; + case 72: return (void*)tp->tp_members; + case 73: return (void*)tp->tp_getset; + case 74: return (void*)tp->tp_free; case 75: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_matrix_multiply : NULL; + ? tp->tp_as_number->nb_matrix_multiply : NULL; case 76: return (tp->tp_as_number) - ? &tp->tp_as_number->nb_inplace_matrix_multiply : NULL; + ? tp->tp_as_number->nb_inplace_matrix_multiply : NULL; case 77: return (tp->tp_as_async) - ? &tp->tp_as_async->am_await : NULL; + ? tp->tp_as_async->am_await : NULL; case 78: return (tp->tp_as_async) - ? &tp->tp_as_async->am_aiter : NULL; + ? tp->tp_as_async->am_aiter : NULL; case 79: return (tp->tp_as_async) - ? &tp->tp_as_async->am_anext : NULL; - case 80: return &tp->tp_finalize; + ? tp->tp_as_async->am_anext : NULL; + case 80: return (void*)tp->tp_finalize; case 81: return (tp->tp_as_async) - ? &tp->tp_as_async->am_send : NULL; - case 82: return &tp->tp_vectorcall; + ? tp->tp_as_async->am_send : NULL; + case 82: return (void*)tp->tp_vectorcall; case 83: return (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) - ? &((PyHeapTypeObject*)tp)->ht_token : NULL; + ? ((PyHeapTypeObject*)tp)->ht_token : NULL; case 88: return (tp->tp_as_buffer) - ? &tp->tp_as_buffer->bf_getbuffer : NULL; + ? tp->tp_as_buffer->bf_getbuffer : NULL; case 89: return (tp->tp_as_buffer) - ? &tp->tp_as_buffer->bf_releasebuffer : NULL; + ? tp->tp_as_buffer->bf_releasebuffer : NULL; case 90: return (tp->tp_as_mapping) - ? &tp->tp_as_mapping->mp_ass_subscript : NULL; + ? tp->tp_as_mapping->mp_ass_subscript : NULL; case 91: return (tp->tp_as_mapping) - ? &tp->tp_as_mapping->mp_length : NULL; + ? tp->tp_as_mapping->mp_length : NULL; } - _PySlot_err_bad_slot("type", slot_id); + _PySlot_err_bad_slot("PyType_GetSlot", slot_id); return NULL; } @@ -410,7 +410,7 @@ _PySlot_get_duplicate_handling(uint16_t slot_id) case 71: case 74: case 75: case 76: case 77: case 78: case 79: case 80: case 81: case 82: case 88: case 89: case 90: case 91: return _PySlot_PROBLEM_DEPRECATED; - case 85: + case 85: case 109: return _PySlot_PROBLEM_ALLOW; default: return _PySlot_PROBLEM_REJECT; @@ -441,14 +441,4 @@ _PySlot_get_null_handling(uint16_t slot_id) } } -static inline bool -_PySlot_is_name(uint16_t slot_id) -{ - switch (slot_id) { - case Py_tp_name: return true; - case Py_mod_name: return true; - default: return false; - } -} - #endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index be74ab758980e5..82d28a62afa3d5 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5219,8 +5219,10 @@ special_offset_from_member( } -PyObject * -type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) +static PyObject * +type_from_slots_or_spec( + PySlot *slots, PyType_Spec *spec, + PyTypeObject *metaclass, PyObject *module, PyObject *bases_in) { /* Invariant: A non-NULL value in one of these means this function holds * a strong reference or owns allocated memory. @@ -5244,26 +5246,61 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) Py_ssize_t basicsize = 0; Py_ssize_t extra_basicsize = 0; Py_ssize_t itemsize = 0; + int flags = 0; + void *token = NULL; bool have_relative_members = false; Py_ssize_t max_relative_offset = 0; - /* The following are borrowed from the slots. */ - PyTypeObject *metaclass = NULL; - PyObject *module = NULL; - PyObject *bases_slot = NULL; - int flags = 0; + PyObject *bases_slot = NULL; /* borrowed from the slots */ _PySlotIterator it; - _PySlotIterator_Init(&it, slots, _PySlot_KIND_TYPE); + + if (spec) { + assert(!slots); + if (spec->basicsize > 0) { + basicsize = spec->basicsize; + } + if (spec->basicsize < 0) { + extra_basicsize = -spec->basicsize; + } + itemsize = spec->itemsize; + flags = spec->flags; + _PySlotIterator_InitLegacy(&it, spec->slots, _PySlot_KIND_TYPE); + it.name = spec->name; + } + else { + assert(!spec); + assert(!metaclass); + assert(!module); + assert(!bases_in); + _PySlotIterator_Init(&it, slots, _PySlot_KIND_TYPE); + } + + #define NO_SPEC \ + if (spec) { \ + PyErr_Format( \ + PyExc_SystemError, \ + "%s must not be used with PyType_Spec", \ + _PySlot_GetName(it.current.sl_id)); \ + goto finally; \ + } \ + ///////////////////////////////////////////////////// + while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: goto finally; + case Py_tp_name: + NO_SPEC; + it.name = it.current.sl_ptr; + break; case Py_tp_metaclass: + NO_SPEC; metaclass = it.current.sl_ptr; break; case Py_tp_module: + NO_SPEC; module = it.current.sl_ptr; break; case Py_tp_bases: @@ -5281,6 +5318,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) } break; case Py_tp_basicsize: + NO_SPEC; basicsize = it.current.sl_size; if (basicsize <= 0) { PyErr_SetString( @@ -5290,6 +5328,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) } break; case Py_tp_extra_basicsize: + NO_SPEC; extra_basicsize = it.current.sl_size; if (extra_basicsize <= 0) { PyErr_SetString( @@ -5299,6 +5338,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) } break; case Py_tp_itemsize: + NO_SPEC; itemsize = it.current.sl_size; if (itemsize <= 0) { PyErr_SetString( @@ -5308,6 +5348,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) } break; case Py_tp_flags: + NO_SPEC; flags = it.current.sl_uint64; break; case Py_tp_members: @@ -5336,22 +5377,21 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) } break; case Py_tp_token: - if (!spec_for_token && it.current.sl_ptr == Py_TP_USE_SPEC) { - PyErr_SetString( - PyExc_SystemError, - "Py_TP_USE_SPEC and NULL can only be used with PyType_Spec"); - goto finally; + token = it.current.sl_ptr; + if (token == Py_TP_USE_SPEC) { + if (!spec) { + PyErr_SetString( + PyExc_SystemError, + "Py_tp_token: Py_TP_USE_SPEC (NULL) can only be " + "used with PyType_Spec"); + goto finally; + } + token = spec; } break; case Py_tp_doc: /* For the docstring slot, which usually points to a static string literal, we need to make a copy */ - if (tp_doc != NULL) { - PyErr_SetString( - PyExc_SystemError, - "Multiple Py_tp_doc slots are not supported."); - goto finally; - } if (it.current.sl_ptr == NULL) { PyMem_Free(tp_doc); tp_doc = NULL; @@ -5368,11 +5408,12 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) break; } } + #undef NO_SPEC /* Required slots & bad combinations */ if (it.name == NULL) { - if (spec_for_token) { + if (spec) { PyErr_SetString(PyExc_SystemError, "Type spec does not define the name field."); } @@ -5395,7 +5436,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) } if (have_relative_members) { - if (_PySlotIterator_SawSlot(&it, Py_tp_basicsize)) { + if (!extra_basicsize) { PyErr_SetString( PyExc_SystemError, "With Py_RELATIVE_OFFSET, basicsize must be extended"); @@ -5443,8 +5484,10 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) /* Get a tuple of bases. * bases is a strong reference (unlike bases_in). + * (This is convoluted for backwards compatibility -- preserving priority + * of the various ways to specify bases) */ - if ((bases_arg ? 1 : 0) + if ((bases_in ? 1 : 0) + (_PySlotIterator_SawSlot(&it, Py_tp_bases) ? 1 : 0) + (_PySlotIterator_SawSlot(&it, Py_tp_base) ? 1 : 0) > 1) @@ -5455,15 +5498,15 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) it.name)) goto finally; } - if (!bases_arg) { - bases_arg = bases_slot; + if (!bases_in) { + bases_in = bases_slot; } - if (bases_arg) { - if (PyTuple_Check(bases_arg)) { - bases = Py_NewRef(bases_arg); + if (bases_in) { + if (PyTuple_Check(bases_in)) { + bases = Py_NewRef(bases_in); } else { - bases = PyTuple_Pack(1, bases_arg); + bases = PyTuple_Pack(1, bases_in); } } else { @@ -5473,8 +5516,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) goto finally; } - /* If this is an immutable type, check if all bases are also immutable, - * and (for now) fire a deprecation warning if not. + /* If this is an immutable type, check if all bases are also immutable. * (This isn't necessary for static types: those can't have heap bases, * and only heap types can be mutable.) */ @@ -5601,6 +5643,8 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) res->_ht_tpname = _ht_tpname; _ht_tpname = NULL; // Give ownership to the type + res->ht_token = token; + /* Copy the sizes */ type->tp_basicsize = basicsize; @@ -5608,7 +5652,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) /* Second pass of slots: copy most of them into the type */ - _PySlotIterator_Rewind(&it, slots); + _PySlotIterator_Rewind(&it, spec ? (void*)spec->slots : (void*)slots); while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: @@ -5636,21 +5680,8 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) } } break; - case Py_tp_token: - { - if (it.current.sl_ptr == Py_TP_USE_SPEC) { - res->ht_token = spec_for_token; - } - else { - res->ht_token = it.current.sl_ptr; - } - } - break; default: - { - /* Copy other slots directly */ - _PySlot_heaptype_apply_field_slot(res, it.current); - } + _PySlot_heaptype_apply_field_slot(res, it.current); break; } } @@ -5757,7 +5788,7 @@ type_from_slots(PySlot *slots, PyType_Spec *spec_for_token, PyObject *bases_arg) PyObject * PyType_FromSlots(PySlot *slots) { - return type_from_slots(slots, NULL, NULL); + return type_from_slots_or_spec(slots, NULL, NULL, NULL, NULL); } PyObject * @@ -5765,48 +5796,25 @@ PyType_FromMetaclass( PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) { - static const PySlot nop = {Py_slot_invalid, .sl_flags=PySlot_OPTIONAL}; - PySlot slots[] = { - PySlot_DATA(Py_tp_name, spec->name), - (spec->basicsize > 0 - ? (PySlot)PySlot_SIZE(Py_tp_basicsize, spec->basicsize) - : spec->basicsize < 0 - ? (PySlot)PySlot_SIZE(Py_tp_extra_basicsize, -spec->basicsize) - : nop), - (spec->itemsize - ? (PySlot)PySlot_SIZE(Py_tp_itemsize, spec->itemsize) - : nop), - PySlot_UINT64(Py_tp_flags, spec->flags), - PySlot_DATA(Py_tp_slots, spec->slots), - (metaclass - ? (PySlot)PySlot_PTR(Py_tp_metaclass, metaclass) - : nop), - (module - ? (PySlot)PySlot_PTR(Py_tp_module, module) - : nop), - /* We pass *bases* separately for deprecation and error reporting; - * eventually we can convert it to Py_tp_bases. */ - PySlot_END, - }; - return type_from_slots(slots, spec, bases); + return type_from_slots_or_spec(NULL, spec, metaclass, module, bases); } PyObject * PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) { - return PyType_FromMetaclass(NULL, module, spec, bases); + return type_from_slots_or_spec(NULL, spec, NULL, module, bases); } PyObject * PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) { - return PyType_FromMetaclass(NULL, NULL, spec, bases); + return type_from_slots_or_spec(NULL, spec, NULL, NULL, bases); } PyObject * PyType_FromSpec(PyType_Spec *spec) { - return PyType_FromMetaclass(NULL, NULL, spec, NULL); + return type_from_slots_or_spec(NULL, spec, NULL, NULL, NULL); } PyObject * @@ -5831,14 +5839,7 @@ void * PyType_GetSlot(PyTypeObject *type, int slot_in) { uint16_t slot = _PySlot_resolve_type_slot(slot_in); - if (slot == Py_slot_invalid) { - return NULL; - } - void **ptr = _PySlot_type_ptr(type, slot); - if (ptr == NULL) { - return NULL; - } - return *ptr; + return _PySlot_type_getslot(type, slot); } PyObject * diff --git a/Python/slots.c b/Python/slots.c index 4320a8e4c23e39..10e4d7c2dae8e4 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -330,12 +330,6 @@ handle_first_run(_PySlotIterator *it) { int id = it->current.sl_id; - if (it->name == NULL && _PySlot_is_name(id)) { - MSG("setting name: %s", (char*)it->current.sl_ptr); - assert(_PySlot_get_dtype(it->current.sl_id) == _PySlot_DTYPE_PTR); - it->name = it->current.sl_ptr; - } - _PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id); if (null_handling != _PySlot_PROBLEM_ALLOW) { bool is_null = false; diff --git a/Python/slots.toml b/Python/slots.toml index 98f14d7f0018b0..9cd5e9538f9733 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -724,7 +724,6 @@ n.ulls = 'allow' [95] name = 'Py_tp_name' -is_name = true kind = 'type' dtype = 'ptr' @@ -750,7 +749,6 @@ dtype = 'uint64' [100] name = 'Py_mod_name' -is_name = true kind = 'mod' dtype = 'ptr' @@ -798,6 +796,7 @@ dtype = 'ptr' name = 'Py_mod_abi' kind = 'mod' dtype = 'ptr' +duplicates = 'allow' [110] name = 'Py_mod_token' diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index 379ff37f262fe3..56ccbe8313b1d3 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -136,7 +136,7 @@ def add_case(slot): out(f'}}') out() out(f'static inline void*') - out(f'_PySlot_type_ptr(PyTypeObject *tp, uint16_t slot_id)') + out(f'_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)') out(f'{{') out(f' switch (slot_id) {{') rest = [] @@ -146,18 +146,18 @@ def add_case(slot): table_ident = slot.get('table', field[:2]) _, table, _ = TABLES[table_ident] if table_ident == 'tp': - out(f' case {slot["id"]}: return &tp->{field};') + out(f' case {slot["id"]}: return (void*)tp->{field};') else: if table_ident == 'ht': cond = 'tp->tp_flags & Py_TPFLAGS_HEAPTYPE' - val = f'&((PyHeapTypeObject*)tp)->{field}' + val = f'((PyHeapTypeObject*)tp)->{field}' else: cond = f'tp->{table}' - val = f'&tp->{table}->{field}' + val = f'tp->{table}->{field}' out(f' case {slot["id"]}: return ({cond})') - out(f' ? {val} : NULL;') + out(f' ? {val} : NULL;') out(f' }}') - out(f' _PySlot_err_bad_slot("type", slot_id);') + out(f' _PySlot_err_bad_slot("PyType_GetSlot", slot_id);') out(f' return NULL;') out(f'}}') out() @@ -231,17 +231,6 @@ def add_case(slot): out(f' }}') out(f'}}') out() - out(f'static inline bool') - out(f'_PySlot_is_name(uint16_t slot_id)') - out(f'{{') - out(f' switch (slot_id) {{') - for slot in slots: - if slot.get('is_name'): - out(f' case {slot["name"]}: return true;') - out(f' default: return false;') - out(f' }}') - out(f'}}') - out() out(f'#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */') From a89405c3081e99f45cba8faaf541363ba7bf6bd4 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Jan 2026 12:44:36 +0100 Subject: [PATCH 17/36] Clean up the generator --- Include/internal/pycore_slots_generated.h | 38 ++- Python/slots.toml | 25 ++ Tools/build/.ruff.toml | 4 + Tools/build/generate_slots.py | 378 ++++++++++++---------- 4 files changed, 265 insertions(+), 180 deletions(-) diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index e8d44e321d6914..20adc1a0ebab43 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -7,10 +7,14 @@ static inline uint16_t _PySlot_resolve_type_slot(uint16_t slot_id) { switch (slot_id) { - case 1: return 88; - case 2: return 89; - case 3: return 90; - case 4: return 91; + case 1: + return 88; + case 2: + return 89; + case 3: + return 90; + case 4: + return 91; case 0: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: @@ -33,10 +37,14 @@ static inline uint16_t _PySlot_resolve_mod_slot(uint16_t slot_id) { switch (slot_id) { - case 1: return 84; - case 2: return 85; - case 3: return 86; - case 4: return 87; + case 1: + return 84; + case 2: + return 85; + case 3: + return 86; + case 4: + return 87; case 0: case 84: case 85: case 86: case 87: case 92: case 94: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 109: case 110: @@ -406,9 +414,10 @@ _PySlot_get_duplicate_handling(uint16_t slot_id) case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: - case 62: case 63: case 65: case 66: case 67: case 68: case 69: case 70: - case 71: case 74: case 75: case 76: case 77: case 78: case 79: case 80: - case 81: case 82: case 88: case 89: case 90: case 91: + case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: + case 70: case 71: case 73: case 74: case 75: case 76: case 77: case 78: + case 79: case 80: case 81: case 82: case 83: case 88: case 89: case 90: + case 91: return _PySlot_PROBLEM_DEPRECATED; case 85: case 109: return _PySlot_PROBLEM_ALLOW; @@ -421,6 +430,10 @@ static inline _PySlot_PROBLEM_HANDLING _PySlot_get_null_handling(uint16_t slot_id) { switch (slot_id) { + case 0: case 1: case 2: case 3: case 4: case 56: case 83: case 86: + case 87: case 92: case 93: case 96: case 97: case 98: case 99: + case 102: + return _PySlot_PROBLEM_ALLOW; case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: @@ -433,9 +446,6 @@ _PySlot_get_null_handling(uint16_t slot_id) case 78: case 79: case 80: case 81: case 82: case 84: case 85: case 88: case 89: case 90: case 91: return _PySlot_PROBLEM_DEPRECATED; - case 0: case 56: case 83: case 86: case 87: case 92: case 93: case 96: - case 97: case 98: case 99: case 102: - return _PySlot_PROBLEM_ALLOW; default: return _PySlot_PROBLEM_REJECT; } diff --git a/Python/slots.toml b/Python/slots.toml index 9cd5e9538f9733..a1d709c1ba3ff1 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -1,4 +1,26 @@ # This file lists all PySlot values +# This should only be used as input to Tools/build/generate_slots.py, +# its format can change at any time (e.g. we can rename it to slots.csv) + +# Entries: +# name: name of the slot +# kind: +# - 'type', 'mod': slots to create a particular object +# - 'slot': special slots applicable to any kind of object +# - 'compat': old IDs that nneed to be resolved +# dtype: datat type (tag for the union of sl_ptr, sl_size, etc.) +# equivalents: for 'compat' slots; the slots to resolve to +# is_type_field: slot that corresponds to a field in the type object (or in +# an array like PyNumberMethods). +# functype: C function type, where needed +# duplicates, nulls: How to handle common "problems" -- duplicate slots with +# the same ID, and NULL pointers, respectively +# - 'allow': not a problem for this slot +# - 'deprecated': issue a deprecation warning. Don't use for new slots. +# (typically, the problem was disallowed in docs, but allowed in practice) +# - 'reject': raise error +# The default is 'reject' (except NULL for non-pointer slots) + [0] name = 'Py_slot_end' @@ -497,6 +519,7 @@ name = 'Py_tp_methods' kind = 'type' is_type_field = true dtype = 'ptr' +duplicates = 'deprecated' nulls = 'deprecated' [65] @@ -567,6 +590,7 @@ name = 'Py_tp_getset' kind = 'type' is_type_field = true dtype = 'ptr' +duplicates = 'deprecated' nulls = 'deprecated' [74] @@ -647,6 +671,7 @@ kind = 'type' is_type_field = true dtype = 'ptr' field = 'ht_token' +duplicates = 'deprecated' nulls = 'allow' [84] diff --git a/Tools/build/.ruff.toml b/Tools/build/.ruff.toml index 996f725fdcb9b5..4a3dd618f6559f 100644 --- a/Tools/build/.ruff.toml +++ b/Tools/build/.ruff.toml @@ -38,3 +38,7 @@ ignore = [ "generate_{re_casefix,sre_constants,token}.py" = [ "UP031", # Use format specifiers instead of percent format ] +"generate_slots.py" = [ + "I001", # Import block is un-sorted + "ISC003", # Explicitly concatenated string +] diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index 56ccbe8313b1d3..61eaeb4e390285 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -9,6 +9,7 @@ import argparse import functools import contextlib +import collections from pathlib import Path GENERATED_BY = 'Generated by Tools/build/generate_slots.py' @@ -22,7 +23,7 @@ FIRST_3_15_SLOT = 83 -TABLE_FIELDS = { +TABLES = { 'tp': 'ht_type', 'am': 'as_async', 'nb': 'as_number', @@ -30,36 +31,118 @@ 'sq': 'as_sequence', 'bf': 'as_buffer', } -TABLES = { - 'tp': ('PyTypeObject', None, None), - 'mp': ('PyTypeObject', 'tp_as_mapping', 'PyMappingMethods'), - 'nb': ('PyTypeObject', 'tp_as_number', 'PyNumberMethods'), - 'sq': ('PyTypeObject', 'tp_as_sequence', 'PySequenceMethods'), - 'am': ('PyTypeObject', 'tp_as_async', 'PyAsyncMethods'), - 'bf': ('PyTypeObject', 'tp_as_buffer', 'PyBufferProcs'), - 'ht': ('PyHeapTypeObject', None, None), -} + + +class SlotInfo: + def __init__(self, id, data): + self.id = id + self.kind = data['kind'] + self._data = data + try: + self.name = data['name'] + except KeyError: + self.name = '/'.join(data["equivalents"].values()) + else: + assert self.name.isidentifier + + @functools.cached_property + def equivalents(self): + return self._data['equivalents'] + + @functools.cached_property + def dtype(self): + try: + return self._data['dtype'] + except KeyError: + if self.is_type_field: + return 'func' + raise + + @functools.cached_property + def functype(self): + return self._data['functype'] + + @functools.cached_property + def is_type_field(self): + return self._data.get('is_type_field') + + @functools.cached_property + def type_field(self): + return self._data.get('field', self.name.removeprefix('Py_')) + + @functools.cached_property + def type_table_ident(self): + return self._data.get('table', self.type_field[:2]) + + @functools.cached_property + def duplicate_handling(self): + return self._data.get('duplicates') + + @functools.cached_property + def null_handling(self): + return self._data.get('nulls') + def parse_slots(file): toml_contents = tomllib.load(file) result = [None] * len(toml_contents) - for key, value in toml_contents.items(): + for key, data in toml_contents.items(): slot_id = int(key) try: if result[slot_id]: raise ValueError(f'slot ID {slot_id} repeated') - kind = value['kind'] - result[slot_id] = {'id': slot_id, **value} + result[slot_id] = SlotInfo(slot_id, data) except Exception as e: e.add_note(f'handling slot {slot_id}') raise return result +class CWriter: + """Simple helper for generating C code""" + def __init__(self, file): + self.file = file + self.indent = '' + self(f'/* {GENERATED_BY} */') + self() + + def out(self, *args, **kwargs): + """print args to the file, with current indent at the start""" + self.file.write(self.indent) + print(*args, file=self.file, **kwargs) + + __call__ = out + + @contextlib.contextmanager + def block(self, header=None, end=''): + """Context for a {}-enclosed block of C""" + if header is None: + self.out('{') + else: + self.out(header, '{') + old_indent = self.indent + self.indent += ' ' + yield + self.indent = old_indent + self.out('}' + end) + + def spam(self, segments): + """Write *segments*, putting as many as will fit on a single line""" + maxlen = 79-len(self.indent) + segments = iter(segments) + current_line = next(segments) + for segment in segments: + if len(current_line) + 1 + len(segment) > maxlen: + self.out(current_line) + current_line = segment + else: + current_line += ' ' + segment + if current_line: + self.out(current_line) + + def write_public_header(f, slots): - out = functools.partial(print, file=f) - out(f'/* {GENERATED_BY} */') - out() + out = CWriter(f) out(f'#ifndef _PY_HAVE_SLOTS_GENERATED_H') out(f'#define _PY_HAVE_SLOTS_GENERATED_H') out() @@ -69,191 +152,154 @@ def write_public_header(f, slots): out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD') out(f'#endif') out() - compat_ids = {new_name: slot['id'] - for slot in slots - for new_name in slot.get('equivalents', {}).values() - } + compat_ids = {} for slot in slots: - if slot['kind'] == 'compat': + if slot.kind == 'compat': + for new_name in slot.equivalents.values(): + compat_ids[new_name] = slot.id + for slot in slots: + if slot.kind == 'compat': continue - slot_id = slot["id"] - name = slot["name"] - if compat := compat_ids.get(name): + slot_id = slot.id + if compat := compat_ids.get(slot.name): slot_id = f'_Py_SLOT_COMPAT_VALUE({compat}, {slot_id})' - out(f'#define {name} {slot_id}') + out(f'#define {slot.name} {slot_id}') out() out(f'#define _Py_slot_COUNT {len(slots)}') out(f'#endif /* _PY_HAVE_SLOTS_GENERATED_H */') -def spam_lines(segments, indent=' ' * 8, sep=' ', maxlen=79): - current_line = '' - maxlen -= len(indent) - for segment in segments: - if len(current_line) + len(sep) + len(segment) > maxlen: - yield indent + current_line - current_line = segment - else: - if current_line: - current_line += sep - current_line += segment - if current_line != indent: - yield indent + current_line - - def write_private_header(f, slots): - out = functools.partial(print, file=f) + out = CWriter(f) def add_case(slot): - out(out(f' case {slot['id']}:')) + out(out(f' case {slot.id}:')) - slots_by_name = {slot['name']: slot for slot in slots if 'name' in slot} + slots_by_name = {slot.name: slot for slot in slots} - out(f'/* {GENERATED_BY} */') - out() out(f'#ifndef _PY_HAVE_INTERNAL_SLOTS_GENERATED_H') out(f'#define _PY_HAVE_INTERNAL_SLOTS_GENERATED_H') for kind in 'type', 'mod': out() out(f'static inline uint16_t') out(f'_PySlot_resolve_{kind}_slot(uint16_t slot_id)') - out(f'{{') - out(f' switch (slot_id) {{') - good_slots = [] - bad_slots = [] - for slot in slots: - if slot['kind'] == 'compat': - new_slot = slots_by_name[slot['equivalents'][kind]] - out(f' case {slot['id']}: return {new_slot['id']};') - elif slot['kind'] in {kind, 'slot'}: - good_slots.append(f'case {slot['id']}:') - for line in spam_lines(good_slots): - out(line) - out(f' return slot_id;') - out(f' default:') - out(f' return Py_slot_invalid;') - out(f' }}') - out(f'}}') + with out.block(): + with out.block('switch (slot_id)'): + good_slots = [] + for slot in slots: + if slot.kind == 'compat': + new_slot = slots_by_name[slot.equivalents[kind]] + out(f'case {slot.id}:') + out(f' return {new_slot.id};') + elif slot.kind in {kind, 'slot'}: + good_slots.append(f'case {slot.id}:') + out.spam(good_slots) + out(f' return slot_id;') + out(f'default:') + out(f' return Py_slot_invalid;') out() out(f'static inline void*') out(f'_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)') - out(f'{{') - out(f' switch (slot_id) {{') - rest = [] - for slot in slots: - if slot.get('is_type_field'): - field = slot.get('field', slot['name'].removeprefix('Py_')) - table_ident = slot.get('table', field[:2]) - _, table, _ = TABLES[table_ident] - if table_ident == 'tp': - out(f' case {slot["id"]}: return (void*)tp->{field};') - else: - if table_ident == 'ht': - cond = 'tp->tp_flags & Py_TPFLAGS_HEAPTYPE' - val = f'((PyHeapTypeObject*)tp)->{field}' - else: - cond = f'tp->{table}' - val = f'tp->{table}->{field}' - out(f' case {slot["id"]}: return ({cond})') - out(f' ? {val} : NULL;') - out(f' }}') - out(f' _PySlot_err_bad_slot("PyType_GetSlot", slot_id);') - out(f' return NULL;') - out(f'}}') + with out.block(): + with out.block('switch (slot_id)'): + for slot in slots: + if slot.is_type_field: + field = slot.type_field + table_ident = slot.type_table_ident + if table_ident == 'tp': + out(f'case {slot.id}: return (void*)tp->{field};') + else: + if table_ident == 'ht': + cond = 'tp->tp_flags & Py_TPFLAGS_HEAPTYPE' + val = f'((PyHeapTypeObject*)tp)->{field}' + else: + table = TABLES[table_ident] + cond = f'tp->tp_{table}' + val = f'tp->tp_{table}->{field}' + out(f'case {slot.id}: return ({cond})') + out(f' ? {val} : NULL;') + out(f'_PySlot_err_bad_slot("PyType_GetSlot", slot_id);') + out(f'return NULL;') out() out(f'static inline void') - out(f'_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht, PySlot slot)') - out(f'{{') - out(f' switch (slot.sl_id) {{') - rest = [] - for slot in slots: - if slot.get('is_type_field'): - field = slot.get('field', slot['name'].removeprefix('Py_')) - table_ident = slot.get('table', field[:2]) - if table_ident == 'ht': - continue - table = TABLE_FIELDS[table_ident] - slot_field = slot.get('dtype', 'func') - functype = slot.get('functype', None) - if functype: - functype = f'({functype})' - else: - functype = '' - out(f' case {slot["id"]}: ht->{table}.{field} = {functype}slot.sl_{slot_field}; break;') - out(f' }}') - out(f'}}') + out(f'_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht,', + f'PySlot slot)') + with out.block(): + with out.block('switch (slot.sl_id)'): + for slot in slots: + if slot.is_type_field: + field = slot.type_field + table_ident = slot.type_table_ident + if table_ident == 'ht': + continue + table = TABLES[table_ident] + if slot.dtype == 'func': + functype = f'({slot.functype})' + else: + functype = '' + out( + f'case {slot.id}:', + f'ht->{table}.{field} = {functype}slot.sl_{slot.dtype};', + f'break;' + ) out() out(f'static inline _PySlot_DTYPE') out(f'_PySlot_get_dtype(uint16_t slot_id)') - out(f'{{') - out(f' switch (slot_id) {{') - for slot in slots: - if slot['kind'] == 'compat': - continue - try: - dtype = slot['dtype'] - except KeyError: - if slot.get('is_type_field'): - dtype = 'func' - assert 'functype' in slot - else: - dtype = '???' - name = slot.get("name", slot["id"]) - out(f' case {name}: return _PySlot_DTYPE_{dtype.upper()};') - out(f' default: return _PySlot_DTYPE_VOID;') - out(f' }}') - out(f'}}') + with out.block(): + with out.block('switch (slot_id)'): + for slot in slots: + if slot.kind == 'compat': + continue + dtype = slot.dtype + name = slot.name + out(f'case {name}: return _PySlot_DTYPE_{dtype.upper()};') + out(f'default: return _PySlot_DTYPE_VOID;') + out() + out(f'static inline _PySlot_PROBLEM_HANDLING') + out(f'_PySlot_get_duplicate_handling(uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + results = collections.defaultdict(list) + for slot in slots: + handling = slot.duplicate_handling or 'reject' + results[handling.upper()].append(f'case {slot.id}:') + results.pop('REJECT') + for handling, cases in results.items(): + out.spam(cases) + out(f' return _PySlot_PROBLEM_{handling};') + out(f'default:') + out(f' return _PySlot_PROBLEM_REJECT;') + out() + out(f'static inline _PySlot_PROBLEM_HANDLING') + out(f'_PySlot_get_null_handling(uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + results = collections.defaultdict(list) + for slot in slots: + handling = slot.null_handling + if handling is None: + if slot.kind != 'compat' and slot.dtype in {'ptr', 'func'}: + handling = 'reject' + else: + handling = 'allow' + results[handling.upper()].append(f'case {slot.id}:') + results.pop('REJECT') + for handling, cases in results.items(): + out.spam(cases) + out(f' return _PySlot_PROBLEM_{handling};') + out(f'default:') + out(f' return _PySlot_PROBLEM_REJECT;') out() - for problem in 'duplicate', 'null': - out(f'static inline _PySlot_PROBLEM_HANDLING') - out(f'_PySlot_get_{problem}_handling(uint16_t slot_id)') - out(f'{{') - out(f' switch (slot_id) {{') - results = {'DEPRECATED': [], 'ALLOW': []} - for slot in slots: - if problem == 'null': - if slot.get('dtype', 'ptr') in {'ptr', 'func'}: - default = 'reject' - else: - default = 'allow' - else: - default = 'reject' - handling = slot.get(problem + 's', default) - if handling == 'reject': - continue - results[handling.upper()].append(f'case {slot["id"]}:') - for handling, cases in results.items(): - for line in spam_lines(cases): - out(line) - out(f' return _PySlot_PROBLEM_{handling};') - out(f' default:') - out(f' return _PySlot_PROBLEM_REJECT;') - out(f' }}') - out(f'}}') - out() out(f'#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */') def write_c(f, slots): - out = functools.partial(print, file=f) - out(f'/* {GENERATED_BY} */') - out() + out = CWriter(f) out('#include "Python.h"') out('#include "pycore_slots.h" // _PySlot_names') out() - out(f'char *_PySlot_names[] = {{') - names = [] - for slot in slots: - try: - names.append(slot["name"]) - except KeyError: - names.append('/'.join(slot["equivalents"].values())) - for line in spam_lines( - [f'"{name}",' for name in names] + ['NULL'], - indent=' ', - ): - out(line) - out(f'}};') + with out.block(f'char *_PySlot_names[] =', end=';'): + out.spam([f'"{slot.name}",' for slot in slots] + ['NULL']) @contextlib.contextmanager From 114a4fa8cbe6025fcd83c46230efe5fc470fb8fa Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Jan 2026 14:55:50 +0100 Subject: [PATCH 18/36] Remove slots.csv --- Python/slots.csv | 159 ----------------------------------------------- 1 file changed, 159 deletions(-) delete mode 100644 Python/slots.csv diff --git a/Python/slots.csv b/Python/slots.csv deleted file mode 100644 index 7a959d27941a54..00000000000000 --- a/Python/slots.csv +++ /dev/null @@ -1,159 +0,0 @@ -id, name, kind, dtype - -#, The syntax of this file is what currently works best for editing; -#, it can change at any time. Run `typeslots.py --jsonl` to get the info -#, in a more stable format. - -#, This file is a superset of CSV; readable with -#, csv.DictReader but also hopefully human-readable and -editable. -#, Perhaps CSV shouldn't be used this way; sue me. -#, -#, Comment lines start with '#' and a comma; that is they have '#' in the first -#, field. Completely blank lines should be ignored too. -#, Avoid commas in comments; you can use semicolons. -#, -#, Each non-comment line has these mandatory columns: -#, - id: the slot's unique number (or '#' for comments) -#, - name: the slot's name without "Py_" -#, - dtype: basic data type -#, - 'func'; 'ptr'; 'size'; 'uint64'; 'int64': the field of the union -#, - 'void': value is ignored -#, - kind: -#, - 'type' is for PyTypeObject slots -#, - 'mod' is for module slots -#, - 'slot' works for any kind; handled by the slot system itself -#, - 'compat' reserved for backwards compatibility -#, -#, After these there can be 0 or more "named" columns whose contents start with -#, with '='; everything after that is the value. No spaces around '='. -#, -#, For all types: -#, - 'compat': slot number before 3.14 -#, - 'subslots=true': contains an array of slots to be merged in -#, - 'duplicates': behavior if multiple slots of this type are given -#, - 'yes': duplicates allowed -#, - 'no': duplicates rejected -#, - default: warn -#, For pointers: -#, - 'null': Behavior for NULL values: -#, - 'reject': NULLs not allowed -#, - 'allow': NULLs allowed -#, - default is that NULL is deprecated -#, - 'is_name': this is the name slot (used automatically for error messages) -#, For type slots: -#, - 'table': Slot table like 'nb' or 'tp'. If not specified: use the first -#, part of the name (except virtual slots) -#, - 'field': Field name. If not specified: use the name -#, - 'virtual=true': Does not directly correspond to a PyTypeObject (sub)field - -0, "slot_end", slot, void -1, "bf_getbuffer/mod_create", compat, void -2, "bf_releasebuffer/mod_exec", compat, void -3, "mp_ass_subscript/mod_multiple_interpreters", compat, void -4, "mp_length/mod_gil", compat, void -5, "mp_subscript", type, func -6, "nb_absolute", type, func -7, "nb_add", type, func -8, "nb_and", type, func -9, "nb_bool", type, func -10, "nb_divmod", type, func -11, "nb_float", type, func -12, "nb_floor_divide", type, func -13, "nb_index", type, func -14, "nb_inplace_add", type, func -15, "nb_inplace_and", type, func -16, "nb_inplace_floor_divide", type, func -17, "nb_inplace_lshift", type, func -18, "nb_inplace_multiply", type, func -19, "nb_inplace_or", type, func -20, "nb_inplace_power", type, func -21, "nb_inplace_remainder", type, func -22, "nb_inplace_rshift", type, func -23, "nb_inplace_subtract", type, func -24, "nb_inplace_true_divide", type, func -25, "nb_inplace_xor", type, func -26, "nb_int", type, func -27, "nb_invert", type, func -28, "nb_lshift", type, func -29, "nb_multiply", type, func -30, "nb_negative", type, func -31, "nb_or", type, func -32, "nb_positive", type, func -33, "nb_power", type, func -34, "nb_remainder", type, func -35, "nb_rshift", type, func -36, "nb_subtract", type, func -37, "nb_true_divide", type, func -38, "nb_xor", type, func -39, "sq_ass_item", type, func -40, "sq_concat", type, func -41, "sq_contains", type, func -42, "sq_inplace_concat", type, func -43, "sq_inplace_repeat", type, func -44, "sq_item", type, func -45, "sq_length", type, func -46, "sq_repeat", type, func -47, "tp_alloc", type, func -48, "tp_base", type, ptr -49, "tp_bases", type, ptr -50, "tp_call", type, func -51, "tp_clear", type, func -52, "tp_dealloc", type, func -53, "tp_del", type, func -54, "tp_descr_get", type, func -55, "tp_descr_set", type, func -56, "tp_doc", type, ptr, null=allow, duplicates=no -57, "tp_getattr", type, func -58, "tp_getattro", type, func -59, "tp_hash", type, func -60, "tp_init", type, func -61, "tp_is_gc", type, func -62, "tp_iter", type, func -63, "tp_iternext", type, func -64, "tp_methods", type, ptr, duplicates=no -65, "tp_new", type, func -66, "tp_repr", type, func -67, "tp_richcompare", type, func -68, "tp_setattr", type, func -69, "tp_setattro", type, func -70, "tp_str", type, func -71, "tp_traverse", type, func -72, "tp_members", type, ptr, duplicates=no -73, "tp_getset", type, ptr, duplicates=no -74, "tp_free", type, func -75, "nb_matrix_multiply", type, func -76, "nb_inplace_matrix_multiply", type, func -77, "am_await", type, func -78, "am_aiter", type, func -79, "am_anext", type, func -80, "tp_finalize", type, func -81, "am_send", type, func -82, "tp_vectorcall", type, func -83, "tp_token", type, ptr, table=ht, field=ht_token, null=allow, duplicates=no -84, "mod_create", mod, func, compat=1, duplicates=no -85, "mod_exec", mod, func, compat=2, duplicates=yes -86, "mod_multiple_interpreters", mod, uint64, compat=3, duplicates=no -87, "mod_gil", mod, uint64, compat=4, duplicates=no -88, "bf_getbuffer", type, func, compat=1 -89, "bf_releasebuffer", type, func, compat=2 -90, "mp_ass_subscript", type, func, compat=3 -91, "mp_length", type, func, compat=4 -92, "slot_subslots", slot, ptr, subslots=true, null=allow -93, "tp_slots", type, ptr, virtual=true, subslots=true, null=allow -94, "mod_slots", mod, ptr, subslots=true, null=allow -95, "tp_name", type, ptr, virtual=true, null=reject, duplicates=no, is_name=true -96, "tp_basicsize", type, size, virtual=true, duplicates=no -97, "tp_extra_basicsize", type, size, virtual=true, duplicates=no -98, "tp_itemsize", type, size, virtual=true, duplicates=no -99, "tp_flags", type, uint64, virtual=true, duplicates=no -100, "mod_name", mod, ptr, null=reject, duplicates=no, is_name=true -101, "mod_doc", mod, ptr, null=reject, duplicates=no -102, "mod_state_size", mod, size, duplicates=no -103, "mod_methods", mod, ptr, null=reject, duplicates=no -104, "mod_state_traverse", mod, func, null=reject, duplicates=no -105, "mod_state_clear", mod, func, null=reject, duplicates=no -106, "mod_state_free", mod, func, null=reject, duplicates=no -107, "tp_metaclass", type, ptr, virtual=true, null=reject, duplicates=no -108, "tp_module", type, ptr, virtual=true, null=reject, duplicates=no -109, "mod_abi", mod, ptr, null=reject, duplicates=yes -110, "mod_token", mod, ptr, null=reject, duplicates=no From 8dc5d885d12283f39597ec7ca549a418f16cbb9a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 17:49:46 +0200 Subject: [PATCH 19/36] Prettier generation --- Include/internal/pycore_slots.h | 2 +- Include/internal/pycore_slots_generated.h | 699 +++++++++++++++------- Makefile.pre.in | 4 +- Python/slots.toml | 13 +- Python/slots_generated.c | 2 +- Tools/build/generate_slots.py | 44 +- 6 files changed, 511 insertions(+), 253 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index 1b496ecea4853c..ef3da285f42c60 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -29,7 +29,7 @@ typedef enum _PySlot_PROBLEM_HANDLING { _PySlot_PROBLEM_REJECT, } _PySlot_PROBLEM_HANDLING; -PyAPI_DATA(char *) _PySlot_names[]; +PyAPI_DATA(const char *) _PySlot_names[]; #define _PySlot_MAX_NESTING 5 diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index 20adc1a0ebab43..878460db8109fa 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -8,13 +8,13 @@ _PySlot_resolve_type_slot(uint16_t slot_id) { switch (slot_id) { case 1: - return 88; + return Py_bf_getbuffer; case 2: - return 89; + return Py_bf_releasebuffer; case 3: - return 90; + return Py_mp_ass_subscript; case 4: - return 91; + return Py_mp_length; case 0: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: @@ -38,13 +38,13 @@ _PySlot_resolve_mod_slot(uint16_t slot_id) { switch (slot_id) { case 1: - return 84; + return Py_mod_create; case 2: - return 85; + return Py_mod_exec; case 3: - return 86; + return Py_mod_multiple_interpreters; case 4: - return 87; + return Py_mod_gil; case 0: case 84: case 85: case 86: case 87: case 92: case 94: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 109: case 110: @@ -58,142 +58,225 @@ static inline void* _PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id) { switch (slot_id) { - case 5: return (tp->tp_as_mapping) - ? tp->tp_as_mapping->mp_subscript : NULL; - case 6: return (tp->tp_as_number) - ? tp->tp_as_number->nb_absolute : NULL; - case 7: return (tp->tp_as_number) - ? tp->tp_as_number->nb_add : NULL; - case 8: return (tp->tp_as_number) - ? tp->tp_as_number->nb_and : NULL; - case 9: return (tp->tp_as_number) - ? tp->tp_as_number->nb_bool : NULL; - case 10: return (tp->tp_as_number) - ? tp->tp_as_number->nb_divmod : NULL; - case 11: return (tp->tp_as_number) - ? tp->tp_as_number->nb_float : NULL; - case 12: return (tp->tp_as_number) - ? tp->tp_as_number->nb_floor_divide : NULL; - case 13: return (tp->tp_as_number) - ? tp->tp_as_number->nb_index : NULL; - case 14: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_add : NULL; - case 15: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_and : NULL; - case 16: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_floor_divide : NULL; - case 17: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_lshift : NULL; - case 18: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_multiply : NULL; - case 19: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_or : NULL; - case 20: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_power : NULL; - case 21: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_remainder : NULL; - case 22: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_rshift : NULL; - case 23: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_subtract : NULL; - case 24: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_true_divide : NULL; - case 25: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_xor : NULL; - case 26: return (tp->tp_as_number) - ? tp->tp_as_number->nb_int : NULL; - case 27: return (tp->tp_as_number) - ? tp->tp_as_number->nb_invert : NULL; - case 28: return (tp->tp_as_number) - ? tp->tp_as_number->nb_lshift : NULL; - case 29: return (tp->tp_as_number) - ? tp->tp_as_number->nb_multiply : NULL; - case 30: return (tp->tp_as_number) - ? tp->tp_as_number->nb_negative : NULL; - case 31: return (tp->tp_as_number) - ? tp->tp_as_number->nb_or : NULL; - case 32: return (tp->tp_as_number) - ? tp->tp_as_number->nb_positive : NULL; - case 33: return (tp->tp_as_number) - ? tp->tp_as_number->nb_power : NULL; - case 34: return (tp->tp_as_number) - ? tp->tp_as_number->nb_remainder : NULL; - case 35: return (tp->tp_as_number) - ? tp->tp_as_number->nb_rshift : NULL; - case 36: return (tp->tp_as_number) - ? tp->tp_as_number->nb_subtract : NULL; - case 37: return (tp->tp_as_number) - ? tp->tp_as_number->nb_true_divide : NULL; - case 38: return (tp->tp_as_number) - ? tp->tp_as_number->nb_xor : NULL; - case 39: return (tp->tp_as_sequence) - ? tp->tp_as_sequence->sq_ass_item : NULL; - case 40: return (tp->tp_as_sequence) - ? tp->tp_as_sequence->sq_concat : NULL; - case 41: return (tp->tp_as_sequence) - ? tp->tp_as_sequence->sq_contains : NULL; - case 42: return (tp->tp_as_sequence) - ? tp->tp_as_sequence->sq_inplace_concat : NULL; - case 43: return (tp->tp_as_sequence) - ? tp->tp_as_sequence->sq_inplace_repeat : NULL; - case 44: return (tp->tp_as_sequence) - ? tp->tp_as_sequence->sq_item : NULL; - case 45: return (tp->tp_as_sequence) - ? tp->tp_as_sequence->sq_length : NULL; - case 46: return (tp->tp_as_sequence) - ? tp->tp_as_sequence->sq_repeat : NULL; - case 47: return (void*)tp->tp_alloc; - case 48: return (void*)tp->tp_base; - case 49: return (void*)tp->tp_bases; - case 50: return (void*)tp->tp_call; - case 51: return (void*)tp->tp_clear; - case 52: return (void*)tp->tp_dealloc; - case 53: return (void*)tp->tp_del; - case 54: return (void*)tp->tp_descr_get; - case 55: return (void*)tp->tp_descr_set; - case 56: return (void*)tp->tp_doc; - case 57: return (void*)tp->tp_getattr; - case 58: return (void*)tp->tp_getattro; - case 59: return (void*)tp->tp_hash; - case 60: return (void*)tp->tp_init; - case 61: return (void*)tp->tp_is_gc; - case 62: return (void*)tp->tp_iter; - case 63: return (void*)tp->tp_iternext; - case 64: return (void*)tp->tp_methods; - case 65: return (void*)tp->tp_new; - case 66: return (void*)tp->tp_repr; - case 67: return (void*)tp->tp_richcompare; - case 68: return (void*)tp->tp_setattr; - case 69: return (void*)tp->tp_setattro; - case 70: return (void*)tp->tp_str; - case 71: return (void*)tp->tp_traverse; - case 72: return (void*)tp->tp_members; - case 73: return (void*)tp->tp_getset; - case 74: return (void*)tp->tp_free; - case 75: return (tp->tp_as_number) - ? tp->tp_as_number->nb_matrix_multiply : NULL; - case 76: return (tp->tp_as_number) - ? tp->tp_as_number->nb_inplace_matrix_multiply : NULL; - case 77: return (tp->tp_as_async) - ? tp->tp_as_async->am_await : NULL; - case 78: return (tp->tp_as_async) - ? tp->tp_as_async->am_aiter : NULL; - case 79: return (tp->tp_as_async) - ? tp->tp_as_async->am_anext : NULL; - case 80: return (void*)tp->tp_finalize; - case 81: return (tp->tp_as_async) - ? tp->tp_as_async->am_send : NULL; - case 82: return (void*)tp->tp_vectorcall; - case 83: return (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) - ? ((PyHeapTypeObject*)tp)->ht_token : NULL; - case 88: return (tp->tp_as_buffer) - ? tp->tp_as_buffer->bf_getbuffer : NULL; - case 89: return (tp->tp_as_buffer) - ? tp->tp_as_buffer->bf_releasebuffer : NULL; - case 90: return (tp->tp_as_mapping) - ? tp->tp_as_mapping->mp_ass_subscript : NULL; - case 91: return (tp->tp_as_mapping) - ? tp->tp_as_mapping->mp_length : NULL; + case Py_mp_subscript: + if (!(tp->tp_as_mapping)) return NULL; + return (void*)tp->tp_as_mapping->mp_subscript; + case Py_nb_absolute: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_absolute; + case Py_nb_add: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_add; + case Py_nb_and: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_and; + case Py_nb_bool: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_bool; + case Py_nb_divmod: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_divmod; + case Py_nb_float: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_float; + case Py_nb_floor_divide: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_floor_divide; + case Py_nb_index: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_index; + case Py_nb_inplace_add: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_add; + case Py_nb_inplace_and: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_and; + case Py_nb_inplace_floor_divide: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_floor_divide; + case Py_nb_inplace_lshift: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_lshift; + case Py_nb_inplace_multiply: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_multiply; + case Py_nb_inplace_or: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_or; + case Py_nb_inplace_power: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_power; + case Py_nb_inplace_remainder: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_remainder; + case Py_nb_inplace_rshift: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_rshift; + case Py_nb_inplace_subtract: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_subtract; + case Py_nb_inplace_true_divide: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_true_divide; + case Py_nb_inplace_xor: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_xor; + case Py_nb_int: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_int; + case Py_nb_invert: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_invert; + case Py_nb_lshift: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_lshift; + case Py_nb_multiply: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_multiply; + case Py_nb_negative: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_negative; + case Py_nb_or: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_or; + case Py_nb_positive: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_positive; + case Py_nb_power: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_power; + case Py_nb_remainder: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_remainder; + case Py_nb_rshift: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_rshift; + case Py_nb_subtract: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_subtract; + case Py_nb_true_divide: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_true_divide; + case Py_nb_xor: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_xor; + case Py_sq_ass_item: + if (!(tp->tp_as_sequence)) return NULL; + return (void*)tp->tp_as_sequence->sq_ass_item; + case Py_sq_concat: + if (!(tp->tp_as_sequence)) return NULL; + return (void*)tp->tp_as_sequence->sq_concat; + case Py_sq_contains: + if (!(tp->tp_as_sequence)) return NULL; + return (void*)tp->tp_as_sequence->sq_contains; + case Py_sq_inplace_concat: + if (!(tp->tp_as_sequence)) return NULL; + return (void*)tp->tp_as_sequence->sq_inplace_concat; + case Py_sq_inplace_repeat: + if (!(tp->tp_as_sequence)) return NULL; + return (void*)tp->tp_as_sequence->sq_inplace_repeat; + case Py_sq_item: + if (!(tp->tp_as_sequence)) return NULL; + return (void*)tp->tp_as_sequence->sq_item; + case Py_sq_length: + if (!(tp->tp_as_sequence)) return NULL; + return (void*)tp->tp_as_sequence->sq_length; + case Py_sq_repeat: + if (!(tp->tp_as_sequence)) return NULL; + return (void*)tp->tp_as_sequence->sq_repeat; + case Py_tp_alloc: + return (void*)tp->tp_alloc; + case Py_tp_base: + return (void*)tp->tp_base; + case Py_tp_bases: + return (void*)tp->tp_bases; + case Py_tp_call: + return (void*)tp->tp_call; + case Py_tp_clear: + return (void*)tp->tp_clear; + case Py_tp_dealloc: + return (void*)tp->tp_dealloc; + case Py_tp_del: + return (void*)tp->tp_del; + case Py_tp_descr_get: + return (void*)tp->tp_descr_get; + case Py_tp_descr_set: + return (void*)tp->tp_descr_set; + case Py_tp_doc: + return (void*)tp->tp_doc; + case Py_tp_getattr: + return (void*)tp->tp_getattr; + case Py_tp_getattro: + return (void*)tp->tp_getattro; + case Py_tp_hash: + return (void*)tp->tp_hash; + case Py_tp_init: + return (void*)tp->tp_init; + case Py_tp_is_gc: + return (void*)tp->tp_is_gc; + case Py_tp_iter: + return (void*)tp->tp_iter; + case Py_tp_iternext: + return (void*)tp->tp_iternext; + case Py_tp_methods: + return (void*)tp->tp_methods; + case Py_tp_new: + return (void*)tp->tp_new; + case Py_tp_repr: + return (void*)tp->tp_repr; + case Py_tp_richcompare: + return (void*)tp->tp_richcompare; + case Py_tp_setattr: + return (void*)tp->tp_setattr; + case Py_tp_setattro: + return (void*)tp->tp_setattro; + case Py_tp_str: + return (void*)tp->tp_str; + case Py_tp_traverse: + return (void*)tp->tp_traverse; + case Py_tp_members: + return (void*)tp->tp_members; + case Py_tp_getset: + return (void*)tp->tp_getset; + case Py_tp_free: + return (void*)tp->tp_free; + case Py_nb_matrix_multiply: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_matrix_multiply; + case Py_nb_inplace_matrix_multiply: + if (!(tp->tp_as_number)) return NULL; + return (void*)tp->tp_as_number->nb_inplace_matrix_multiply; + case Py_am_await: + if (!(tp->tp_as_async)) return NULL; + return (void*)tp->tp_as_async->am_await; + case Py_am_aiter: + if (!(tp->tp_as_async)) return NULL; + return (void*)tp->tp_as_async->am_aiter; + case Py_am_anext: + if (!(tp->tp_as_async)) return NULL; + return (void*)tp->tp_as_async->am_anext; + case Py_tp_finalize: + return (void*)tp->tp_finalize; + case Py_am_send: + if (!(tp->tp_as_async)) return NULL; + return (void*)tp->tp_as_async->am_send; + case Py_tp_vectorcall: + return (void*)tp->tp_vectorcall; + case Py_tp_token: + if (!(tp->tp_flags & Py_TPFLAGS_HEAPTYPE)) return NULL; + return (void*)((PyHeapTypeObject*)tp)->ht_token; + case Py_bf_getbuffer: + if (!(tp->tp_as_buffer)) return NULL; + return (void*)tp->tp_as_buffer->bf_getbuffer; + case Py_bf_releasebuffer: + if (!(tp->tp_as_buffer)) return NULL; + return (void*)tp->tp_as_buffer->bf_releasebuffer; + case Py_mp_ass_subscript: + if (!(tp->tp_as_mapping)) return NULL; + return (void*)tp->tp_as_mapping->mp_ass_subscript; + case Py_mp_length: + if (!(tp->tp_as_mapping)) return NULL; + return (void*)tp->tp_as_mapping->mp_length; } _PySlot_err_bad_slot("PyType_GetSlot", slot_id); return NULL; @@ -203,88 +286,252 @@ static inline void _PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht, PySlot slot) { switch (slot.sl_id) { - case 5: ht->as_mapping.mp_subscript = (binaryfunc)slot.sl_func; break; - case 6: ht->as_number.nb_absolute = (unaryfunc)slot.sl_func; break; - case 7: ht->as_number.nb_add = (binaryfunc)slot.sl_func; break; - case 8: ht->as_number.nb_and = (binaryfunc)slot.sl_func; break; - case 9: ht->as_number.nb_bool = (inquiry)slot.sl_func; break; - case 10: ht->as_number.nb_divmod = (binaryfunc)slot.sl_func; break; - case 11: ht->as_number.nb_float = (unaryfunc)slot.sl_func; break; - case 12: ht->as_number.nb_floor_divide = (binaryfunc)slot.sl_func; break; - case 13: ht->as_number.nb_index = (unaryfunc)slot.sl_func; break; - case 14: ht->as_number.nb_inplace_add = (binaryfunc)slot.sl_func; break; - case 15: ht->as_number.nb_inplace_and = (binaryfunc)slot.sl_func; break; - case 16: ht->as_number.nb_inplace_floor_divide = (binaryfunc)slot.sl_func; break; - case 17: ht->as_number.nb_inplace_lshift = (binaryfunc)slot.sl_func; break; - case 18: ht->as_number.nb_inplace_multiply = (binaryfunc)slot.sl_func; break; - case 19: ht->as_number.nb_inplace_or = (binaryfunc)slot.sl_func; break; - case 20: ht->as_number.nb_inplace_power = (ternaryfunc)slot.sl_func; break; - case 21: ht->as_number.nb_inplace_remainder = (binaryfunc)slot.sl_func; break; - case 22: ht->as_number.nb_inplace_rshift = (binaryfunc)slot.sl_func; break; - case 23: ht->as_number.nb_inplace_subtract = (binaryfunc)slot.sl_func; break; - case 24: ht->as_number.nb_inplace_true_divide = (binaryfunc)slot.sl_func; break; - case 25: ht->as_number.nb_inplace_xor = (binaryfunc)slot.sl_func; break; - case 26: ht->as_number.nb_int = (unaryfunc)slot.sl_func; break; - case 27: ht->as_number.nb_invert = (unaryfunc)slot.sl_func; break; - case 28: ht->as_number.nb_lshift = (binaryfunc)slot.sl_func; break; - case 29: ht->as_number.nb_multiply = (binaryfunc)slot.sl_func; break; - case 30: ht->as_number.nb_negative = (unaryfunc)slot.sl_func; break; - case 31: ht->as_number.nb_or = (binaryfunc)slot.sl_func; break; - case 32: ht->as_number.nb_positive = (unaryfunc)slot.sl_func; break; - case 33: ht->as_number.nb_power = (ternaryfunc)slot.sl_func; break; - case 34: ht->as_number.nb_remainder = (binaryfunc)slot.sl_func; break; - case 35: ht->as_number.nb_rshift = (binaryfunc)slot.sl_func; break; - case 36: ht->as_number.nb_subtract = (binaryfunc)slot.sl_func; break; - case 37: ht->as_number.nb_true_divide = (binaryfunc)slot.sl_func; break; - case 38: ht->as_number.nb_xor = (binaryfunc)slot.sl_func; break; - case 39: ht->as_sequence.sq_ass_item = (ssizeobjargproc)slot.sl_func; break; - case 40: ht->as_sequence.sq_concat = (binaryfunc)slot.sl_func; break; - case 41: ht->as_sequence.sq_contains = (objobjproc)slot.sl_func; break; - case 42: ht->as_sequence.sq_inplace_concat = (binaryfunc)slot.sl_func; break; - case 43: ht->as_sequence.sq_inplace_repeat = (ssizeargfunc)slot.sl_func; break; - case 44: ht->as_sequence.sq_item = (ssizeargfunc)slot.sl_func; break; - case 45: ht->as_sequence.sq_length = (lenfunc)slot.sl_func; break; - case 46: ht->as_sequence.sq_repeat = (ssizeargfunc)slot.sl_func; break; - case 47: ht->ht_type.tp_alloc = (allocfunc)slot.sl_func; break; - case 48: ht->ht_type.tp_base = slot.sl_ptr; break; - case 49: ht->ht_type.tp_bases = slot.sl_ptr; break; - case 50: ht->ht_type.tp_call = (ternaryfunc)slot.sl_func; break; - case 51: ht->ht_type.tp_clear = (inquiry)slot.sl_func; break; - case 52: ht->ht_type.tp_dealloc = (destructor)slot.sl_func; break; - case 53: ht->ht_type.tp_del = (destructor)slot.sl_func; break; - case 54: ht->ht_type.tp_descr_get = (descrgetfunc)slot.sl_func; break; - case 55: ht->ht_type.tp_descr_set = (descrsetfunc)slot.sl_func; break; - case 56: ht->ht_type.tp_doc = slot.sl_ptr; break; - case 57: ht->ht_type.tp_getattr = (getattrfunc)slot.sl_func; break; - case 58: ht->ht_type.tp_getattro = (getattrofunc)slot.sl_func; break; - case 59: ht->ht_type.tp_hash = (hashfunc)slot.sl_func; break; - case 60: ht->ht_type.tp_init = (initproc)slot.sl_func; break; - case 61: ht->ht_type.tp_is_gc = (inquiry)slot.sl_func; break; - case 62: ht->ht_type.tp_iter = (getiterfunc)slot.sl_func; break; - case 63: ht->ht_type.tp_iternext = (iternextfunc)slot.sl_func; break; - case 64: ht->ht_type.tp_methods = slot.sl_ptr; break; - case 65: ht->ht_type.tp_new = (newfunc)slot.sl_func; break; - case 66: ht->ht_type.tp_repr = (reprfunc)slot.sl_func; break; - case 67: ht->ht_type.tp_richcompare = (richcmpfunc)slot.sl_func; break; - case 68: ht->ht_type.tp_setattr = (setattrfunc)slot.sl_func; break; - case 69: ht->ht_type.tp_setattro = (setattrofunc)slot.sl_func; break; - case 70: ht->ht_type.tp_str = (reprfunc)slot.sl_func; break; - case 71: ht->ht_type.tp_traverse = (traverseproc)slot.sl_func; break; - case 72: ht->ht_type.tp_members = slot.sl_ptr; break; - case 73: ht->ht_type.tp_getset = slot.sl_ptr; break; - case 74: ht->ht_type.tp_free = (freefunc)slot.sl_func; break; - case 75: ht->as_number.nb_matrix_multiply = (binaryfunc)slot.sl_func; break; - case 76: ht->as_number.nb_inplace_matrix_multiply = (binaryfunc)slot.sl_func; break; - case 77: ht->as_async.am_await = (unaryfunc)slot.sl_func; break; - case 78: ht->as_async.am_aiter = (unaryfunc)slot.sl_func; break; - case 79: ht->as_async.am_anext = (unaryfunc)slot.sl_func; break; - case 80: ht->ht_type.tp_finalize = (destructor)slot.sl_func; break; - case 81: ht->as_async.am_send = (sendfunc)slot.sl_func; break; - case 82: ht->ht_type.tp_vectorcall = (vectorcallfunc)slot.sl_func; break; - case 88: ht->as_buffer.bf_getbuffer = (getbufferproc)slot.sl_func; break; - case 89: ht->as_buffer.bf_releasebuffer = (releasebufferproc)slot.sl_func; break; - case 90: ht->as_mapping.mp_ass_subscript = (objobjargproc)slot.sl_func; break; - case 91: ht->as_mapping.mp_length = (lenfunc)slot.sl_func; break; + case Py_mp_subscript: + ht->as_mapping.mp_subscript = (binaryfunc)slot.sl_func; + break; + case Py_nb_absolute: + ht->as_number.nb_absolute = (unaryfunc)slot.sl_func; + break; + case Py_nb_add: + ht->as_number.nb_add = (binaryfunc)slot.sl_func; + break; + case Py_nb_and: + ht->as_number.nb_and = (binaryfunc)slot.sl_func; + break; + case Py_nb_bool: + ht->as_number.nb_bool = (inquiry)slot.sl_func; + break; + case Py_nb_divmod: + ht->as_number.nb_divmod = (binaryfunc)slot.sl_func; + break; + case Py_nb_float: + ht->as_number.nb_float = (unaryfunc)slot.sl_func; + break; + case Py_nb_floor_divide: + ht->as_number.nb_floor_divide = (binaryfunc)slot.sl_func; + break; + case Py_nb_index: + ht->as_number.nb_index = (unaryfunc)slot.sl_func; + break; + case Py_nb_inplace_add: + ht->as_number.nb_inplace_add = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_and: + ht->as_number.nb_inplace_and = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_floor_divide: + ht->as_number.nb_inplace_floor_divide = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_lshift: + ht->as_number.nb_inplace_lshift = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_multiply: + ht->as_number.nb_inplace_multiply = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_or: + ht->as_number.nb_inplace_or = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_power: + ht->as_number.nb_inplace_power = (ternaryfunc)slot.sl_func; + break; + case Py_nb_inplace_remainder: + ht->as_number.nb_inplace_remainder = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_rshift: + ht->as_number.nb_inplace_rshift = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_subtract: + ht->as_number.nb_inplace_subtract = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_true_divide: + ht->as_number.nb_inplace_true_divide = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_xor: + ht->as_number.nb_inplace_xor = (binaryfunc)slot.sl_func; + break; + case Py_nb_int: + ht->as_number.nb_int = (unaryfunc)slot.sl_func; + break; + case Py_nb_invert: + ht->as_number.nb_invert = (unaryfunc)slot.sl_func; + break; + case Py_nb_lshift: + ht->as_number.nb_lshift = (binaryfunc)slot.sl_func; + break; + case Py_nb_multiply: + ht->as_number.nb_multiply = (binaryfunc)slot.sl_func; + break; + case Py_nb_negative: + ht->as_number.nb_negative = (unaryfunc)slot.sl_func; + break; + case Py_nb_or: + ht->as_number.nb_or = (binaryfunc)slot.sl_func; + break; + case Py_nb_positive: + ht->as_number.nb_positive = (unaryfunc)slot.sl_func; + break; + case Py_nb_power: + ht->as_number.nb_power = (ternaryfunc)slot.sl_func; + break; + case Py_nb_remainder: + ht->as_number.nb_remainder = (binaryfunc)slot.sl_func; + break; + case Py_nb_rshift: + ht->as_number.nb_rshift = (binaryfunc)slot.sl_func; + break; + case Py_nb_subtract: + ht->as_number.nb_subtract = (binaryfunc)slot.sl_func; + break; + case Py_nb_true_divide: + ht->as_number.nb_true_divide = (binaryfunc)slot.sl_func; + break; + case Py_nb_xor: + ht->as_number.nb_xor = (binaryfunc)slot.sl_func; + break; + case Py_sq_ass_item: + ht->as_sequence.sq_ass_item = (ssizeobjargproc)slot.sl_func; + break; + case Py_sq_concat: + ht->as_sequence.sq_concat = (binaryfunc)slot.sl_func; + break; + case Py_sq_contains: + ht->as_sequence.sq_contains = (objobjproc)slot.sl_func; + break; + case Py_sq_inplace_concat: + ht->as_sequence.sq_inplace_concat = (binaryfunc)slot.sl_func; + break; + case Py_sq_inplace_repeat: + ht->as_sequence.sq_inplace_repeat = (ssizeargfunc)slot.sl_func; + break; + case Py_sq_item: + ht->as_sequence.sq_item = (ssizeargfunc)slot.sl_func; + break; + case Py_sq_length: + ht->as_sequence.sq_length = (lenfunc)slot.sl_func; + break; + case Py_sq_repeat: + ht->as_sequence.sq_repeat = (ssizeargfunc)slot.sl_func; + break; + case Py_tp_alloc: + ht->ht_type.tp_alloc = (allocfunc)slot.sl_func; + break; + case Py_tp_base: + ht->ht_type.tp_base = slot.sl_ptr; + break; + case Py_tp_bases: + ht->ht_type.tp_bases = slot.sl_ptr; + break; + case Py_tp_call: + ht->ht_type.tp_call = (ternaryfunc)slot.sl_func; + break; + case Py_tp_clear: + ht->ht_type.tp_clear = (inquiry)slot.sl_func; + break; + case Py_tp_dealloc: + ht->ht_type.tp_dealloc = (destructor)slot.sl_func; + break; + case Py_tp_del: + ht->ht_type.tp_del = (destructor)slot.sl_func; + break; + case Py_tp_descr_get: + ht->ht_type.tp_descr_get = (descrgetfunc)slot.sl_func; + break; + case Py_tp_descr_set: + ht->ht_type.tp_descr_set = (descrsetfunc)slot.sl_func; + break; + case Py_tp_doc: + ht->ht_type.tp_doc = slot.sl_ptr; + break; + case Py_tp_getattr: + ht->ht_type.tp_getattr = (getattrfunc)slot.sl_func; + break; + case Py_tp_getattro: + ht->ht_type.tp_getattro = (getattrofunc)slot.sl_func; + break; + case Py_tp_hash: + ht->ht_type.tp_hash = (hashfunc)slot.sl_func; + break; + case Py_tp_init: + ht->ht_type.tp_init = (initproc)slot.sl_func; + break; + case Py_tp_is_gc: + ht->ht_type.tp_is_gc = (inquiry)slot.sl_func; + break; + case Py_tp_iter: + ht->ht_type.tp_iter = (getiterfunc)slot.sl_func; + break; + case Py_tp_iternext: + ht->ht_type.tp_iternext = (iternextfunc)slot.sl_func; + break; + case Py_tp_methods: + ht->ht_type.tp_methods = slot.sl_ptr; + break; + case Py_tp_new: + ht->ht_type.tp_new = (newfunc)slot.sl_func; + break; + case Py_tp_repr: + ht->ht_type.tp_repr = (reprfunc)slot.sl_func; + break; + case Py_tp_richcompare: + ht->ht_type.tp_richcompare = (richcmpfunc)slot.sl_func; + break; + case Py_tp_setattr: + ht->ht_type.tp_setattr = (setattrfunc)slot.sl_func; + break; + case Py_tp_setattro: + ht->ht_type.tp_setattro = (setattrofunc)slot.sl_func; + break; + case Py_tp_str: + ht->ht_type.tp_str = (reprfunc)slot.sl_func; + break; + case Py_tp_traverse: + ht->ht_type.tp_traverse = (traverseproc)slot.sl_func; + break; + case Py_tp_members: + ht->ht_type.tp_members = slot.sl_ptr; + break; + case Py_tp_getset: + ht->ht_type.tp_getset = slot.sl_ptr; + break; + case Py_tp_free: + ht->ht_type.tp_free = (freefunc)slot.sl_func; + break; + case Py_nb_matrix_multiply: + ht->as_number.nb_matrix_multiply = (binaryfunc)slot.sl_func; + break; + case Py_nb_inplace_matrix_multiply: + ht->as_number.nb_inplace_matrix_multiply = (binaryfunc)slot.sl_func; + break; + case Py_am_await: + ht->as_async.am_await = (unaryfunc)slot.sl_func; + break; + case Py_am_aiter: + ht->as_async.am_aiter = (unaryfunc)slot.sl_func; + break; + case Py_am_anext: + ht->as_async.am_anext = (unaryfunc)slot.sl_func; + break; + case Py_tp_finalize: + ht->ht_type.tp_finalize = (destructor)slot.sl_func; + break; + case Py_am_send: + ht->as_async.am_send = (sendfunc)slot.sl_func; + break; + case Py_tp_vectorcall: + ht->ht_type.tp_vectorcall = (vectorcallfunc)slot.sl_func; + break; + case Py_bf_getbuffer: + ht->as_buffer.bf_getbuffer = (getbufferproc)slot.sl_func; + break; + case Py_bf_releasebuffer: + ht->as_buffer.bf_releasebuffer = (releasebufferproc)slot.sl_func; + break; + case Py_mp_ass_subscript: + ht->as_mapping.mp_ass_subscript = (objobjargproc)slot.sl_func; + break; + case Py_mp_length: + ht->as_mapping.mp_length = (lenfunc)slot.sl_func; + break; } } diff --git a/Makefile.pre.in b/Makefile.pre.in index c32b8af5da0d2a..3f4eff469b4b1e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2322,9 +2322,9 @@ regen-typeslots: $(MAKE) regen-slots .PHONY: regen-slots -regen-slots: +regen-slots: Python/slots.toml # Regenerate {Python,Include}/slots_generated.{c,h} - # from Python/slots.csv using Tools/build/generate_slots.py + # from Python/slots.toml using Tools/build/generate_slots.py $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_slots.py \ --generate-all diff --git a/Python/slots.toml b/Python/slots.toml index a1d709c1ba3ff1..188df8eab3c34d 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -1,14 +1,14 @@ # This file lists all PySlot values # This should only be used as input to Tools/build/generate_slots.py, -# its format can change at any time (e.g. we can rename it to slots.csv) +# its format can change at any time (e.g. we can switch to slots.csv) # Entries: # name: name of the slot # kind: -# - 'type', 'mod': slots to create a particular object +# - 'type', 'mod': slots to create a particular kind of object # - 'slot': special slots applicable to any kind of object -# - 'compat': old IDs that nneed to be resolved -# dtype: datat type (tag for the union of sl_ptr, sl_size, etc.) +# - 'compat': old IDs that need to be resolved +# dtype: data type (tag for the union of sl_ptr, sl_size, etc.) # equivalents: for 'compat' slots; the slots to resolve to # is_type_field: slot that corresponds to a field in the type object (or in # an array like PyNumberMethods). @@ -19,7 +19,9 @@ # - 'deprecated': issue a deprecation warning. Don't use for new slots. # (typically, the problem was disallowed in docs, but allowed in practice) # - 'reject': raise error -# The default is 'reject' (except NULL for non-pointer slots) +# The default for duplicate slots is 'reject' +# The default for NULLs is 'reject' for pointer slots; 'allow' for +# non-pointer ones [0] @@ -827,4 +829,3 @@ duplicates = 'allow' name = 'Py_mod_token' kind = 'mod' dtype = 'ptr' - diff --git a/Python/slots_generated.c b/Python/slots_generated.c index f1c369f3a22bba..ccd228c7066ef6 100644 --- a/Python/slots_generated.c +++ b/Python/slots_generated.c @@ -3,7 +3,7 @@ #include "Python.h" #include "pycore_slots.h" // _PySlot_names -char *_PySlot_names[] = { +const char *_PySlot_names[] = { "Py_slot_end", "Py_bf_getbuffer/Py_mod_create", "Py_bf_releasebuffer/Py_mod_exec", "Py_mp_ass_subscript/Py_mod_multiple_interpreters", diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index 61eaeb4e390285..04b00758fa88d5 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -1,7 +1,9 @@ #!/usr/bin/python -"""Generate type/module slot files. +"""Generate type/module slot files """ +# See the input file (Python/slots.toml) for a description of its format. + import io import sys import json @@ -21,8 +23,6 @@ DEFAULT_PRIVATE_HEADER_PATH = INCLUDE_PATH / 'internal/pycore_slots_generated.h' DEFAULT_C_PATH = REPO_ROOT / 'Python/slots_generated.c' -FIRST_3_15_SLOT = 83 - TABLES = { 'tp': 'ht_type', 'am': 'as_async', @@ -68,19 +68,28 @@ def is_type_field(self): @functools.cached_property def type_field(self): + assert self.is_type_field return self._data.get('field', self.name.removeprefix('Py_')) @functools.cached_property def type_table_ident(self): + assert self.is_type_field return self._data.get('table', self.type_field[:2]) @functools.cached_property def duplicate_handling(self): - return self._data.get('duplicates') + return self._data.get('duplicates', 'reject') @functools.cached_property def null_handling(self): - return self._data.get('nulls') + try: + return self._data['nulls'] + except KeyError: + if self.kind == 'compat': + return 'allow' + if self.dtype in {'ptr', 'func'}: + return 'reject' + return 'allow' def parse_slots(file): @@ -100,6 +109,7 @@ def parse_slots(file): class CWriter: """Simple helper for generating C code""" + def __init__(self, file): self.file = file self.indent = '' @@ -108,7 +118,7 @@ def __init__(self, file): def out(self, *args, **kwargs): """print args to the file, with current indent at the start""" - self.file.write(self.indent) + print(self.indent, end='', file=self.file) print(*args, file=self.file, **kwargs) __call__ = out @@ -190,7 +200,7 @@ def add_case(slot): if slot.kind == 'compat': new_slot = slots_by_name[slot.equivalents[kind]] out(f'case {slot.id}:') - out(f' return {new_slot.id};') + out(f' return {new_slot.name};') elif slot.kind in {kind, 'slot'}: good_slots.append(f'case {slot.id}:') out.spam(good_slots) @@ -207,7 +217,8 @@ def add_case(slot): field = slot.type_field table_ident = slot.type_table_ident if table_ident == 'tp': - out(f'case {slot.id}: return (void*)tp->{field};') + out(f'case {slot.name}:') + out(f' return (void*)tp->{field};') else: if table_ident == 'ht': cond = 'tp->tp_flags & Py_TPFLAGS_HEAPTYPE' @@ -216,8 +227,9 @@ def add_case(slot): table = TABLES[table_ident] cond = f'tp->tp_{table}' val = f'tp->tp_{table}->{field}' - out(f'case {slot.id}: return ({cond})') - out(f' ? {val} : NULL;') + out(f'case {slot.name}:') + out(f' if (!({cond})) return NULL;') + out(f' return (void*){val};') out(f'_PySlot_err_bad_slot("PyType_GetSlot", slot_id);') out(f'return NULL;') out() @@ -237,11 +249,9 @@ def add_case(slot): functype = f'({slot.functype})' else: functype = '' - out( - f'case {slot.id}:', - f'ht->{table}.{field} = {functype}slot.sl_{slot.dtype};', - f'break;' - ) + out(f'case {slot.name}:') + out(f' ht->{table}.{field} = {functype}slot.sl_{slot.dtype};') + out(f' break;') out() out(f'static inline _PySlot_DTYPE') out(f'_PySlot_get_dtype(uint16_t slot_id)') @@ -261,7 +271,7 @@ def add_case(slot): with out.block('switch (slot_id)'): results = collections.defaultdict(list) for slot in slots: - handling = slot.duplicate_handling or 'reject' + handling = slot.duplicate_handling results[handling.upper()].append(f'case {slot.id}:') results.pop('REJECT') for handling, cases in results.items(): @@ -298,7 +308,7 @@ def write_c(f, slots): out('#include "Python.h"') out('#include "pycore_slots.h" // _PySlot_names') out() - with out.block(f'char *_PySlot_names[] =', end=';'): + with out.block(f'const char *_PySlot_names[] =', end=';'): out.spam([f'"{slot.name}",' for slot in slots] + ['NULL']) From 796dd757ef7d6223b82e34ef1b47e7da255cb505 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 17:50:56 +0200 Subject: [PATCH 20/36] Tighten module creation --- Include/internal/pycore_slots_generated.h | 4 +- Objects/moduleobject.c | 110 +++++++--------------- Python/slots.toml | 2 +- 3 files changed, 36 insertions(+), 80 deletions(-) diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index 878460db8109fa..c46640165a9f29 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -690,8 +690,8 @@ _PySlot_get_null_handling(uint16_t slot_id) case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: - case 78: case 79: case 80: case 81: case 82: case 84: case 85: case 88: - case 89: case 90: case 91: + case 78: case 79: case 80: case 81: case 82: case 84: case 88: case 89: + case 90: case 91: return _PySlot_PROBLEM_DEPRECATED; default: return _PySlot_PROBLEM_REJECT; diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 26dafa037f63dd..72894cd3988fda 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -446,62 +446,6 @@ module_from_slots_and_spec( } it.name = name; - // Macro to copy a non-NULL, non-repeatable slot. -#define COPY_NONNULL_SLOT(TYPE, SL_MEMBER, DEST) \ - do { \ - if (!(TYPE)(it.current.SL_MEMBER)) { \ - PyErr_Format( \ - PyExc_SystemError, \ - "module %s: %s must not be NULL", \ - name, _PySlot_GetName(it.current.sl_id)); \ - goto error; \ - } \ - DEST = (TYPE)(it.current.SL_MEMBER); \ - } while (0); \ - ///////////////////////////////////////////////////////////////// - - // Macro to copy a non-NULL, non-repeatable slot to def_like. -#define COPY_DEF_SLOT(TYPE, SL_MEMBER, MEMBER) \ - do { \ - if (original_def) { \ - TYPE orig_value = (TYPE)original_def->MEMBER; \ - TYPE new_value = (TYPE)it.current.SL_MEMBER; \ - if (orig_value != new_value) { \ - PyErr_Format( \ - PyExc_SystemError, \ - "module %s: %s conflicts with " \ - "PyModuleDef." #MEMBER, \ - name, _PySlot_GetName(it.current.sl_id)); \ - goto error; \ - } \ - } \ - COPY_NONNULL_SLOT(TYPE, SL_MEMBER, (def_like->MEMBER)) \ - } while (0); \ - ///////////////////////////////////////////////////////////////// - - // Macro to copy a non-NULL, non-repeatable slot without a - // corresponding PyModuleDef member. - // DEST must be initially NULL (so we don't need a seen_* flag). -#define COPY_NONDEF_SLOT(TYPE, SL_MEMBER, DEST) \ - do { \ - if (DEST) { \ - PyErr_Format( \ - PyExc_SystemError, \ - "module %s has multiple %s slots", \ - name, _PySlot_GetName(it.current.sl_id)); \ - goto error; \ - } \ - COPY_NONNULL_SLOT(TYPE, SL_MEMBER, DEST) \ - } while (0); \ - ///////////////////////////////////////////////////////////////// - - // Define the whole common case -#define DEF_SLOT_CASE(SLOT, TYPE, SL_MEMBER, MEMBER) \ - case SLOT: \ - COPY_DEF_SLOT(TYPE, SL_MEMBER, MEMBER); \ - break; \ - ///////////////////////////////////////////////////////////////// - while (_PySlotIterator_Next(&it)) { switch (it.current.sl_id) { case Py_slot_invalid: @@ -518,34 +462,21 @@ module_from_slots_and_spec( name); goto error; } - if (!it.current.sl_func) { - PyErr_Format( - PyExc_SystemError, - "module %s: Py_mod_exec slot must not be NULL", - name); - goto error; - } - COPY_NONDEF_SLOT(_Py_modexecfunc, sl_func, m_exec); + m_exec = (_Py_modexecfunc)it.current.sl_func; } break; case Py_mod_multiple_interpreters: multiple_interpreters = it.current.sl_uint64; break; case Py_mod_gil: - requires_gil = (it.current.sl_uint64 != (uint64_t)Py_MOD_GIL_NOT_USED); + uint64_t val = it.current.sl_uint64; + requires_gil = (val != (uint64_t)Py_MOD_GIL_NOT_USED); break; case Py_mod_abi: if (PyABIInfo_Check(it.current.sl_ptr, name) < 0) { goto error; } break; - DEF_SLOT_CASE(Py_mod_name, char*, sl_ptr, m_name) - DEF_SLOT_CASE(Py_mod_doc, char*, sl_ptr, m_doc) - DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, sl_size, m_size) - DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, sl_ptr, m_methods) - DEF_SLOT_CASE(Py_mod_state_traverse, traverseproc, sl_func, m_traverse) - DEF_SLOT_CASE(Py_mod_state_clear, inquiry, sl_func, m_clear) - DEF_SLOT_CASE(Py_mod_state_free, freefunc, sl_func, m_free) case Py_mod_token: if (original_def && original_def != it.current.sl_ptr) { PyErr_Format( @@ -555,13 +486,38 @@ module_from_slots_and_spec( name); goto error; } - COPY_NONDEF_SLOT(void*, sl_ptr, token); + token = it.current.sl_ptr; break; - } + // Common case: Copy a PEP 793 slot to def_like +#define DEF_SLOT_CASE(SLOT, TYPE, SL_MEMBER, MEMBER) \ + case SLOT: \ + do { \ + if (original_def) { \ + TYPE orig_value = (TYPE)original_def->MEMBER; \ + TYPE new_value = (TYPE)it.current.SL_MEMBER; \ + if (orig_value != new_value) { \ + PyErr_Format( \ + PyExc_SystemError, \ + "module %s: %s conflicts with " \ + "PyModuleDef." #MEMBER, \ + name, _PySlot_GetName(it.current.sl_id)); \ + goto error; \ + } \ + } \ + (def_like->MEMBER) = (TYPE)it.current.SL_MEMBER; \ + } while (0); \ + break; \ + ///////////////////////////////////////////////////////////////// + DEF_SLOT_CASE(Py_mod_name, char*, sl_ptr, m_name) + DEF_SLOT_CASE(Py_mod_doc, char*, sl_ptr, m_doc) + DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, sl_size, m_size) + DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, sl_ptr, m_methods) + DEF_SLOT_CASE(Py_mod_state_traverse, + traverseproc, sl_func, m_traverse) + DEF_SLOT_CASE(Py_mod_state_clear, inquiry, sl_func, m_clear) + DEF_SLOT_CASE(Py_mod_state_free, freefunc, sl_func, m_free) #undef DEF_SLOT_CASE -#undef COPY_DEF_SLOT -#undef COPY_NONDEF_SLOT -#undef COPY_NONNULL_SLOT + } } if (!original_def && !_PySlotIterator_SawSlot(&it, Py_mod_abi)) { PyErr_Format( diff --git a/Python/slots.toml b/Python/slots.toml index 188df8eab3c34d..fa65324750079b 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -687,7 +687,7 @@ name = 'Py_mod_exec' kind = 'mod' dtype = 'func' duplicates = 'allow' -nulls = 'deprecated' +nulls = 'reject' [86] name = 'Py_mod_multiple_interpreters' From b942f10f5f47c07ad694824cd271d884b32b9a41 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 17:51:20 +0200 Subject: [PATCH 21/36] Fix math --- Include/internal/pycore_slots.h | 3 ++- Python/slots.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index ef3da285f42c60..c193db505e1103 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -46,13 +46,14 @@ typedef struct _PySlotIterator_state { bool ignoring_fallbacks :1; } _PySlotIterator_state; +#define SEEN_ENTRY_BITS (8 * sizeof(unsigned int)) /* State for a slots iterator */ typedef struct { _PySlotIterator_state *state; _PySlotIterator_state states[_PySlot_MAX_NESTING]; + unsigned int seen[_Py_slot_COUNT / SEEN_ENTRY_BITS + 1]; _PySlot_KIND kind; uint8_t recursion_level; - unsigned int seen[_Py_slot_COUNT / sizeof(unsigned int) + 1]; bool is_at_end :1; bool is_first_run :1; diff --git a/Python/slots.c b/Python/slots.c index 10e4d7c2dae8e4..f3ece1e533caab 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -81,13 +81,13 @@ _PySlotIterator_Rewind(_PySlotIterator *it, const void *slots) static Py_ssize_t seen_index(uint16_t id) { - return id / sizeof(unsigned int); + return id / SEEN_ENTRY_BITS; } static unsigned int seen_mask(uint16_t id) { - return ((unsigned int)1) << (id % sizeof(unsigned int)); + return ((unsigned int)1) << (id % SEEN_ENTRY_BITS); } bool From 6a53b4c40113714abe5d177986114f6e63847fbd Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 17:52:06 +0200 Subject: [PATCH 22/36] Remove PySlot_HAS_FALLBACK --- Include/internal/pycore_slots.h | 2 +- Include/slots.h | 5 ++--- Python/slots.c | 25 +------------------------ 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index c193db505e1103..2dbfa9bc79b160 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -43,10 +43,10 @@ typedef struct _PySlotIterator_state { const void *any_slot; }; _PySlot_KIND slot_struct_kind; - bool ignoring_fallbacks :1; } _PySlotIterator_state; #define SEEN_ENTRY_BITS (8 * sizeof(unsigned int)) + /* State for a slots iterator */ typedef struct { _PySlotIterator_state *state; diff --git a/Include/slots.h b/Include/slots.h index 8ff928f9bd4c03..65bfa068be6e6c 100644 --- a/Include/slots.h +++ b/Include/slots.h @@ -19,9 +19,8 @@ struct PySlot { }; #define PySlot_OPTIONAL 0x01 -#define PySlot_HAS_FALLBACK 0x02 -#define PySlot_STATIC 0x08 -#define PySlot_INTPTR 0x10 +#define PySlot_STATIC 0x02 +#define PySlot_INTPTR 0x04 #define Py_slot_invalid 0xffff diff --git a/Python/slots.c b/Python/slots.c index f3ece1e533caab..613d3b25553b7b 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -40,7 +40,6 @@ init_with_kind(_PySlotIterator *it, const void *slots, it->state = it->states; it->state->any_slot = slots; it->state->slot_struct_kind = slot_struct_kind; - it->state->ignoring_fallbacks = false; it->kind = result_kind; it->name = NULL; it->recursion_level = 0; @@ -141,7 +140,7 @@ _PySlotIterator_Next(_PySlotIterator *it) switch (it->state->slot_struct_kind) { case _PySlot_KIND_SLOT: { MSG("copying PySlot structure"); - it->current = *it->state->slot; /* struct copy */ + it->current = *it->state->slot; } break; case _PySlot_KIND_TYPE: { MSG("converting PyType_Slot structure"); @@ -166,16 +165,6 @@ _PySlotIterator_Next(_PySlotIterator *it) PySlot *const result = &it->current; uint16_t flags = result->sl_flags; - if (it->state->ignoring_fallbacks) { - if (!(it->state->slot->sl_flags & PySlot_HAS_FALLBACK)) { - MSG("stopping to ignore fallbacks"); - it->state->ignoring_fallbacks = false; - } - MSG("skipped (ignoring fallbacks)"); - advance(it); - continue; - } - MSG("slot %d, flags 0x%x, from %p", (int)result->sl_id, (unsigned)flags, it->state->slot); @@ -194,11 +183,6 @@ _PySlotIterator_Next(_PySlotIterator *it) (int)result->sl_id, _PySlot_GetName(result->sl_id)); if (result->sl_id == Py_slot_invalid) { - if (flags & (PySlot_OPTIONAL | PySlot_HAS_FALLBACK)) { - MSG("skipped (unknown/invalid slot)"); - advance(it); - continue; - } MSG("error (unknown/invalid slot)"); _PySlot_err_bad_slot(kind_name(it->kind), orig_id); goto error; @@ -206,12 +190,6 @@ _PySlotIterator_Next(_PySlotIterator *it) if (result->sl_id == Py_slot_end) { MSG("sentinel slot, flags %x", (unsigned)flags); if (flags & PySlot_OPTIONAL) { - MSG("skipped (optional sentinel)"); - advance(it); - continue; - } - const uint16_t bad_flags = PySlot_OPTIONAL | PySlot_HAS_FALLBACK; - if (flags & bad_flags) { MSG("error (bad flags on sentinel)"); PyErr_Format(PyExc_SystemError, "invalid flags for Py_slot_end: 0x%x", @@ -311,7 +289,6 @@ _PySlotIterator_Next(_PySlotIterator *it) } assert (result->sl_id > 0); assert (result->sl_id <= _Py_slot_COUNT); - assert (result->sl_id <= INT_MAX); if (it->is_first_run && handle_first_run(it) < 0) { goto error; } From f352558c4befa04529645315438e0123c57afa7e Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 17:52:20 +0200 Subject: [PATCH 23/36] Bump version --- Include/object.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/object.h b/Include/object.h index c7e2a9c6e0764d..32f5948932f004 100644 --- a/Include/object.h +++ b/Include/object.h @@ -364,7 +364,7 @@ PyAPI_FUNC(Py_ssize_t) PyType_GetTypeDataSize(PyTypeObject *cls); PyAPI_FUNC(int) PyType_GetBaseByToken(PyTypeObject *, void *, PyTypeObject **); #define Py_TP_USE_SPEC NULL #endif -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 14) +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15) PyAPI_FUNC(PyObject *) PyType_FromSlots(struct PySlot *); #endif From bd78c064e190b23f2f22419455e55272958058bf Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 17:52:37 +0200 Subject: [PATCH 24/36] Better comments --- Include/internal/pycore_slots.h | 7 ++++++- Objects/typeobject.c | 5 ++++- Python/slots.c | 10 ++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Include/internal/pycore_slots.h b/Include/internal/pycore_slots.h index 2dbfa9bc79b160..bc115a85fae25a 100644 --- a/Include/internal/pycore_slots.h +++ b/Include/internal/pycore_slots.h @@ -7,6 +7,7 @@ #include +/* Slot data type */ typedef enum _PySlot_DTYPE { _PySlot_DTYPE_VOID, _PySlot_DTYPE_FUNC, @@ -16,6 +17,10 @@ typedef enum _PySlot_DTYPE { _PySlot_DTYPE_UINT64, }_PySlot_DTYPE; +/* Slot kind, used to identify: + * - the thing the slot initializes (type/module/special) + * - the struct type (PySlot/PyType_Slot/PyModuleDef_Slot) + */ typedef enum _PySlot_KIND { _PySlot_KIND_TYPE, _PySlot_KIND_MOD, @@ -81,7 +86,7 @@ _PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots, /* Reset a *successfully exhausted* iterator to the beginning. * The *slots* must be the same as for the previous * `_PySlotIterator_InitWithKind` call. - * (Subsequent iterations skip some validation.) + * (Unlike creating a new iterator, we can skip some validation after Rewind.) */ PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, const void *slots); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 82d28a62afa3d5..092b55c6706ff3 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5352,7 +5352,10 @@ type_from_slots_or_spec( flags = it.current.sl_uint64; break; case Py_tp_members: - for (const PyMemberDef *memb = it.current.sl_ptr; memb->name != NULL; memb++) { + for (const PyMemberDef *memb = it.current.sl_ptr; + memb->name != NULL; + memb++) + { nmembers++; if (memb->flags & Py_RELATIVE_OFFSET) { if (memb->offset < 0) { diff --git a/Python/slots.c b/Python/slots.c index 613d3b25553b7b..eac42abbf18d53 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -8,7 +8,7 @@ #include // Iterating through a recursive structure doesn't look great in a debugger. -// Define this to get a trace on stderr. +// Flip the #if to 1 to get a trace on stderr. // (The messages can also serve as code comments.) #if 0 #define MSG(...) { \ @@ -27,7 +27,6 @@ kind_name(_PySlot_KIND kind) case _PySlot_KIND_SLOT: return "generic slot"; } Py_UNREACHABLE(); - return ""; } static void @@ -252,11 +251,6 @@ _PySlotIterator_Next(_PySlotIterator *it) } } - if (flags & PySlot_HAS_FALLBACK) { - MSG("starting to ignore fallbacks"); - it->state->ignoring_fallbacks = true; - } - advance(it); switch (_PySlot_get_dtype(result->sl_id)) { case _PySlot_DTYPE_VOID: @@ -289,7 +283,7 @@ _PySlotIterator_Next(_PySlotIterator *it) } assert (result->sl_id > 0); assert (result->sl_id <= _Py_slot_COUNT); - if (it->is_first_run && handle_first_run(it) < 0) { + if (it->is_first_run && (handle_first_run(it) < 0)) { goto error; } return result->sl_id != Py_slot_end; From 181df86576ed2f6277f44cde6ff8aac21240aa87 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 24 Apr 2026 18:07:38 +0200 Subject: [PATCH 25/36] Limit deprecations to new-style slots --- Python/slots.c | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/Python/slots.c b/Python/slots.c index eac42abbf18d53..c4ecf1f540d839 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -324,14 +324,19 @@ handle_first_run(_PySlotIterator *it) _PySlot_GetName(id)); return -1; } - MSG("deprecated NULL"); - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, - 1, - "NULL value in slot %s is deprecated", - _PySlot_GetName(id)) < 0) - { - return -1; + if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) { + MSG("deprecated NULL"); + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + 1, + "NULL value in slot %s is deprecated", + _PySlot_GetName(id)) < 0) + { + return -1; + } + } + else { + MSG("unwanted NULL in legacy struct"); } } } @@ -352,17 +357,22 @@ handle_first_run(_PySlotIterator *it) (int)it->current.sl_id); return -1; } - MSG("deprecated duplicate"); - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, - 0, - "%s%s%s has multiple %s (%d) slots. This is deprecated.", - kind_name(it->kind), - it->name ? " " : "", - it->name ? it->name : "", - _PySlot_GetName(id), - (int)it->current.sl_id) < 0) { - return -1; + if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) { + MSG("deprecated duplicate"); + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + 0, + "%s%s%s has multiple %s (%d) slots. This is deprecated.", + kind_name(it->kind), + it->name ? " " : "", + it->name ? it->name : "", + _PySlot_GetName(id), + (int)it->current.sl_id) < 0) { + return -1; + } + } + else { + MSG("unwanted duplicate in legacy struct"); } } } From 644ae4672e0d6a9545e867361804c1164873ec60 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Sun, 26 Apr 2026 02:17:28 +0200 Subject: [PATCH 26/36] Start on docs --- Doc/c-api/extension-modules.rst | 16 +- Doc/c-api/index.rst | 1 + Doc/c-api/module.rst | 123 ++-- Doc/c-api/slots.rst | 233 ++++++++ Doc/c-api/type.rst | 608 ++++++++++++++------ Doc/extending/first-extension-module.rst | 15 +- Doc/includes/capi-extension/spammodule-01.c | 12 +- Python/slots.toml | 2 +- 8 files changed, 755 insertions(+), 255 deletions(-) create mode 100644 Doc/c-api/slots.rst diff --git a/Doc/c-api/extension-modules.rst b/Doc/c-api/extension-modules.rst index 7bc04970b19503..b498ec89ac4bd7 100644 --- a/Doc/c-api/extension-modules.rst +++ b/Doc/c-api/extension-modules.rst @@ -38,7 +38,7 @@ Extension export hook The export hook must be an exported function with the following signature: -.. c:function:: PyModuleDef_Slot *PyModExport_modulename(void) +.. c:function:: PySlot *PyModExport_modulename(void) For modules with ASCII-only names, the :ref:`export hook ` must be named :samp:`PyModExport_{}`, @@ -57,7 +57,7 @@ Python's *punycode* encoding with hyphens replaced by underscores. In Python: suffix = b'U_' + name.encode('punycode').replace(b'-', b'_') return b'PyModExport' + suffix -The export hook returns an array of :c:type:`PyModuleDef_Slot` entries, +The export hook returns an array of :c:type:`PySlot` entries, terminated by an entry with a slot ID of ``0``. These slots describe how the module should be created and initialized. @@ -75,7 +75,7 @@ It is recommended to define the export hook function using a helper macro: Declare an extension module export hook. This macro: - * specifies the :c:expr:`PyModuleDef_Slot*` return type, + * specifies the :c:expr:`PySlot*` return type, * adds any special linkage declarations required by the platform, and * for C++, declares the function as ``extern "C"``. @@ -83,12 +83,12 @@ For example, a module called ``spam`` would be defined like this:: PyABIInfo_VAR(abi_info); - static PyModuleDef_Slot spam_slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "spam"}, - {Py_mod_init, spam_init_function}, + static PySlot spam_slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "spam"), + PySlot_DATA(Py_mod_init, spam_init_function), ... - {0, NULL}, + PySlot_END }; PyMODEXPORT_FUNC diff --git a/Doc/c-api/index.rst b/Doc/c-api/index.rst index eabe00f4004001..051f6fd765e850 100644 --- a/Doc/c-api/index.rst +++ b/Doc/c-api/index.rst @@ -18,6 +18,7 @@ document the API functions in detail. refcounting.rst exceptions.rst extension-modules.rst + slots.rst utilities.rst abstract.rst concrete.rst diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index b67ca671a2a118..7229ea6c69ee46 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -133,14 +133,16 @@ Module Objects unencodable filenames, use :c:func:`PyModule_GetFilenameObject` instead. +.. _c_module_slots: .. _pymoduledef_slot: Module definition ----------------- Modules created using the C API are typically defined using an -array of :dfn:`slots`. -The slots provide a "description" of how a module should be created. +array of :c:type:`PySlot` structs, which provides a "description" of how a +module should be created. +See :ref:`capi-slots` for more information on slots in general. .. versionchanged:: 3.15 @@ -158,30 +160,12 @@ Unless specified otherwise, the same slot ID may not be repeated in an array of slots. -.. c:type:: PyModuleDef_Slot - - .. c:member:: int slot - - A slot ID, chosen from the available ``Py_mod_*`` values explained below. - - An ID of 0 marks the end of a :c:type:`!PyModuleDef_Slot` array. - - .. c:member:: void* value - - Value of the slot, whose meaning depends on the slot ID. - - The value may not be NULL. - To leave a slot out, omit the :c:type:`PyModuleDef_Slot` entry entirely. - - .. versionadded:: 3.5 - - Metadata slots .............. .. c:macro:: Py_mod_name - :c:type:`Slot ID ` for the name of the new module, + :c:member:`Slot ID ` for the name of the new module, as a NUL-terminated UTF8-encoded ``const char *``. Note that modules are typically created using a @@ -196,7 +180,7 @@ Metadata slots .. c:macro:: Py_mod_doc - :c:type:`Slot ID ` for the docstring of the new + :c:type:`Slot ID ` for the docstring of the new module, as a NUL-terminated UTF8-encoded ``const char *``. Usually it is set to a variable created with :c:macro:`PyDoc_STRVAR`. @@ -211,7 +195,7 @@ Feature slots .. c:macro:: Py_mod_abi - :c:type:`Slot ID ` whose value points to + :c:member:`Slot ID ` whose value points to a :c:struct:`PyABIInfo` structure describing the ABI that the extension is using. @@ -222,8 +206,8 @@ Feature slots PyABIInfo_VAR(abi_info); - static PyModuleDef_Slot mymodule_slots[] = { - {Py_mod_abi, &abi_info}, + static PySlot mymodule_slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), ... }; @@ -237,7 +221,7 @@ Feature slots .. c:macro:: Py_mod_multiple_interpreters - :c:type:`Slot ID ` whose value is one of: + :c:member:`Slot ID ` whose value is one of: .. c:namespace:: NULL @@ -267,7 +251,7 @@ Feature slots .. c:macro:: Py_mod_gil - :c:type:`Slot ID ` whose value is one of: + :c:member:`Slot ID ` whose value is one of: .. c:namespace:: NULL @@ -296,7 +280,7 @@ Creation and initialization slots .. c:macro:: Py_mod_create - :c:type:`Slot ID ` for a function that creates + :c:member:`Slot ID ` for a function that creates the module object itself. The function must have the signature: @@ -346,7 +330,7 @@ Creation and initialization slots .. c:macro:: Py_mod_exec - :c:type:`Slot ID ` for a function that will + :c:member:`Slot ID ` for a function that will :dfn:`execute`, or initialize, the module. This function does the equivalent to executing the code of a Python module: typically, it adds classes and constants to the module. @@ -375,7 +359,7 @@ Creation and initialization slots .. c:macro:: Py_mod_methods - :c:type:`Slot ID ` for a table of module-level + :c:member:`Slot ID ` for a table of module-level functions, as an array of :c:type:`PyMethodDef` values suitable as the *functions* argument to :c:func:`PyModule_AddFunctions`. @@ -446,12 +430,12 @@ To retrieve the state from a given module, use the following functions: Slots for defining module state ............................... -The following :c:member:`PyModuleDef_Slot.slot` IDs are available for +The following :c:member:`slot IDs ` are available for defining the module state. .. c:macro:: Py_mod_state_size - :c:type:`Slot ID ` for the size of the module state, + :c:member:`Slot ID ` for the size of the module state, in bytes. Setting the value to a non-negative value means that the module can be @@ -468,7 +452,7 @@ defining the module state. .. c:macro:: Py_mod_state_traverse - :c:type:`Slot ID ` for a traversal function to call + :c:member:`Slot ID ` for a traversal function to call during GC traversal of the module object. The signature of the function, and meanings of the arguments, @@ -491,7 +475,7 @@ defining the module state. .. c:macro:: Py_mod_state_clear - :c:type:`Slot ID ` for a clear function to call + :c:member:`Slot ID ` for a clear function to call during GC clearing of the module object. The signature of the function is: @@ -519,7 +503,7 @@ defining the module state. .. c:macro:: Py_mod_state_free - :c:type:`Slot ID ` for a function to call during + :c:member:`Slot ID ` for a function to call during deallocation of the module object. The signature of the function is: @@ -578,7 +562,7 @@ A module's token -- and the *your_token* value to use in the above code -- is: .. c:macro:: Py_mod_token - :c:type:`Slot ID ` for the module token. + :c:member:`Slot ID ` for the module token. If you use this slot to set the module token (rather than rely on the default), you must ensure that: @@ -617,14 +601,14 @@ Creating extension modules dynamically The following functions may be used to create an extension module dynamically, rather than from an extension's :ref:`export hook `. -.. c:function:: PyObject *PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec) +.. c:function:: PyObject *PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec) Create a new module object, given an array of :ref:`slots ` and the :py:class:`~importlib.machinery.ModuleSpec` *spec*. - The *slots* argument must point to an array of :c:type:`PyModuleDef_Slot` + The *slots* argument must point to an array of :c:type:`PySlot` structures, terminated by an entry with slot ID of 0 - (typically written as ``{0}`` or ``{0, NULL}`` in C). + (typically written as :c:macro:`PySlot_END`). The array must include a :c:data:`Py_mod_abi` entry. The *spec* argument may be any ``ModuleSpec``-like object, as described @@ -640,10 +624,6 @@ rather than from an extension's :ref:`export hook `. must be called to fully initialize a module. (See also :ref:`multi-phase-initialization`.) - The *slots* array only needs to be valid for the duration of the - :c:func:`!PyModule_FromSlotsAndSpec` call. - In particular, it may be heap-allocated. - .. versionadded:: 3.15 .. c:function:: int PyModule_Exec(PyObject *module) @@ -738,6 +718,8 @@ remove it. .. c:member:: PyModuleDef_Slot* m_slots An array of additional slots, terminated by a ``{0, NULL}`` entry. + Note that the entries use the older :c:type:`PyModuleDef_Slot` structure, + rather than :c:type:`PySlot`. If the array contains slots corresponding to :c:type:`PyModuleDef` members, the values must match. @@ -752,6 +734,39 @@ remove it. .. c:member:: inquiry m_reload + .. c:namespace:: NULL + + .. c:type:: PyModuleDef_Slot + + Older structure defining additional slots of a module. + + Note that a :c:type:`!PyModuleDef_Slot` array may be included in a + :c:type:`!PySlot` array using :c:macro:`Py_mod_slots`, + and vice versa using :c:macro:`Py_slot_subslots`. + + Each :c:type:`!PyModuleDef_Slot` structure ``modslot`` is interpreted + as the following :c:type:`PySlot` structure:: + + (PySlot){ + .sl_id=modslot.slot, + .sl_flags=PySlot_INTPTR | sub_static, + .sl_ptr=modslot.value + } + + where ``sub_static`` is ``PySlot_STATIC`` if the slot requires + the flag (such as for :c:macro:`Py_mod_methods`), or if this flag + is present on the "parent" :c:macro:`!Py_mod_slots` slot (if any). + + .. c:member:: int slot + + Corresponds to :c:member:`PySlot.sl_id`. + + .. c:member:: void* value + + Corresponds to :c:member:`PySlot.sl_ptr`. + + .. versionadded:: 3.5 + .. c:member:: traverseproc m_traverse inquiry m_clear freefunc m_free @@ -774,6 +789,14 @@ remove it. The type of ``PyModuleDef`` objects. +.. c:macro:: Py_mod_slots + + :c:member:`Slot ID ` that works like + :c:macro:`Py_slot_subslots`, except it specifies an array of + :c:type:`PyModuleDef_Slot` structures. + + .. versionadded:: next + .. _moduledef-dynamic: The following API can be used to create modules from a :c:type:`!PyModuleDef` @@ -813,6 +836,10 @@ struct: .. versionadded:: 3.5 + .. soft-deprecated:: next + + Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code. + .. c:function:: PyObject * PyModule_FromDefAndSpec2(PyModuleDef *def, PyObject *spec, int module_api_version) Create a new module object, given the definition in *def* and the @@ -833,12 +860,22 @@ struct: .. versionadded:: 3.5 + .. soft-deprecated:: next + + Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code. + .. c:function:: int PyModule_ExecDef(PyObject *module, PyModuleDef *def) Process any execution slots (:c:data:`Py_mod_exec`) given in *def*. .. versionadded:: 3.5 + .. soft-deprecated:: next + + To run a module's own execution slots, prefer :c:func:`PyModule_Exec`, + which works on modules that were not created from a + :c:type:`PyModuleDef` structure. + .. c:macro:: PYTHON_API_VERSION PYTHON_API_STRING diff --git a/Doc/c-api/slots.rst b/Doc/c-api/slots.rst new file mode 100644 index 00000000000000..c1e2a87d9a76da --- /dev/null +++ b/Doc/c-api/slots.rst @@ -0,0 +1,233 @@ +.. highlight:: c + +.. _capi-slots: + +Definition slots +================ + +To define :ref:`module objects ` and +:ref:`classes `, you may use +an array of *slots* -- essentally, key-value pairs that describe features +of the object to create. +This decouples the data from the structures used at runtime, allowing CPython +-- and other Python C API implementations -- to update the stuctures without +breaking backwards compatibility. + +This section documents slots in general. +For object-specific behavior and slot values, see documentation for functions +that apply slots: + +- :c:func:`PyType_FromSlots` for types; +- :c:func:`PyModule_FromSlotsAndSpec` and :ref:`extension-export-hook` + for modules. + +When slots are passed to a function that applies them, the function will +not modify the slot array, nor any data it points to (recursively). +After the function is done, the caller is allowed to modify or deallocate +the array and any data it points to (recursively), except data +explicitly marked with :c:macro:`PySlot_STATIC`. + +Except when documented otherwise, multiple slots with the same ID +(:c:member:`~PySlot.sl_id`) may not occur in a single slots array. + +Entries of the slots array use the following structure: + +.. c:struct:: PySlot + + An entry in a slots array. Defined as: + + .. code-block:: c + + typedef struct { + uint16_t sl_id; + uint16_t sl_flags; + uint32_t _reserved; // must be 0 + union { + void *sl_ptr; + void (*sl_func)(void); + Py_ssize_t sl_size; + int64_t sl_int64; + uint64_t sl_uint64; + }; + } PySlot; + + .. c:member:: uint16_t sl_id + + A slot ID, chosen from: + + - ``Py_slot_*`` values documented in :ref:`pyslot-common-ids` below; + - ``Py_mod_*`` values for modules, as documented in :ref:`c_module_slots`; + - Values for types, as documented in :ref:`pyslot_type_slot_ids`. + + A :c:member:`!sl_id` of zero (:c:macro:`Py_slot_end`) marks the end of a + slots array. + + .. c:member:: void *sl_ptr + void (*sl_func)(void) + Py_ssize_t sl_size + int64_t sl_int64 + uint64_t sl_uint64 + + The data for the slot. + These members are part of an anonymous union; + the member to use depends on which data type is required by the slot ID: + data pointer, function pointer, size, signed or unsigned + integer, respectively. + + Except when documented otherwise for a specific slot ID, pointers + (that is :c:member:`!sl_ptr` and :c:member:`!sl_func`) may not be NULL. + + .. c:member:: uint16_t sl_flags + + Zero or more of the following flags, OR-ed together: + + .. c:namespace:: NULL + + .. c:macro:: PySlot_STATIC + + All data the slot points to is statically allocated and constant. + Thus, the interpreter does not need to copy the information. + + This flag is implied for function pointers. + + The flag applies even to data the slot points to “indirectly”, + except for slots nested via :c:macro:`Py_slot_subslots` which may + have their own :c:macro:`!PySlot_STATIC`` flags. + For example, if applied to a :c:macro:`Py_tp_members` slot that + points to an array of :c:type:`PyMemberDef` structures, + then the entire array, as well as the name and doc strings + in its elements, must be static and constant. + + .. c:macro:: PySlot_INTPTR + + The data is stored in ``sl_ptr``; CPython will cast it to + the appropriate type. + + This flag can simplify porting from the older :c:type:`PyType_Slot` + and :c:type:`PyModuleDef_Slot` structures. + + .. c:macro:: PySlot_OPTIONAL + + If the slot ID is unknown, the interpreter should ignore the + slot, rather than fail. + + For example, if Python 3.16 adds a new feature with a new slot ID, + the corresponding slot may be marked :c:macro:`!PySlot_OPTIONAL` + so that Python 3.15 ignores it. + + Note that the "optionality" only applies to unknown slot IDs. + This flag does not make Python skip invalid values of known slots. + + .. versionadded:: next + + +Convenience macros +------------------ + +.. c:macro:: PySlot_DATA(name, value) + PySlot_FUNC(name, value) + PySlot_SIZE(name, value) + PySlot_INT64(name, value) + PySlot_UINT64(name, value) + PySlot_STATIC_DATA(name, value) + + Convenience macros to define :c:type:`!PySlot` structures with + :c:member:`~PySlot.sl_id` and a particular union member set. + + :c:macro:`!PySlot_STATIC_DATA` sets the :c:macro:`PySlot_STATIC` flag; + others set no flags. + + Note that these macros use *designated initializers*, a C language feature + that C++ added in the 2000 version of the standard. + If your code needs to be compatible with C++11 or older, + use :c:macro:`PySlot_PTR` instead. + + Defined as:: + + #define PySlot_DATA(NAME, VALUE) \ + {.sl_id=NAME, .sl_ptr=(void*)(VALUE)} + + #define PySlot_FUNC(NAME, VALUE) \ + {.sl_id=NAME, .sl_func=(VALUE)} + + #define PySlot_SIZE(NAME, VALUE) \ + {.sl_id=NAME, .sl_size=(VALUE)} + + #define PySlot_INT64(NAME, VALUE) \ + {.sl_id=NAME, .sl_int64=(VALUE)} + + #define PySlot_UINT64(NAME, VALUE) \ + {.sl_id=NAME, .sl_uint64=(VALUE)} + + #define PySlot_STATIC_DATA(NAME, VALUE) \ + {.sl_id=NAME, .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)} + + .. versionadded:: next + +.. c:macro:: PySlot_END + + Convenience macros to mark the end of a :c:type:`!PySlot` array. + + Defined as:: + + #define PySlot_END {0} + + .. versionadded:: next + +.. c:macro:: PySlot_PTR(name, value) + PySlot_PTR_STATIC(name, value) + + Convenience macros for use in C++11-compatible code. + This version of C++ does not allow setting union members in literals, + these macros set the :c:macro:`PySlot_INTPTR` flag and cast the value + to ``(void*)``. + + Defined as:: + + #define PySlot_PTR(NAME, VALUE) \ + {NAME, PySlot_INTPTR, {0}, {(void*)(VALUE)}} + + #define PySlot_PTR_STATIC(NAME, VALUE) \ + {NAME, PySlot_INTPTR|Py_SLOT_STATIC, {0}, {(void*)(VALUE)}} + + .. versionadded:: next + +.. _pyslot-common-ids: + +Common slot IDs +--------------- + +The following slot IDs may be used in both type and module definitions. + +.. c:macro:: Py_slot_end + + Marks the end of a slots array. + Defined as zero. + + .. versionadded:: next + +.. c:macro:: Py_slot_subslots + + Nested slots array. + + The value (:c:member:`~PySlot.sl_ptr`) should point to an array of + :c:type:`PySlot` structures. + The slots in the array (up to but not including the zero-ID + terminator) will be treated as if they were inserted if the current + slot array, at the point :c:macro:`!Py_slot_subslots` appears. + + Slot nesting depth is limited to 5 levels. + This restriction may be lifted in the future. + + .. versionadded:: next + +.. c:macro:: Py_slot_invalid + + Reserved; will always be treated as an unknown slot ID. + Defined as ``UINT16_MAX`` (``0xFFFF``). + + When used with the :c:macro:`PySlot_OPTIONAL` flag, defines a slot with + no effect. + Without the flag, processing a slot with this ID will fail. + + .. versionadded:: next diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index c9bb5c3f09ac18..c3642960f079dd 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -3,7 +3,7 @@ .. _typeobjects: Type Objects ------------- +============ .. index:: pair: object; type @@ -384,36 +384,19 @@ Type Objects * :py:mod:`weakref` -Creating Heap-Allocated Types -............................. - -The following functions and structs are used to create -:ref:`heap types `. +.. _creating-heap-types: -.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) +Creating Heap-Allocated Types +----------------------------- - Create and return a :ref:`heap type ` from the *spec* - (see :c:macro:`Py_TPFLAGS_HEAPTYPE`). +The following function is used to create :ref:`heap types `: - The metaclass *metaclass* is used to construct the resulting type object. - When *metaclass* is ``NULL``, the metaclass is derived from *bases* - (or *Py_tp_base[s]* slots if *bases* is ``NULL``, see below). +.. c:function:: PyObject *PyType_FromSlots(const PySlot *slots) - Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not - supported, except if ``tp_new`` is ``NULL``. - - The *bases* argument can be used to specify base classes; it can either - be only one class or a tuple of classes. - If *bases* is ``NULL``, the :c:data:`Py_tp_bases` slot is used instead. - If that also is ``NULL``, the :c:data:`Py_tp_base` slot is used instead. - If that also is ``NULL``, the new type derives from :class:`object`. - - The *module* argument can be used to record the module in which the new - class is defined. It must be a module object or ``NULL``. - If not ``NULL``, the module is associated with the new type and can later be - retrieved with :c:func:`PyType_GetModule`. - The associated module is not inherited by subclasses; it must be specified - for each class individually. + Create and return a :ref:`heap type ` from a :c:type:`!PySlot` + array. + See :ref:`capi-slots` for general information on slots, + and :ref:`pyslot_type_slot_ids` for slots specific to type creation. This function calls :c:func:`PyType_Ready` on the new type. @@ -430,8 +413,367 @@ The following functions and structs are used to create * :py:meth:`~object.__init_subclass__` is not called on any bases. * :py:meth:`~object.__set_name__` is not called on new descriptors. + Slots are typically defined as a global static constant arrays. + However, sometimes slot values are not statically known at compile time. + For example, slots like :c:data:`Py_tp_bases`, :c:data:`Py_tp_metaclass` + and :c:data:`Py_tp_module` require live Python objects. + In this case, it is recommended to put such slots on the heap, + and use :c:macro:`Py_slot_subslots` to refer to an array of static slots. + For example:: + + static const PySlot my_slots[] = { + PySlot_DATA(Py_tp_name, "MyClass"), + PySlot_FUNC(Py_tp_repr, my_repr_func), + ... + PySlot_END + }; + + PyObject *make_my_class(PyObject *module) { + PySlot all_slots[] = { + PySlot_DATA(Py_slot_subslots, my_slots), + PySlot_DATA(Py_tp_module, module), + PySlot_END + }; + return PyType_FromSlots(all_slots); + } + +Heap types created without the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag may be +modified, for example by setting attributes on them, as with classes defined +in Python code. +Sometimes, such modifications are necessary to fully initialize a type, +but you may wish to prevent users from changing the type after +the initialisation is done: + +.. c:function:: int PyType_Freeze(PyTypeObject *type) + + Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag. + + All base classes of *type* must be immutable. + + On success, return ``0``. + On error, set an exception and return ``-1``. + + The type must not be used before it's made immutable. For example, type + instances must not be created before the type is made immutable. + + .. versionadded:: 3.14 + + +.. _pyslot_type_slot_ids: + +Type slot IDs +............. + +Most type slot IDs are named like the field names of the structures +:c:type:`PyTypeObject`, :c:type:`PyNumberMethods`, +:c:type:`PySequenceMethods`, :c:type:`PyMappingMethods` and +:c:type:`PyAsyncMethods` with an added ``Py_`` prefix. +For example, use: + +* :c:data:`Py_tp_dealloc` to set :c:member:`PyTypeObject.tp_dealloc` +* :c:data:`Py_nb_add` to set :c:member:`PyNumberMethods.nb_add` +* :c:data:`Py_sq_length` to set :c:member:`PySequenceMethods.sq_length` + +An additional slot is supported that does not correspond to a +:c:type:`!PyTypeObject` struct field: + +* :c:data:`Py_tp_token` + +The following “offset” fields cannot be set using :c:type:`PyType_Slot`: + +* :c:member:`~PyTypeObject.tp_weaklistoffset` + (use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible) +* :c:member:`~PyTypeObject.tp_dictoffset` + (use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible) +* :c:member:`~PyTypeObject.tp_vectorcall_offset` + (use ``"__vectorcalloffset__"`` in + :ref:`PyMemberDef `) + +If it is not possible to switch to a ``MANAGED`` flag (for example, +for vectorcall or to support Python older than 3.12), specify the +offset in :c:data:`Py_tp_members`. +See :ref:`PyMemberDef documentation ` +for details. + +The following internal fields cannot be set at all when creating a heap +type: + +* :c:member:`~PyTypeObject.tp_dict`, + :c:member:`~PyTypeObject.tp_mro`, + :c:member:`~PyTypeObject.tp_cache`, + :c:member:`~PyTypeObject.tp_subclasses`, and + :c:member:`~PyTypeObject.tp_weaklist`. + +The :c:data:`Py_tp_base` slot is equivalent to :c:data:`Py_tp_bases`; +both may be set either to a type or a tuple of types. +Specifying both is deprecated; in this case the value of :c:data:`Py_tp_bases` +is used. + +Slot values may not be ``NULL``, except for the following: + +* :c:data:`Py_tp_doc` +* :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC` + rather than ``NULL``) + +.. versionchanged:: 3.9 + Slots in :c:type:`PyBufferProcs` may be set in the unlimited API. + +.. versionchanged:: 3.11 + :c:member:`~PyBufferProcs.bf_getbuffer` and + :c:member:`~PyBufferProcs.bf_releasebuffer` are now available + under the :ref:`limited API `. + +.. versionchanged:: 3.14 + The field :c:member:`~PyTypeObject.tp_vectorcall` can now be set + using :c:data:`Py_tp_vectorcall`. See the field's documentation + for details. + +.. versionchanged:: next + + Specifying both :c:data:`Py_tp_base` and :c:data:`Py_tp_bases` + is deprecated. + +The following slots do not correspond to public fields in the +underlying structures: + +.. c:macro:: Py_tp_name + + :c:member:`Slot ID ` for the name of the type, + used to set :c:member:`PyTypeObject.tp_name`. + + This slot (or :c:func:`PyType_Spec.name`) is required to create a type. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_Spec.name` instead. + + .. impl-detail:: + + CPython processes slots in order. + It is recommended to put ``Py_tp_name`` at the beginning of the slots + array, so that if processing of a later slots fails, error messages + can include the class name. + + .. versionadded:: next + +.. c:macro:: Py_tp_basicsize + + :c:member:`Slot ID ` for the size of the instance in bytes. + It is used to set :c:member:`PyTypeObject.tp_basicsize`. + + The value must be positive. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_Spec.basicsize` instead. + + This slot may not be used with :c:func:`PyType_GetSlot`. + Use :c:member:`PyTypeObject.tp_basicsize` instead if needed, but be aware + that a type's size is often considered an implementation detail. + + .. versionadded:: next + +.. c:macro:: Py_tp_extra_basicsize + + :c:member:`Slot ID ` for type data size in bytes, that is, + how much space instances of the class need *in addition* + to space needed for superclasses. + + The value is used, together with the size of superclasses, to set + :c:member:`PyTypeObject.tp_basicsize`. + Python will insert padding as needed to meet + :c:member:`!tp_basicsize`'s alignment requirements. + + Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific + memory reserved this way. + + The value must be positive. + To specify that instances need no additional size (that is, size should be + inherited), omit the :c:macro:`!Py_tp_extra_basicsize` slot; do not set it + to zero. + + Specifying both :c:macro:`Py_tp_basicsize` and + :c:macro:`!Py_tp_extra_basicsize` is an error. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use negative :c:func:`PyType_Spec.basicsize` instead. + + This slot may not be used with :c:func:`PyType_GetSlot`. + + .. versionadded:: next + +.. c:macro:: Py_tp_itemsize + + :c:member:`Slot ID ` for the size of one element of a + variable-size type, in bytes. + Used to set :c:member:`PyTypeObject.tp_itemsize`. + See :c:member:`!tp_itemsize` documentation for caveats. + + The value must be positive. + + If this slot is missing, :c:member:`~PyTypeObject.tp_itemsize` is inherited. + Extending arbitrary variable-sized classes is dangerous, + since some types use a fixed offset for variable-sized memory, + which can then overlap fixed-sized memory used by a subclass. + To help prevent mistakes, inheriting ``itemsize`` is only possible + in the following situations: + + - The base is not variable-sized (its + :c:member:`~PyTypeObject.tp_itemsize`). + - The requested :c:member:`PyType_Spec.basicsize` is positive, + suggesting that the memory layout of the base class is known. + - The requested :c:member:`PyType_Spec.basicsize` is zero, + suggesting that the subclass does not access the instance's memory + directly. + - With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_Spec.itemsize` instead. + + This slot may not be used with :c:func:`PyType_GetSlot`. + + .. versionadded:: next + +.. c:macro:: Py_tp_flags + + :c:member:`Slot ID ` for type flags, used to set + :c:member:`PyTypeObject.tp_flags`. + + The ``Py_TPFLAGS_HEAPTYPE`` flag is not set, + :c:func:`PyType_FromSpecWithBases` sets it automatically. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use negative :c:func:`PyType_Spec.basicsize` instead. + + This slot may not be used with :c:func:`PyType_GetSlot`. + Use :c:func:`PyType_GetFlags` instead. + + .. versionadded:: next + +.. c:macro:: Py_tp_metaclass + + :c:member:`Slot ID ` for the metaclass used to construct + the resulting type object. + When omitted the metaclass is derived from bases + (:c:macro:`Py_tp_bases` or the *bases* argument of + :c:func:`PyType_FromMetaclass`). + + Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not + supported, except if ``tp_new`` is ``NULL``. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_FromMetaclass` to specify a metaclass with + :c:type:`!PyType_Spec`. + + This slot may not be used with :c:func:`PyType_GetSlot`. + Use :c:func:`Py_TYPE` on the type object instead. + + .. versionadded:: next + +.. c:macro:: Py_tp_module + + :c:member:`Slot ID ` for recording the module in which + the new class is defined. + + The value must be a module object. + The module is associated with the new type and can later be + retrieved with :c:func:`PyType_GetModule`. + The associated module is not inherited by subclasses; it must be specified + for each class individually. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_FromMetaclass` to specify a module with + :c:type:`!PyType_Spec`. + + This slot may not be used with :c:func:`PyType_GetSlot`. + Use :c:func:`PyType_GetModule` instead. + + .. versionadded:: next + +.. c:macro:: Py_tp_token + + :c:member:`Slot ID ` for recording a static memory layout ID + for a class. + + If the class is defined using a :c:type:`PyType_Spec`, and that spec is + statically allocated, the token can be set to the spec using the special + value :c:data:`Py_TP_USE_SPEC`: + + .. code-block:: c + + static PyType_Slot foo_slots[] = { + {Py_tp_token, Py_TP_USE_SPEC}, + + It can also be set to an arbitrary pointer, but you must ensure that: + + * The pointer outlives the class, so it's not reused for something else + while the class exists. + * It "belongs" to the extension module where the class lives, so it will not + clash with other extensions. + + Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has + a given token -- that is, check whether the memory layout is compatible. + + To get the token for a given class (without considering superclasses), + use :c:func:`PyType_GetSlot` with ``Py_tp_token``. + + .. versionadded:: 3.14 + + .. c:namespace:: NULL + + .. c:macro:: Py_TP_USE_SPEC + + Used as a value with :c:data:`Py_tp_token` to set the token to the + class's :c:type:`PyType_Spec`. + May only be used for classes defined using :c:type:`!PyType_Spec`. + + Expands to ``NULL``. + + .. versionadded:: 3.14 + +.. c:macro:: Py_tp_slots + + :c:member:`Slot ID ` that works like + :c:macro:`Py_slot_subslots`, except it specifies an array of + :c:type:`PyType_Slot` structures. + + .. versionadded:: next + + +Soft-deprecated API +................... + +The following functions are :term:`soft deprecated`. +They will continue to work, but new features will be added as slots for +:c:func:`PyType_FromSlots`, not as arguments to new ``PyType_From*`` functions. + +.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) + + Create and return a :ref:`heap type ` from the *spec* + (see :c:macro:`Py_TPFLAGS_HEAPTYPE`). + + A non-``NULL`` *metaclass* argument corresponds to the + :c:macro:`Py_tp_metaclass` slot. + + A non-``NULL`` *bases* argument corresponds to the :c:data:`Py_tp_bases` + slot, and takes precedence over :c:data:`Py_tp_bases` and + :c:data:`Py_tp_bases` slots. + Specifying more than one of *bases*, :c:data:`Py_tp_bases` and + :c:data:`Py_tp_bases` is deprecated. + + A non-``NULL`` *module* argument corresponds to the + :c:macro:`Py_tp_module` slot. + + This function calls :c:func:`PyType_Ready` on the new type. + + Note that this function does *not* fully match the behavior of + calling :py:class:`type() ` or using the :keyword:`class` statement. + See the note in :c:func:`PyType_FromSlots` documentation for details. + .. versionadded:: 3.12 + .. soft-deprecated:: next + + Prefer :c:func:`PyType_FromSlots` in new code. + .. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) @@ -459,6 +801,10 @@ The following functions and structs are used to create Creating classes whose metaclass overrides :c:member:`~PyTypeObject.tp_new` is no longer allowed. + .. soft-deprecated:: next + + Prefer :c:func:`PyType_FromSlots` in new code. + .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) @@ -481,6 +827,10 @@ The following functions and structs are used to create Creating classes whose metaclass overrides :c:member:`~PyTypeObject.tp_new` is no longer allowed. + .. soft-deprecated:: next + + Prefer :c:func:`PyType_FromSlots` in new code. + .. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec) @@ -502,20 +852,9 @@ The following functions and structs are used to create Creating classes whose metaclass overrides :c:member:`~PyTypeObject.tp_new` is no longer allowed. + .. soft-deprecated:: next -.. c:function:: int PyType_Freeze(PyTypeObject *type) - - Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag. - - All base classes of *type* must be immutable. - - On success, return ``0``. - On error, set an exception and return ``-1``. - - The type must not be used before it's made immutable. For example, type - instances must not be created before the type is made immutable. - - .. versionadded:: 3.14 + Prefer :c:func:`PyType_FromSlots` in new code. .. raw:: html @@ -528,27 +867,23 @@ The following functions and structs are used to create .. c:type:: PyType_Spec - Structure defining a type's behavior. + Structure defining a type's behavior, used for soft-deprecated functions + like :c:func:`PyType_FromMetaclass`. + + This structure contains several members that can instead be specified + as :ref:`slots ` for :c:func:`PyType_FromSlots`, + and an array of slot entries with a simpler structure. .. c:member:: const char* name - Name of the type, used to set :c:member:`PyTypeObject.tp_name`. + Corresponds to :c:macro:`Py_tp_name`. .. c:member:: int basicsize - If positive, specifies the size of the instance in bytes. - It is used to set :c:member:`PyTypeObject.tp_basicsize`. - - If zero, specifies that :c:member:`~PyTypeObject.tp_basicsize` - should be inherited. + If positive, corresponds to :c:macro:`Py_tp_basicsize`. - If negative, the absolute value specifies how much space instances of the - class need *in addition* to the superclass. - Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific - memory reserved this way. - For negative :c:member:`!basicsize`, Python will insert padding when - needed to meet :c:member:`~PyTypeObject.tp_basicsize`'s alignment - requirements. + If negative, corresponds to :c:macro:`Py_tp_extra_basicsize` set to + the absolute value. .. versionchanged:: 3.12 @@ -556,160 +891,53 @@ The following functions and structs are used to create .. c:member:: int itemsize - Size of one element of a variable-size type, in bytes. - Used to set :c:member:`PyTypeObject.tp_itemsize`. - See ``tp_itemsize`` documentation for caveats. - - If zero, :c:member:`~PyTypeObject.tp_itemsize` is inherited. - Extending arbitrary variable-sized classes is dangerous, - since some types use a fixed offset for variable-sized memory, - which can then overlap fixed-sized memory used by a subclass. - To help prevent mistakes, inheriting ``itemsize`` is only possible - in the following situations: - - - The base is not variable-sized (its - :c:member:`~PyTypeObject.tp_itemsize`). - - The requested :c:member:`PyType_Spec.basicsize` is positive, - suggesting that the memory layout of the base class is known. - - The requested :c:member:`PyType_Spec.basicsize` is zero, - suggesting that the subclass does not access the instance's memory - directly. - - With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag. + Corresponds to :c:macro:`Py_tp_itemsize`. .. c:member:: unsigned int flags - Type flags, used to set :c:member:`PyTypeObject.tp_flags`. - - If the ``Py_TPFLAGS_HEAPTYPE`` flag is not set, - :c:func:`PyType_FromSpecWithBases` sets it automatically. + Corresponds to :c:macro:`Py_tp_flags`. .. c:member:: PyType_Slot *slots - Array of :c:type:`PyType_Slot` structures. - Terminated by the special slot value ``{0, NULL}``. + Array of :c:type:`PyType_Slot` (not :c:type:`PySlot`) structures. + Terminated by the special slot value ``{0, NULL}``. Each slot ID should be specified at most once. -.. raw:: html - - - - - -.. c:type:: PyType_Slot - - Structure defining optional functionality of a type, containing a slot ID - and a value pointer. - - .. c:member:: int slot + .. c:namespace:: NULL - A slot ID. + .. raw:: html - Slot IDs are named like the field names of the structures - :c:type:`PyTypeObject`, :c:type:`PyNumberMethods`, - :c:type:`PySequenceMethods`, :c:type:`PyMappingMethods` and - :c:type:`PyAsyncMethods` with an added ``Py_`` prefix. - For example, use: + + + - * :c:data:`Py_tp_dealloc` to set :c:member:`PyTypeObject.tp_dealloc` - * :c:data:`Py_nb_add` to set :c:member:`PyNumberMethods.nb_add` - * :c:data:`Py_sq_length` to set :c:member:`PySequenceMethods.sq_length` + .. c:type:: PyType_Slot - An additional slot is supported that does not correspond to a - :c:type:`!PyTypeObject` struct field: + Structure defining optional functionality of a type, used for + soft-deprecated functions like :c:func:`PyType_FromMetaclass`. - * :c:data:`Py_tp_token` + Note that a :c:type:`!PyType_Slot` array may be included in a + :c:type:`!PySlot` array using :c:macro:`Py_tp_slots`, + and vice versa using :c:macro:`Py_slot_subslots`. - The following “offset” fields cannot be set using :c:type:`PyType_Slot`: + Each :c:type:`!PyType_Slot` structure ``tpslot`` is interpreted + as the following :c:type:`PySlot` structure:: - * :c:member:`~PyTypeObject.tp_weaklistoffset` - (use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible) - * :c:member:`~PyTypeObject.tp_dictoffset` - (use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible) - * :c:member:`~PyTypeObject.tp_vectorcall_offset` - (use ``"__vectorcalloffset__"`` in - :ref:`PyMemberDef `) + (PySlot){ + .sl_id=tpslot.slot, + .sl_flags=PySlot_INTPTR | sub_static, + .sl_ptr=tpslot.func + } - If it is not possible to switch to a ``MANAGED`` flag (for example, - for vectorcall or to support Python older than 3.12), specify the - offset in :c:data:`Py_tp_members`. - See :ref:`PyMemberDef documentation ` - for details. + where ``sub_static`` is ``PySlot_STATIC`` if the slot requires + the flag (such as for :c:macro:`Py_tp_methods`), or if this flag + is present on the "parent" :c:macro:`!Py_tp_slots` slot (if any). - The following internal fields cannot be set at all when creating a heap - type: + .. c:member:: int slot - * :c:member:`~PyTypeObject.tp_dict`, - :c:member:`~PyTypeObject.tp_mro`, - :c:member:`~PyTypeObject.tp_cache`, - :c:member:`~PyTypeObject.tp_subclasses`, and - :c:member:`~PyTypeObject.tp_weaklist`. + Corresponds to :c:member:`PySlot.sl_id`. - Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be - problematic on some platforms. - To avoid issues, use the *bases* argument of - :c:func:`PyType_FromSpecWithBases` instead. + .. c:member:: void *pfunc - .. versionchanged:: 3.9 - Slots in :c:type:`PyBufferProcs` may be set in the unlimited API. - - .. versionchanged:: 3.11 - :c:member:`~PyBufferProcs.bf_getbuffer` and - :c:member:`~PyBufferProcs.bf_releasebuffer` are now available - under the :ref:`limited API `. - - .. versionchanged:: 3.14 - The field :c:member:`~PyTypeObject.tp_vectorcall` can now be set - using :c:data:`Py_tp_vectorcall`. See the field's documentation - for details. - - .. c:member:: void *pfunc - - The desired value of the slot. In most cases, this is a pointer - to a function. - - *pfunc* values may not be ``NULL``, except for the following slots: - - * :c:data:`Py_tp_doc` - * :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC` - rather than ``NULL``) - - -.. c:macro:: Py_tp_token - - A :c:member:`~PyType_Slot.slot` that records a static memory layout ID - for a class. - - If the :c:type:`PyType_Spec` of the class is statically - allocated, the token can be set to the spec using the special value - :c:data:`Py_TP_USE_SPEC`: - - .. code-block:: c - - static PyType_Slot foo_slots[] = { - {Py_tp_token, Py_TP_USE_SPEC}, - - It can also be set to an arbitrary pointer, but you must ensure that: - - * The pointer outlives the class, so it's not reused for something else - while the class exists. - * It "belongs" to the extension module where the class lives, so it will not - clash with other extensions. - - Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has - a given token -- that is, check whether the memory layout is compatible. - - To get the token for a given class (without considering superclasses), - use :c:func:`PyType_GetSlot` with ``Py_tp_token``. - - .. versionadded:: 3.14 - - .. c:namespace:: NULL - - .. c:macro:: Py_TP_USE_SPEC - - Used as a value with :c:data:`Py_tp_token` to set the token to the - class's :c:type:`PyType_Spec`. - Expands to ``NULL``. - - .. versionadded:: 3.14 + Corresponds to :c:member:`PySlot.sl_ptr`. diff --git a/Doc/extending/first-extension-module.rst b/Doc/extending/first-extension-module.rst index cd755a98f7f5f4..73f9a6e0f1b6a6 100644 --- a/Doc/extending/first-extension-module.rst +++ b/Doc/extending/first-extension-module.rst @@ -260,18 +260,18 @@ create a module. Let's start with the basics: the name and docstring. The information should be defined in a ``static`` array of -:c:type:`PyModuleDef_Slot` entries, which are essentially key-value pairs. +:c:type:`PySlot` entries, which are essentially key-value pairs. Define this array just before your export hook: .. code-block:: c PyABIInfo_VAR(abi_info); - static PyModuleDef_Slot spam_slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "spam"}, - {Py_mod_doc, "A wonderful module with an example function"}, - {0, NULL} + static PySlot spam_slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "spam"), + PySlot_DATA(Py_mod_doc, "A wonderful module with an example function"), + PySlot_END }; The ``PyABIInfo_VAR(abi_info);`` macro and the :c:data:`Py_mod_abi` slot @@ -281,7 +281,8 @@ a different version of Python from crashing the interpreter. For both :c:data:`Py_mod_name` and :c:data:`Py_mod_doc`, the values are C strings -- that is, NUL-terminated, UTF-8 encoded byte arrays. -Note the zero-filled sentinel entry at the end. +Note ``PySlot_END`` sentinel entry at the end. +This marks the end of the array. If you forget it, you'll trigger undefined behavior. The array is defined as ``static`` -- that is, not visible outside this ``.c`` file. diff --git a/Doc/includes/capi-extension/spammodule-01.c b/Doc/includes/capi-extension/spammodule-01.c index 0bc34ef57445cb..6827c068912b61 100644 --- a/Doc/includes/capi-extension/spammodule-01.c +++ b/Doc/includes/capi-extension/spammodule-01.c @@ -37,12 +37,12 @@ static PyMethodDef spam_methods[] = { PyABIInfo_VAR(abi_info); -static PyModuleDef_Slot spam_slots[] = { - {Py_mod_abi, &abi_info}, - {Py_mod_name, "spam"}, - {Py_mod_doc, "A wonderful module with an example function"}, - {Py_mod_methods, spam_methods}, - {0, NULL} +static PySlot spam_slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "spam"), + PySlot_DATA(Py_mod_doc, "A wonderful module with an example function"), + PySlot_DATA(Py_mod_methods, spam_methods), + PySlot_END }; /// Export hook prototype diff --git a/Python/slots.toml b/Python/slots.toml index fa65324750079b..f9e0772d10163f 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -686,7 +686,7 @@ nulls = 'deprecated' name = 'Py_mod_exec' kind = 'mod' dtype = 'func' -duplicates = 'allow' +duplicates = 'allow' # only alowed in PyModuleDef.m_slots nulls = 'reject' [86] From 0433d1e10961286bf3b50cfaa674e5b9108e4575 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 08:50:01 +0200 Subject: [PATCH 27/36] Require some slots to be static --- Include/internal/pycore_slots_generated.h | 12 ++++++++++++ Python/slots.c | 16 +++++++++++++++- Python/slots.toml | 5 +++++ Tools/build/generate_slots.py | 14 ++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index c46640165a9f29..640230ab80b1b8 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -698,4 +698,16 @@ _PySlot_get_null_handling(uint16_t slot_id) } } +static inline bool +_PySlot_get_must_be_static(uint16_t slot_id) +{ + switch (slot_id) { + case Py_tp_methods: return true; + case Py_tp_members: return true; + case Py_tp_getset: return true; + case Py_mod_methods: return true; + } + return false; +} + #endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */ diff --git a/Python/slots.c b/Python/slots.c index c4ecf1f540d839..7b0442fda76d6a 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -70,7 +70,6 @@ _PySlotIterator_Rewind(_PySlotIterator *it, const void *slots) assert (it->is_at_end); assert (it->recursion_level == 0); assert (it->state == it->states); - assert (!it->state->ignoring_fallbacks); it->is_at_end = false; it->state->any_slot = slots; it->is_first_run = false; @@ -301,6 +300,21 @@ handle_first_run(_PySlotIterator *it) { int id = it->current.sl_id; + if (_PySlot_get_must_be_static(id)) { + if (!(it->current.sl_flags & PySlot_STATIC) + && (it->state->slot_struct_kind == _PySlot_KIND_SLOT)) + { + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + 1, + "%s must requires PySlot_STATIC", + _PySlot_GetName(id)) < 0) + { + return -1; + } + } + } + _PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id); if (null_handling != _PySlot_PROBLEM_ALLOW) { bool is_null = false; diff --git a/Python/slots.toml b/Python/slots.toml index f9e0772d10163f..5666ec07f5efb0 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -22,6 +22,7 @@ # The default for duplicate slots is 'reject' # The default for NULLs is 'reject' for pointer slots; 'allow' for # non-pointer ones +# must_be_static: true if slot needs the PySlot_STATIC flag (in PySlot struct) [0] @@ -523,6 +524,7 @@ is_type_field = true dtype = 'ptr' duplicates = 'deprecated' nulls = 'deprecated' +must_be_static = true [65] name = 'Py_tp_new' @@ -586,6 +588,7 @@ kind = 'type' is_type_field = true dtype = 'ptr' nulls = 'deprecated' +must_be_static = true [73] name = 'Py_tp_getset' @@ -594,6 +597,7 @@ is_type_field = true dtype = 'ptr' duplicates = 'deprecated' nulls = 'deprecated' +must_be_static = true [74] name = 'Py_tp_free' @@ -793,6 +797,7 @@ dtype = 'size' name = 'Py_mod_methods' kind = 'mod' dtype = 'ptr' +must_be_static = true [104] name = 'Py_mod_state_traverse' diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index 04b00758fa88d5..d916d9ff546bb5 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -91,6 +91,10 @@ def null_handling(self): return 'reject' return 'allow' + @functools.cached_property + def must_be_static(self): + return self._data.get('must_be_static', False) + def parse_slots(file): toml_contents = tomllib.load(file) @@ -300,6 +304,16 @@ def add_case(slot): out(f'default:') out(f' return _PySlot_PROBLEM_REJECT;') out() + out(f'static inline bool') + out(f'_PySlot_get_must_be_static(uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + cases = [] + for slot in slots: + if slot.must_be_static: + out(f'case {slot.name}: return true;') + out(f'return false;') + out() out(f'#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */') From b3131a3dcdf8dc0f635bf2f97e72228d0692dd7b Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 10:40:40 +0200 Subject: [PATCH 28/36] Add to Stable ABI --- Doc/c-api/slots.rst | 2 +- Doc/data/stable_abi.dat | 26 +++++++++++++++ Lib/test/test_stable_abi_ctypes.py | 1 + Misc/stable_abi.toml | 53 ++++++++++++++++++++++++++++++ PC/python3dll.c | 1 + 5 files changed, 82 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/slots.rst b/Doc/c-api/slots.rst index c1e2a87d9a76da..f38d745b9a24dc 100644 --- a/Doc/c-api/slots.rst +++ b/Doc/c-api/slots.rst @@ -32,7 +32,7 @@ Except when documented otherwise, multiple slots with the same ID Entries of the slots array use the following structure: -.. c:struct:: PySlot +.. c:type:: PySlot An entry in a slots array. Defined as: diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 4ae5e999f0bf21..dbfb86e5060f53 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -669,6 +669,19 @@ func,PySlice_GetIndicesEx,3.2,, func,PySlice_New,3.2,, data,PySlice_Type,3.2,, func,PySlice_Unpack,3.7,, +type,PySlot,3.15,,full-abi +macro,PySlot_DATA,3.15,, +macro,PySlot_END,3.15,, +macro,PySlot_FUNC,3.15,, +macro,PySlot_INT64,3.15,, +macro,PySlot_INTPTR,3.15,, +macro,PySlot_OPTIONAL,3.15,, +macro,PySlot_PTR,3.15,, +macro,PySlot_PTR_STATIC,3.15,, +macro,PySlot_SIZE,3.15,, +macro,PySlot_STATIC,3.15,, +macro,PySlot_STATIC_DATA,3.15,, +macro,PySlot_UINT64,3.15,, func,PyState_AddModule,3.3,, func,PyState_FindModule,3.2,, func,PyState_RemoveModule,3.3,, @@ -748,6 +761,7 @@ func,PyType_ClearCache,3.2,, func,PyType_Freeze,3.14,, func,PyType_FromMetaclass,3.12,, func,PyType_FromModuleAndSpec,3.10,, +func,PyType_FromSlots,3.15,, func,PyType_FromSpec,3.2,, func,PyType_FromSpecWithBases,3.3,, func,PyType_GenericAlloc,3.2,, @@ -1007,6 +1021,7 @@ macro,Py_mod_gil,3.13,, macro,Py_mod_methods,3.15,, macro,Py_mod_multiple_interpreters,3.12,, macro,Py_mod_name,3.15,, +macro,Py_mod_slots,3.15,, macro,Py_mod_state_clear,3.15,, macro,Py_mod_state_free,3.15,, macro,Py_mod_state_size,3.15,, @@ -1050,6 +1065,9 @@ macro,Py_nb_rshift,3.2,, macro,Py_nb_subtract,3.2,, macro,Py_nb_true_divide,3.2,, macro,Py_nb_xor,3.2,, +macro,Py_slot_end,3.15,, +macro,Py_slot_invalid,3.15,, +macro,Py_slot_subslots,3.15,, macro,Py_sq_ass_item,3.2,, macro,Py_sq_concat,3.2,, macro,Py_sq_contains,3.2,, @@ -1062,6 +1080,7 @@ type,Py_ssize_t,3.2,, macro,Py_tp_alloc,3.2,, macro,Py_tp_base,3.2,, macro,Py_tp_bases,3.2,, +macro,Py_tp_basicsize,3.15,, macro,Py_tp_call,3.2,, macro,Py_tp_clear,3.2,, macro,Py_tp_dealloc,3.2,, @@ -1069,7 +1088,9 @@ macro,Py_tp_del,3.2,, macro,Py_tp_descr_get,3.2,, macro,Py_tp_descr_set,3.2,, macro,Py_tp_doc,3.2,, +macro,Py_tp_extra_basicsize,3.15,, macro,Py_tp_finalize,3.5,, +macro,Py_tp_flags,3.15,, macro,Py_tp_free,3.2,, macro,Py_tp_getattr,3.2,, macro,Py_tp_getattro,3.2,, @@ -1077,15 +1098,20 @@ macro,Py_tp_getset,3.2,, macro,Py_tp_hash,3.2,, macro,Py_tp_init,3.2,, macro,Py_tp_is_gc,3.2,, +macro,Py_tp_itemsize,3.15,, macro,Py_tp_iter,3.2,, macro,Py_tp_iternext,3.2,, macro,Py_tp_members,3.2,, +macro,Py_tp_metaclass,3.15,, macro,Py_tp_methods,3.2,, +macro,Py_tp_module,3.15,, +macro,Py_tp_name,3.15,, macro,Py_tp_new,3.2,, macro,Py_tp_repr,3.2,, macro,Py_tp_richcompare,3.2,, macro,Py_tp_setattr,3.2,, macro,Py_tp_setattro,3.2,, +macro,Py_tp_slots,3.15,, macro,Py_tp_str,3.2,, macro,Py_tp_token,3.14,, macro,Py_tp_traverse,3.2,, diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index ed0868e0017fce..7907b240bc36f7 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -738,6 +738,7 @@ def test_windows_feature_macros(self): "PyType_Freeze", "PyType_FromMetaclass", "PyType_FromModuleAndSpec", + "PyType_FromSlots", "PyType_FromSpec", "PyType_FromSpecWithBases", "PyType_GenericAlloc", diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 101737a27829c9..5de4af4ce7bfce 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2689,6 +2689,59 @@ added = '3.15' [function.PyType_GetModuleByToken_DuringGC] added = '3.15' +[struct.PySlot] + added = '3.15' + struct_abi_kind = 'full-abi' +[function.PyType_FromSlots] + added = '3.15' +[const.PySlot_OPTIONAL] + added = '3.15' +[const.PySlot_STATIC] + added = '3.15' +[const.PySlot_INTPTR] + added = '3.15' +[macro.PySlot_DATA] + added = '3.15' +[macro.PySlot_FUNC] + added = '3.15' +[macro.PySlot_SIZE] + added = '3.15' +[macro.PySlot_INT64] + added = '3.15' +[macro.PySlot_UINT64] + added = '3.15' +[macro.PySlot_STATIC_DATA] + added = '3.15' +[macro.PySlot_END] + added = '3.15' +[macro.PySlot_PTR] + added = '3.15' +[macro.PySlot_PTR_STATIC] + added = '3.15' +[const.Py_slot_end] + added = '3.15' +[const.Py_slot_invalid] + added = '3.15' +[const.Py_slot_subslots] + added = '3.15' +[const.Py_tp_slots] + added = '3.15' +[const.Py_mod_slots] + added = '3.15' +[const.Py_tp_name] + added = '3.15' +[const.Py_tp_basicsize] + added = '3.15' +[const.Py_tp_extra_basicsize] + added = '3.15' +[const.Py_tp_itemsize] + added = '3.15' +[const.Py_tp_flags] + added = '3.15' +[const.Py_tp_metaclass] + added = '3.15' +[const.Py_tp_module] + added = '3.15' # PEP 757 import/export API. diff --git a/PC/python3dll.c b/PC/python3dll.c index abbe35c342c13e..7f9194d96d701f 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -676,6 +676,7 @@ EXPORT_FUNC(PyType_ClearCache) EXPORT_FUNC(PyType_Freeze) EXPORT_FUNC(PyType_FromMetaclass) EXPORT_FUNC(PyType_FromModuleAndSpec) +EXPORT_FUNC(PyType_FromSlots) EXPORT_FUNC(PyType_FromSpec) EXPORT_FUNC(PyType_FromSpecWithBases) EXPORT_FUNC(PyType_GenericAlloc) From b35230d80f398b982cefc0fc2b4da6d754d44fc4 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 10:41:43 +0200 Subject: [PATCH 29/36] blurb, versionadded, whatsnew --- Doc/c-api/slots.rst | 9 ++++- Doc/whatsnew/3.15.rst | 38 +++++++++++++++++++ ...-04-27-10-56-22.gh-issue-149044.TbOcUS.rst | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/C_API/2026-04-27-10-56-22.gh-issue-149044.TbOcUS.rst diff --git a/Doc/c-api/slots.rst b/Doc/c-api/slots.rst index f38d745b9a24dc..c65727a1796b8b 100644 --- a/Doc/c-api/slots.rst +++ b/Doc/c-api/slots.rst @@ -6,7 +6,7 @@ Definition slots ================ To define :ref:`module objects ` and -:ref:`classes `, you may use +:ref:`classes ` using the C API, you may use an array of *slots* -- essentally, key-value pairs that describe features of the object to create. This decouples the data from the structures used at runtime, allowing CPython @@ -30,6 +30,13 @@ explicitly marked with :c:macro:`PySlot_STATIC`. Except when documented otherwise, multiple slots with the same ID (:c:member:`~PySlot.sl_id`) may not occur in a single slots array. +.. versionadded:: next + + Slot arrays generalize an earlier way of defining objects: + using :c:type:`PyType_Spec` with :c:type:`PyType_Slot` for types, and + :c:type:`PyModuleDef` with :c:type:`PyModuleDef_Slot` for modules. + The earlier API is :term:`soft deprecated`; there are no plans to remove it. + Entries of the slots array use the following structure: .. c:type:: PySlot diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index dbdd5de01700a3..dda3bac8a61d23 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -84,6 +84,7 @@ Summary -- Release highlights * :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object ` * :pep:`803`: :ref:`Stable ABI for Free-Threaded Builds ` +* :pep:`820`: :ref:`PySlot: Unified slot system for the C API ` * :ref:`The JIT compiler has been significantly upgraded ` * :ref:`Improved error messages ` * :ref:`The official Windows 64-bit binaries now use the tail-calling interpreter @@ -1911,6 +1912,43 @@ New features It should only be used for debugging. (Contributed by Victor Stinner in :gh:`141070`.) +.. _whatsnew315-pyslot: + +* Implement :pep:`820`: ``PySlot`` -- Unified slot system for the C API. + See :ref:`capi-slots` for documentation. + + This adds: + + * The :c:type:`PySlot` struct; + * the :c:func:`PyType_FromSlots` function; + * new slot IDs: :c:macro:`Py_slot_end`, :c:macro:`Py_slot_invalid`; + :c:macro:`Py_slot_subslots`, :c:macro:`Py_tp_slots` + :c:macro:`Py_mod_slots`; + :c:macro:`Py_tp_name`, :c:macro:`Py_tp_basicsize`, + :c:macro:`Py_tp_extra_basicsize`, :c:macro:`Py_tp_itemsize`, + :c:macro:`Py_tp_flags`, :c:macro:`Py_tp_metaclass`, + :c:macro:`Py_tp_module`, :c:macro:`Py_tp_flags`; + * convenience macros: :c:macro:`PySlot_DATA`, :c:macro:`PySlot_FUNC`, + :c:macro:`PySlot_SIZE` :c:macro:`PySlot_INT64`, :c:macro:`PySlot_UINT64`, + :c:macro:`PySlot_STATIC_DATA`, :c:macro:`PySlot_END`, + :c:macro:`PySlot_PTR`, :c:macro:`PySlot_PTR_STATIC`. + + The :c:func:`PyModule_FromSlotsAndSpec` function and + ``PyModExport`` :ref:`module export hook ` also + use the new :c:type:`!PySlot` struct. + + These following functions are :term:`soft deprecated`: + + * :c:func:`PyType_FromSpec` + * :c:func:`PyType_FromSpecWithBases` + * :c:func:`PyType_FromModuleAndSpec` + * :c:func:`PyType_FromMetaclass` + * :c:func:`PyModule_FromDefAndSpec` + * :c:func:`PyModule_FromDefAndSpec2` + * :c:func:`PyModule_ExecDef` + + (Contributed by Petr Viktorin in :gh:`149044`.) + * Add :c:func:`PyUnstable_ThreadState_SetStackProtection` and :c:func:`PyUnstable_ThreadState_ResetStackProtection` functions to set the stack protection base address and stack protection size of a Python diff --git a/Misc/NEWS.d/next/C_API/2026-04-27-10-56-22.gh-issue-149044.TbOcUS.rst b/Misc/NEWS.d/next/C_API/2026-04-27-10-56-22.gh-issue-149044.TbOcUS.rst new file mode 100644 index 00000000000000..d7bb38f7cd7366 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-04-27-10-56-22.gh-issue-149044.TbOcUS.rst @@ -0,0 +1 @@ +Implement :pep:`820`: Unified slot system for the C API. From ae0ea2924c2f853ab8ce19fcd5892212f4bd462d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 12:09:21 +0200 Subject: [PATCH 30/36] Don't deprecate mixing the bases slots/args --- Doc/c-api/type.rst | 9 +-------- Objects/typeobject.c | 11 ----------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index c3642960f079dd..9d247135cecec2 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -506,7 +506,7 @@ type: The :c:data:`Py_tp_base` slot is equivalent to :c:data:`Py_tp_bases`; both may be set either to a type or a tuple of types. -Specifying both is deprecated; in this case the value of :c:data:`Py_tp_bases` +If both are specified, the value of :c:data:`Py_tp_bases` is used. Slot values may not be ``NULL``, except for the following: @@ -528,11 +528,6 @@ Slot values may not be ``NULL``, except for the following: using :c:data:`Py_tp_vectorcall`. See the field's documentation for details. -.. versionchanged:: next - - Specifying both :c:data:`Py_tp_base` and :c:data:`Py_tp_bases` - is deprecated. - The following slots do not correspond to public fields in the underlying structures: @@ -756,8 +751,6 @@ They will continue to work, but new features will be added as slots for A non-``NULL`` *bases* argument corresponds to the :c:data:`Py_tp_bases` slot, and takes precedence over :c:data:`Py_tp_bases` and :c:data:`Py_tp_bases` slots. - Specifying more than one of *bases*, :c:data:`Py_tp_bases` and - :c:data:`Py_tp_bases` is deprecated. A non-``NULL`` *module* argument corresponds to the :c:macro:`Py_tp_module` slot. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 092b55c6706ff3..2127b813e9fc09 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5490,17 +5490,6 @@ type_from_slots_or_spec( * (This is convoluted for backwards compatibility -- preserving priority * of the various ways to specify bases) */ - if ((bases_in ? 1 : 0) - + (_PySlotIterator_SawSlot(&it, Py_tp_bases) ? 1 : 0) - + (_PySlotIterator_SawSlot(&it, Py_tp_base) ? 1 : 0) - > 1) - { - if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "type %s specifies multiple of: bases argument, Py_tp_bases, " - "and Py_tp_base. This will become an error in Python 3.20.", - it.name)) - goto finally; - } if (!bases_in) { bases_in = bases_slot; } From 2b4be46968113e6bcb9a5cb8cc52f4e7ea292136 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 12:13:07 +0200 Subject: [PATCH 31/36] Add notes to stable_abi.toml --- Misc/stable_abi.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 5de4af4ce7bfce..bacaf106dd1b16 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -296,10 +296,12 @@ [const.Py_sq_inplace_repeat] added = '3.2' [const.Py_mp_length] + # Note: value changed in 3.15 (old value is still accepted) added = '3.2' [const.Py_mp_subscript] added = '3.2' [const.Py_mp_ass_subscript] + # Note: value changed in 3.15 (old value is still accepted) added = '3.2' [typedef.Py_uintptr_t] @@ -1914,8 +1916,10 @@ [data.PyModuleDef_Type] added = '3.5' [const.Py_mod_create] + # Note: value changed in 3.15 (old value is still accepted) added = '3.5' [const.Py_mod_exec] + # Note: value changed in 3.15 (old value is still accepted) added = '3.5' [struct.PyModuleDef_Slot] added = '3.5' @@ -2325,8 +2329,10 @@ [function.PyMemoryView_FromBuffer] added = '3.11' [const.Py_bf_getbuffer] + # Note: value changed in 3.15 (old value is still accepted) added = '3.11' [const.Py_bf_releasebuffer] + # Note: value changed in 3.15 (old value is still accepted) added = '3.11' # Constants for Py_buffer API added to this list in Python 3.11.1 (https://github.com/python/cpython/issues/98680) @@ -2463,6 +2469,7 @@ [const.Py_TPFLAGS_ITEMS_AT_END] added = '3.12' [const.Py_mod_multiple_interpreters] + # Note: value changed in 3.15 (old value is still accepted) added = '3.12' [function.PyImport_AddModuleRef] @@ -2544,6 +2551,7 @@ [function.PyEval_GetFrameLocals] added = '3.13' [const.Py_mod_gil] + # Note: value changed in 3.15 (old value is still accepted) added = '3.13' [function.Py_TYPE] From ac7277f6931ffbc319bc5ebc3e49d9b041833d7a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 15:11:51 +0200 Subject: [PATCH 32/36] Type tests --- Doc/c-api/slots.rst | 8 +- Doc/c-api/type.rst | 47 ++- Doc/c-api/typeobj.rst | 10 + Include/internal/pycore_slots_generated.h | 6 +- Lib/test/test_capi/test_slots.py | 189 +++++++++++ Modules/Setup.stdlib.in | 2 +- Modules/_testlimitedcapi.c | 3 + Modules/_testlimitedcapi/parts.h | 1 + Modules/_testlimitedcapi/slots.c | 379 ++++++++++++++++++++++ Objects/typeobject.c | 6 - PCbuild/_testlimitedcapi.vcxproj | 1 + PCbuild/_testlimitedcapi.vcxproj.filters | 1 + Python/slots.c | 17 +- Python/slots.toml | 2 +- 14 files changed, 631 insertions(+), 41 deletions(-) create mode 100644 Lib/test/test_capi/test_slots.py create mode 100644 Modules/_testlimitedcapi/slots.c diff --git a/Doc/c-api/slots.rst b/Doc/c-api/slots.rst index c65727a1796b8b..6461274b8e092e 100644 --- a/Doc/c-api/slots.rst +++ b/Doc/c-api/slots.rst @@ -118,7 +118,7 @@ Entries of the slots array use the following structure: If the slot ID is unknown, the interpreter should ignore the slot, rather than fail. - For example, if Python 3.16 adds a new feature with a new slot ID, + For example, if Python 3.16 adds a new feature with a new slot ID,attr the corresponding slot may be marked :c:macro:`!PySlot_OPTIONAL` so that Python 3.15 ignores it. @@ -185,9 +185,9 @@ Convenience macros PySlot_PTR_STATIC(name, value) Convenience macros for use in C++11-compatible code. - This version of C++ does not allow setting union members in literals, - these macros set the :c:macro:`PySlot_INTPTR` flag and cast the value - to ``(void*)``. + This version of C++ does not allow setting arbitrary union members in + literals, these macros set the :c:macro:`PySlot_INTPTR` flag and cast + the value to ``(void*)``. Defined as:: diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 9d247135cecec2..683a0dcd4f1a69 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -474,20 +474,28 @@ For example, use: * :c:data:`Py_nb_add` to set :c:member:`PyNumberMethods.nb_add` * :c:data:`Py_sq_length` to set :c:member:`PySequenceMethods.sq_length` -An additional slot is supported that does not correspond to a -:c:type:`!PyTypeObject` struct field: +The following slots need additional considerations when specified as slots: + +* :c:data:`Py_tp_name` +* :c:data:`Py_tp_basicsize` and :c:data:`Py_tp_extra_basicsize` +* :c:data:`Py_tp_itemsize` +* :c:data:`Py_tp_flags` + +Additional slots do not directly correspond to a :c:type:`!PyTypeObject` +struct field: * :c:data:`Py_tp_token` +* :c:data:`Py_tp_metaclass` +* :c:data:`Py_tp_module` The following “offset” fields cannot be set using :c:type:`PyType_Slot`: * :c:member:`~PyTypeObject.tp_weaklistoffset` - (use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible) + (use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible) * :c:member:`~PyTypeObject.tp_dictoffset` - (use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible) + (use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible) * :c:member:`~PyTypeObject.tp_vectorcall_offset` - (use ``"__vectorcalloffset__"`` in - :ref:`PyMemberDef `) + (use ``"__vectorcalloffset__"`` in :ref:`PyMemberDef `) If it is not possible to switch to a ``MANAGED`` flag (for example, for vectorcall or to support Python older than 3.12), specify the @@ -499,10 +507,10 @@ The following internal fields cannot be set at all when creating a heap type: * :c:member:`~PyTypeObject.tp_dict`, - :c:member:`~PyTypeObject.tp_mro`, - :c:member:`~PyTypeObject.tp_cache`, - :c:member:`~PyTypeObject.tp_subclasses`, and - :c:member:`~PyTypeObject.tp_weaklist`. + :c:member:`~PyTypeObject.tp_mro`, + :c:member:`~PyTypeObject.tp_cache`, + :c:member:`~PyTypeObject.tp_subclasses`, and + :c:member:`~PyTypeObject.tp_weaklist`. The :c:data:`Py_tp_base` slot is equivalent to :c:data:`Py_tp_bases`; both may be set either to a type or a tuple of types. @@ -513,7 +521,7 @@ Slot values may not be ``NULL``, except for the following: * :c:data:`Py_tp_doc` * :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC` - rather than ``NULL``) + rather than ``NULL``) .. versionchanged:: 3.9 Slots in :c:type:`PyBufferProcs` may be set in the unlimited API. @@ -528,8 +536,8 @@ Slot values may not be ``NULL``, except for the following: using :c:data:`Py_tp_vectorcall`. See the field's documentation for details. -The following slots do not correspond to public fields in the -underlying structures: +The following slots correspond to fields in the underlying type structure, +but need extra remarks for use slots: .. c:macro:: Py_tp_name @@ -612,12 +620,12 @@ underlying structures: in the following situations: - The base is not variable-sized (its - :c:member:`~PyTypeObject.tp_itemsize`). + :c:member:`~PyTypeObject.tp_itemsize`). - The requested :c:member:`PyType_Spec.basicsize` is positive, - suggesting that the memory layout of the base class is known. + suggesting that the memory layout of the base class is known. - The requested :c:member:`PyType_Spec.basicsize` is zero, - suggesting that the subclass does not access the instance's memory - directly. + suggesting that the subclass does not access the instance's memory + directly. - With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag. This may not be used in :c:member:`PyType_Spec.slots`. @@ -643,6 +651,9 @@ underlying structures: .. versionadded:: next +The following slots do not correspond to public fields in the +underlying structures: + .. c:macro:: Py_tp_metaclass :c:member:`Slot ID ` for the metaclass used to construct @@ -734,7 +745,7 @@ underlying structures: Soft-deprecated API -................... +------------------- The following functions are :term:`soft deprecated`. They will continue to work, but new features will be added as slots for diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index d3d8239365f9bf..38db69e5c6db96 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -555,6 +555,9 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: const char* PyTypeObject.tp_name + See :c:macro:`Py_tp_name` for the corresponding + :c:member:`Slot ID `. + Pointer to a NUL-terminated string containing the name of the type. For types that are accessible as module globals, the string should be the full module name, followed by a dot, followed by the type name; for built-in types, it @@ -594,6 +597,10 @@ and :c:data:`PyType_Type` effectively act as defaults.) These fields allow calculating the size in bytes of instances of the type. + See :c:macro:`Py_tp_basicsize`, :c:macro:`Py_tp_extra_basicsize` and + :c:macro:`Py_tp_itemsize` for the corresponding + :c:member:`Slot IDs `. + There are two kinds of types: types with fixed-length instances have a zero :c:member:`!tp_itemsize` field, types with variable-length instances have a non-zero :c:member:`!tp_itemsize` field. For a type with fixed-length instances, all @@ -1133,6 +1140,9 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: unsigned long PyTypeObject.tp_flags + See :c:macro:`Py_tp_flags` for the corresponding + :c:member:`Slot ID `. + This field is a bit mask of various flags. Some flags indicate variant semantics for certain situations; others are used to indicate that certain fields in the type object (or in the extension structures referenced via diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index 640230ab80b1b8..92b53c4abc0162 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -689,9 +689,9 @@ _PySlot_get_null_handling(uint16_t slot_id) case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: - case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: - case 78: case 79: case 80: case 81: case 82: case 84: case 88: case 89: - case 90: case 91: + case 70: case 71: case 73: case 74: case 75: case 76: case 77: case 78: + case 79: case 80: case 81: case 82: case 84: case 88: case 89: case 90: + case 91: return _PySlot_PROBLEM_DEPRECATED; default: return _PySlot_PROBLEM_REJECT; diff --git a/Lib/test/test_capi/test_slots.py b/Lib/test/test_capi/test_slots.py new file mode 100644 index 00000000000000..0e953f0c166a5e --- /dev/null +++ b/Lib/test/test_capi/test_slots.py @@ -0,0 +1,189 @@ +from test.support import import_helper, subTests +import contextlib +import unittest +import types + +_testlimitedcapi = import_helper.import_module('_testlimitedcapi') + +class FakeSpec: + name = 'module' + + +# See Modules/_testlimitedcapi/slots.c for the definitions. +# This module is full of "magic constants" which simply need to match +# between the C and Python part of the tests. + +class TypeSlotsTests(unittest.TestCase): + def test_basic_type_slots(self): + cls = _testlimitedcapi.type_from_slots("basic") + self.assertEqual(cls.__name__, "MyType") + + # Py_TPFLAGS_IMMUTABLETYPE is *not* set + cls.attr = 123 + + # Py_TPFLAGS_BASETYPE is *not* set + with self.assertRaises(TypeError): + class Sub(cls): + pass + + def test_mod_slot_in_type(self): + with self.assertRaisesRegex(SystemError, "invalid.* 100 .*Py_mod_name"): + _testlimitedcapi.type_from_slots("foreign_slot") + + def test_size_slots(self): + cls = _testlimitedcapi.type_from_slots("basicsize") + self.assertEqual(cls.__basicsize__, 256) + + cls = _testlimitedcapi.type_from_slots("extra_basicsize") + self.assertEqual(cls.__basicsize__, object.__basicsize__ + 256) + + cls = _testlimitedcapi.type_from_slots("itemsize") + self.assertEqual(cls.__itemsize__, 16) + + def test_flag_slots(self): + cls = _testlimitedcapi.type_from_slots("flags") + with self.assertRaises(TypeError): + # Py_TPFLAGS_IMMUTABLETYPE is set + cls.attr = 123 + class Sub(cls): + # Py_TPFLAGS_BASETYPE is set + pass + + def test_func_slots(self): + cls = _testlimitedcapi.type_from_slots("matmul_123") + self.assertEqual(cls() @ None, 123) + + def test_optional_end(self): + with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"): + cls = _testlimitedcapi.type_from_slots("optional_end") + + def test_invalid(self): + with self.assertRaisesRegex(SystemError, "Py_slot_invalid"): + cls = _testlimitedcapi.type_from_slots("invalid") + with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"): + cls = _testlimitedcapi.type_from_slots("invalid_fbad") + + cls = _testlimitedcapi.type_from_slots("optional_invalid") + self.assertEqual(cls.__basicsize__, object.__basicsize__ + 256) + + cls = _testlimitedcapi.type_from_slots("optional_invalid_fbad") + self.assertEqual(cls.__basicsize__, object.__basicsize__ + 256) + + @subTests("case_name", ["old_slot_numbers", "new_slot_numbers"]) + def test_compat_slot_numbers(self, case_name): + cls = _testlimitedcapi.type_from_slots(case_name) + obj = cls() + + # Py_bf_getbuffer (1), Py_bf_releasebuffer (2) + self.assertEqual(obj.buf_counter, 0) + mem = memoryview(obj) + self.assertEqual(bytes(mem), b"buf\0") + self.assertEqual(obj.buf_counter, 1) + mem.release() + self.assertEqual(obj.buf_counter, 0) + + # Py_mp_ass_subscript (3) + with self.assertRaises(KeyError): + obj["key"] = "value" + + # Py_mp_length (4) + self.assertEqual(len(obj), 456) + + def test_nonstatic_tp_members(self): + with self.assertRaisesRegex(SystemError, "Py_tp_members.*STATIC"): + _testlimitedcapi.type_from_slots("nonstatic_tp_members") + + def test_intptr_flags(self): + cls = _testlimitedcapi.type_from_slots("intptr_flags_macro") + with self.assertRaises(TypeError): + # Py_TPFLAGS_IMMUTABLETYPE is set + cls.attr = 123 + + cls = _testlimitedcapi.type_from_slots("intptr_flags_struct") + with self.assertRaises(TypeError): + # Py_TPFLAGS_IMMUTABLETYPE is set + cls.attr = 123 + + cls = _testlimitedcapi.type_from_slots("intptr_static") + cls.attribute = 123 + + def test_nested(self): + cls = _testlimitedcapi.type_from_slots("nested") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + + cls = _testlimitedcapi.type_from_slots("nested_max") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + self.assertEqual(cls() * 1, 345) + self.assertEqual(cls() / 1, 456) + self.assertEqual(cls() ** 1, 567) + + with self.assertRaisesRegex(SystemError, "too many levels"): + _testlimitedcapi.type_from_slots("nested_over_limit") + + cls = _testlimitedcapi.type_from_slots("nested_old") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + + cls = _testlimitedcapi.type_from_slots("nested_old_max") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + self.assertEqual(cls() * 1, 345) + self.assertEqual(cls() / 1, 456) + self.assertEqual(cls() ** 1, 567) + + with self.assertRaisesRegex(SystemError, "too many levels"): + _testlimitedcapi.type_from_slots("nested_old_over_limit") + + cls = _testlimitedcapi.type_from_slots("nested_pingpong") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + self.assertEqual(cls() * 1, 345) + self.assertEqual(cls() / 1, 456) + self.assertEqual(cls() ** 1, 567) + + # Slot names aren't exposed to Python yet; see Include/slots_generated.h + # for the definitions. + + @subTests("slot_number", [ + *range(1, 83), # Original slots + *range(88, 92), # New compat slot values + *range(95, 99), # Slots for PyTypeDef fields + *range(107, 109), # Slots for PyType_FromMetaclass args + ]) + def test_null_slot_handling(self, slot_number): + if slot_number == 56: + # Py_tp_doc + return + elif slot_number == 72 or slot_number >= 95: + # Py_tp_members; all new slots + ctx = self.assertRaisesRegex( + SystemError, "NULL not allowed|must be positive") + ctx_old = ctx + else: + ctx = self.assertWarnsRegex(DeprecationWarning, "NULL") + ctx_old = contextlib.nullcontext() + with ctx: + _testlimitedcapi.type_from_null_slot(slot_number) + if slot_number < 95: + with ctx_old: + _testlimitedcapi.type_from_null_spec_slot(slot_number) + + def test_repeat_warning(self): + with self.assertWarnsRegex(DeprecationWarning, "multiple"): + cls = _testlimitedcapi.type_from_slots("repeat_add") + self.assertEqual(cls() + 1, 456) + + def test_repeat_error(self): + with self.assertRaisesRegex(SystemError, "multiple"): + cls = _testlimitedcapi.type_from_slots("repeat_module") + +class ModuleSlotsTests(unittest.TestCase): + def test_basic_module_slots(self): + mod = _testlimitedcapi.module_from_slots("basic", FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + + def test_type_slot_in_module(self): + with self.assertRaisesRegex(SystemError, "invalid.* 95 .*Py_tp_name"): + _testlimitedcapi.module_from_slots("foreign_slot", FakeSpec()) diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 0d520684c795d6..5cabd5f71f5bf1 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -176,7 +176,7 @@ @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c _testinternalcapi/interpreter.c _testinternalcapi/tuple.c @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c -@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c +@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c index d3eb02d4727347..5f2be0dd43954e 100644 --- a/Modules/_testlimitedcapi.c +++ b/Modules/_testlimitedcapi.c @@ -74,6 +74,9 @@ PyInit__testlimitedcapi(void) if (_PyTestLimitedCAPI_Init_Set(mod) < 0) { return NULL; } + if (_PyTestLimitedCAPI_Init_Slots(mod) < 0) { + return NULL; + } if (_PyTestLimitedCAPI_Init_Sys(mod) < 0) { return NULL; } diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h index 1cbb4f5659af0f..1eea4f74d14416 100644 --- a/Modules/_testlimitedcapi/parts.h +++ b/Modules/_testlimitedcapi/parts.h @@ -37,6 +37,7 @@ int _PyTestLimitedCAPI_Init_List(PyObject *module); int _PyTestLimitedCAPI_Init_Long(PyObject *module); int _PyTestLimitedCAPI_Init_PyOS(PyObject *module); int _PyTestLimitedCAPI_Init_Set(PyObject *module); +int _PyTestLimitedCAPI_Init_Slots(PyObject *module); int _PyTestLimitedCAPI_Init_Sys(PyObject *module); int _PyTestLimitedCAPI_Init_ThreadState(PyObject *module); int _PyTestLimitedCAPI_Init_Tuple(PyObject *module); diff --git a/Modules/_testlimitedcapi/slots.c b/Modules/_testlimitedcapi/slots.c new file mode 100644 index 00000000000000..34eba5fdf90234 --- /dev/null +++ b/Modules/_testlimitedcapi/slots.c @@ -0,0 +1,379 @@ +#define Py_LIMITED_API 0x030f0000 + +#include "parts.h" + +PyABIInfo_VAR(abi_info); + +/* Define a bunch of (mostly nonsensical) functions to put in slots, so + * Lib/test/test_capi/test_slots.py can verify they've been assigned to + * the right slots. + + * This module is full of "magic constants" which simply need to match + * between the C and Python part of the tests. + */ + +// getbufferproc: export buffer; increment a counter +static int +demo_getbuffer(PyObject *exporter, Py_buffer *view, int flags) +{ + Py_INCREF(exporter); + // PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type + int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter)); + if (!data) { + return -1; + } + (*data)++; + return PyBuffer_FillInfo(view, exporter, "buf", 4, 1, flags); +} + +// releasebufferproc: release buffer; increment a counter +static void +demo_releasebuffer(PyObject *exporter, Py_buffer *view) +{ + Py_DECREF(exporter); + // PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type + int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter)); + if (!data) { + PyErr_WriteUnraisable(exporter); + return; + } + (*data)--; + return; +} + +// objobjargproc: raise KeyError +static int +demo_ass_subscript(PyObject *o, PyObject *key, PyObject *v) +{ + PyErr_Format(PyExc_KeyError, "I don't like that key"); + return -1; +} + +// lenfunc: report 456 +static Py_ssize_t +demo_length(PyObject *o) +{ + return (Py_ssize_t)456; +} + +// binaryfunc; return constant value +static PyObject *binop_123(PyObject* a, PyObject *b) { return PyLong_FromLong(123); } +static PyObject *binop_234(PyObject* a, PyObject *b) { return PyLong_FromLong(234); } +static PyObject *binop_345(PyObject* a, PyObject *b) { return PyLong_FromLong(345); } +static PyObject *binop_456(PyObject* a, PyObject *b) { return PyLong_FromLong(456); } +static PyObject *binop_567(PyObject* a, PyObject *b) { return PyLong_FromLong(567); } +static PyObject *binop_678(PyObject* a, PyObject *b) { return PyLong_FromLong(678); } + +static PyObject * +type_from_slots(PyObject* module, PyObject *args) +{ + char *case_name; + if (!PyArg_ParseTuple(args, "s", &case_name)) { + return NULL; + } +#define CASE(NAME) \ + if (strcmp(case_name, NAME) == 0) { \ + return PyType_FromSlots((PySlot[]) { \ + PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"), \ + PySlot_DATA(Py_tp_module, module), \ + ///////////////////////////////////////////////////////////////////////// +#define ENDCASE() \ + PySlot_END \ + }); \ + } \ + ///////////////////////////////////////////////////////////////////////// + + CASE("basic") + ENDCASE() + CASE("foreign_slot") + PySlot_DATA(Py_mod_name, "this is not a module"), + ENDCASE() + CASE("basicsize") + PySlot_SIZE(Py_tp_basicsize, 256), + ENDCASE() + CASE("extra_basicsize") + PySlot_SIZE(Py_tp_extra_basicsize, 256), + ENDCASE() + CASE("itemsize") + PySlot_SIZE(Py_tp_itemsize, 16), + ENDCASE() + CASE("flags") + PySlot_UINT64(Py_tp_flags, + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_BASETYPE), + ENDCASE() + CASE("matmul_123") + PySlot_FUNC(Py_nb_matrix_multiply, binop_123), + ENDCASE() + CASE("optional_end") + {.sl_flags=PySlot_OPTIONAL}, + ENDCASE() + CASE("invalid") + {.sl_id=Py_slot_invalid}, + ENDCASE() + CASE("invalid_fbad") + {.sl_id=0xfbad}, + ENDCASE() + CASE("optional_invalid") + {.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL}, + PySlot_SIZE(Py_tp_extra_basicsize, 256), + ENDCASE() + CASE("optional_invalid_fbad") + {.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL}, + PySlot_SIZE(Py_tp_extra_basicsize, 256), + ENDCASE() + CASE("old_slot_numbers") + PySlot_FUNC(1, demo_getbuffer), + PySlot_FUNC(2, demo_releasebuffer), + PySlot_FUNC(3, demo_ass_subscript), + PySlot_FUNC(4, demo_length), + PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)), + PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) { + {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET}, + {NULL}, + })), + ENDCASE() + CASE("new_slot_numbers") + PySlot_FUNC(88, demo_getbuffer), + PySlot_FUNC(89, demo_releasebuffer), + PySlot_FUNC(90, demo_ass_subscript), + PySlot_FUNC(91, demo_length), + PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)), + PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) { + {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET}, + {NULL}, + })), + ENDCASE() + CASE("nonstatic_tp_members") + PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)), + PySlot_DATA(Py_tp_members, ((PyMemberDef[]) { + {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET}, + {NULL}, + })), + ENDCASE() + CASE("intptr_flags_macro") + PySlot_PTR(Py_tp_flags, (void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE), + ENDCASE() + CASE("intptr_flags_struct") + {.sl_id=Py_tp_flags, + .sl_flags=PySlot_INTPTR, + .sl_ptr=(void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE, + }, + ENDCASE() + CASE("intptr_static") + PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)), + PySlot_PTR_STATIC(Py_tp_members, ((PyMemberDef[]) { + {"attribute", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET}, + {NULL}, + })), + ENDCASE() + CASE("nested") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_subtract, binop_234), + PySlot_END, + })), + ENDCASE() + CASE("nested_max") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_subtract, binop_234), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_multiply, binop_345), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_true_divide, binop_456), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_power, binop_567), + PySlot_END + })), + PySlot_END, + })), + PySlot_END, + })), + PySlot_END, + })), + ENDCASE() + CASE("nested_over_limit") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_subtract, binop_234), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_multiply, binop_345), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_true_divide, binop_456), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_power, binop_567), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_xor, binop_678), + PySlot_END + })), + PySlot_END + })), + PySlot_END, + })), + PySlot_END, + })), + PySlot_END, + })), + ENDCASE() + CASE("nested_old") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_subtract, binop_234}, + {0}, + })), + ENDCASE() + CASE("nested_old_max") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_subtract, binop_234}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_multiply, binop_345}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_true_divide, binop_456}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_power, binop_567}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })), + ENDCASE() + CASE("nested_old_over_limit") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_subtract, binop_234}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_multiply, binop_345}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_true_divide, binop_456}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_power, binop_567}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_xor, binop_678}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })), + ENDCASE() + CASE("nested_pingpong") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_subtract, binop_234}, + {Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_multiply, binop_345), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_true_divide, binop_456}, + {Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_power, binop_567), + PySlot_END + })}, + {0}, + })), + PySlot_END, + })}, + {0}, + })), + ENDCASE() + CASE("repeat_add") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_FUNC(Py_nb_add, binop_456), + ENDCASE() + CASE("repeat_module") + PySlot_DATA(Py_tp_module, Py_True), + PySlot_DATA(Py_tp_module, Py_False), + ENDCASE() + +#undef CASE +#undef ENDCASE + PyErr_Format(PyExc_AssertionError, "bad case: %s", case_name); + return NULL; +} + +static PyObject * +type_from_null_slot(PyObject* module, PyObject *args) +{ + long slot_number; + if (!PyArg_ParseTuple(args, "l", &slot_number)) { + return NULL; + } + return PyType_FromSlots((PySlot[]) { + PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"), + PySlot_DATA(Py_tp_module, module), + PySlot_PTR_STATIC(slot_number, NULL), + PySlot_END + }); +} + +static PyObject * +type_from_null_spec_slot(PyObject* module, PyObject *args) +{ + long slot_number; + if (!PyArg_ParseTuple(args, "l", &slot_number)) { + return NULL; + } + return PyType_FromSpec(&(PyType_Spec) { + .name = "_testlimitedcapi.MyType", + .slots = (PyType_Slot[]) { + {slot_number, NULL}, + {0}, + }, + }); +} + +static PyObject * +module_from_slots(PyObject* Py_UNUSED(module), PyObject *args) +{ + PyObject *spec; + char *case_name; + if (!PyArg_ParseTuple(args, "sO", &case_name, &spec)) { + return NULL; + } +#define CASE(NAME) \ + if (strcmp(case_name, NAME) == 0) { \ + return PyModule_FromSlotsAndSpec((PySlot[]) { \ + PySlot_DATA(Py_mod_abi, &abi_info), \ + ///////////////////////////////////////////////////////////////////////// +#define ENDCASE() \ + PySlot_END \ + }, spec); \ + } \ + ///////////////////////////////////////////////////////////////////////// + + CASE("basic") + ENDCASE() + CASE("foreign_slot") + PySlot_DATA(Py_tp_name, "this is not a type"), + ENDCASE() + +#undef CASE +#undef ENDCASE + PyErr_Format(PyExc_AssertionError, "bad case: %s", case_name); + return NULL; +} + +static PyMethodDef TestMethods[] = { + {"type_from_slots", type_from_slots, METH_VARARGS}, + {"type_from_null_slot", type_from_null_slot, METH_VARARGS}, + {"type_from_null_spec_slot", type_from_null_spec_slot, METH_VARARGS}, + {"module_from_slots", module_from_slots, METH_VARARGS}, + {NULL}, +}; + +int +_PyTestLimitedCAPI_Init_Slots(PyObject *m) +{ + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + + return 0; +} diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 2127b813e9fc09..75d5325d4eb604 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5305,12 +5305,6 @@ type_from_slots_or_spec( break; case Py_tp_bases: bases_slot = it.current.sl_ptr; - if (!PyTuple_Check(bases_slot)) { - PyErr_SetString( - PyExc_SystemError, - "Py_tp_bases is not a tuple"); - goto finally; - } break; case Py_tp_base: if (!_PySlotIterator_SawSlot(&it, Py_tp_bases)) { diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj index 935467dfcb3283..113b7600ee8564 100644 --- a/PCbuild/_testlimitedcapi.vcxproj +++ b/PCbuild/_testlimitedcapi.vcxproj @@ -109,6 +109,7 @@ + diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters index 5e0a0f65cfcc3d..a29973786c9485 100644 --- a/PCbuild/_testlimitedcapi.vcxproj.filters +++ b/PCbuild/_testlimitedcapi.vcxproj.filters @@ -24,6 +24,7 @@ + diff --git a/Python/slots.c b/Python/slots.c index 7b0442fda76d6a..38608d1bc09ff3 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -177,11 +177,15 @@ _PySlotIterator_Next(_PySlotIterator *it) default: Py_UNREACHABLE(); } - MSG("resolved to slot %s (%d)", + MSG("resolved to slot %d (%s)", (int)result->sl_id, _PySlot_GetName(result->sl_id)); if (result->sl_id == Py_slot_invalid) { MSG("error (unknown/invalid slot)"); + if (flags & PySlot_OPTIONAL) { + advance(it); + continue; + } _PySlot_err_bad_slot(kind_name(it->kind), orig_id); goto error; } @@ -304,14 +308,11 @@ handle_first_run(_PySlotIterator *it) if (!(it->current.sl_flags & PySlot_STATIC) && (it->state->slot_struct_kind == _PySlot_KIND_SLOT)) { - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, - 1, + PyErr_Format( + PyExc_SystemError, "%s must requires PySlot_STATIC", - _PySlot_GetName(id)) < 0) - { - return -1; - } + _PySlot_GetName(id)); + return -1; } } diff --git a/Python/slots.toml b/Python/slots.toml index 5666ec07f5efb0..95b2371a0c795c 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -587,7 +587,7 @@ name = 'Py_tp_members' kind = 'type' is_type_field = true dtype = 'ptr' -nulls = 'deprecated' +nulls = 'reject' must_be_static = true [73] From c0427a0bd1add508d550b0ac883d887f7cfe51b5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 16:26:24 +0200 Subject: [PATCH 33/36] Tests for modules --- Include/internal/pycore_slots_generated.h | 4 +- Lib/test/test_capi/test_slots.py | 120 +++++++++- Modules/_testlimitedcapi/slots.c | 280 +++++++++++++++++++++- Python/slots.c | 8 + Python/slots.toml | 2 +- 5 files changed, 406 insertions(+), 8 deletions(-) diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index 92b53c4abc0162..4898150b855ca5 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -690,8 +690,8 @@ _PySlot_get_null_handling(uint16_t slot_id) case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 73: case 74: case 75: case 76: case 77: case 78: - case 79: case 80: case 81: case 82: case 84: case 88: case 89: case 90: - case 91: + case 79: case 80: case 81: case 82: case 84: case 85: case 88: case 89: + case 90: case 91: return _PySlot_PROBLEM_DEPRECATED; default: return _PySlot_PROBLEM_REJECT; diff --git a/Lib/test/test_capi/test_slots.py b/Lib/test/test_capi/test_slots.py index 0e953f0c166a5e..1c6fd69da314da 100644 --- a/Lib/test/test_capi/test_slots.py +++ b/Lib/test/test_capi/test_slots.py @@ -1,4 +1,5 @@ from test.support import import_helper, subTests +import importlib.machinery import contextlib import unittest import types @@ -149,7 +150,7 @@ def test_nested(self): @subTests("slot_number", [ *range(1, 83), # Original slots *range(88, 92), # New compat slot values - *range(95, 99), # Slots for PyTypeDef fields + *range(95, 99), # Slots for PyType_Spec fields *range(107, 109), # Slots for PyType_FromMetaclass args ]) def test_null_slot_handling(self, slot_number): @@ -187,3 +188,120 @@ def test_basic_module_slots(self): def test_type_slot_in_module(self): with self.assertRaisesRegex(SystemError, "invalid.* 95 .*Py_tp_name"): _testlimitedcapi.module_from_slots("foreign_slot", FakeSpec()) + + def test_size_slots(self): + mod = _testlimitedcapi.module_from_slots("state_size", FakeSpec()) + self.assertEqual(mod.state_size, 42) + + def test_flag_slots(self): + mod = _testlimitedcapi.module_from_slots("multi_interp", FakeSpec()) + mod = _testlimitedcapi.module_from_slots("gil", FakeSpec()) + + def test_exec_slot(self): + mod = _testlimitedcapi.module_from_slots("exec", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + + def test_optional_end(self): + with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"): + _testlimitedcapi.module_from_slots("optional_end", FakeSpec()) + + def test_invalid(self): + with self.assertRaisesRegex(SystemError, "Py_slot_invalid"): + _testlimitedcapi.module_from_slots("invalid", FakeSpec()) + with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"): + _testlimitedcapi.module_from_slots("invalid_fbad", FakeSpec()) + + mod = _testlimitedcapi.module_from_slots("optional_invalid", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + + mod = _testlimitedcapi.module_from_slots("optional_invalid_fbad", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + + @subTests("case_name", ["old_slot_numbers", "new_slot_numbers"]) + def test_compat_slot_numbers(self, case_name): + mod = _testlimitedcapi.module_from_slots(case_name, FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + + @subTests("case_name", ["old_slot_number_create", "new_slot_number_create"]) + def test_compat_slot_number_create(self, case_name): + spec = FakeSpec() + mod = _testlimitedcapi.module_from_slots(case_name, spec) + self.assertIs(mod, spec) + + def test_nonstatic_mod_methods(self): + with self.assertRaisesRegex(SystemError, "Py_mod_methods.*STATIC"): + _testlimitedcapi.module_from_slots("nonstatic_mod_methods", + FakeSpec()) + + def test_intptr_methods(self): + mod = _testlimitedcapi.module_from_slots("intptr_methods", + FakeSpec()) + self.assertEqual(mod.type_from_slots.__name__, "type_from_slots") + + def test_nested(self): + mod = _testlimitedcapi.module_from_slots("nested", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.__doc__, "doc") + + mod = _testlimitedcapi.module_from_slots("nested_max", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.state_size, 53) + self.assertEqual(mod.__doc__, "doc") + + with self.assertRaisesRegex(SystemError, "too many levels"): + _testlimitedcapi.module_from_slots("nested_over_limit", FakeSpec()) + + mod = _testlimitedcapi.module_from_slots("nested_old", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.__doc__, "doc") + + mod = _testlimitedcapi.module_from_slots("nested_old_max", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.state_size, 53) + self.assertEqual(mod.__doc__, "doc") + + with self.assertRaisesRegex(SystemError, "too many levels"): + _testlimitedcapi.module_from_slots("nested_old_over_limit", FakeSpec()) + + mod = _testlimitedcapi.module_from_slots("nested_pingpong", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.state_size, 53) + self.assertEqual(mod.__doc__, "doc") + + def test_nested_nonstatic_from_def(self): + with self.assertRaisesRegex(SystemError, "must be static"): + _testlimitedcapi.module_from_def_nonstatic_nested(FakeSpec()) + + # Slot names aren't exposed to Python yet; see Include/slots_generated.h + # for the definitions. + + @subTests("slot_number", [ + *range(1, 5), # Old compat slot values + *range(84, 88), # New compat slot values + *range(100, 107), # Slots for PyModuleDef fields + *range(109, 111), # Slots new in 3.15 + ]) + def test_null_slot_handling(self, slot_number): + if slot_number in {3, 86, 4, 87, 102}: + # Py_mod_mult.interp., Py_mod_gil, Py_mod_state_size + return + elif slot_number > 85: + # new slots + ctx = self.assertRaisesRegex(SystemError, "NULL not allowed") + ctx_old = ctx + else: + ctx = self.assertWarnsRegex(DeprecationWarning, "NULL") + ctx_old = contextlib.nullcontext() + with ctx: + _testlimitedcapi.module_from_null_slot(slot_number, FakeSpec()) + with ctx_old: + _testlimitedcapi.module_from_null_def_slot(slot_number, + FakeSpec()) + + def test_repeat_error(self): + with self.assertRaisesRegex(SystemError, "multiple"): + _testlimitedcapi.module_from_slots("repeat_create", FakeSpec()) + with self.assertRaisesRegex(SystemError, "multiple"): + _testlimitedcapi.module_from_slots("repeat_exec", FakeSpec()) + with self.assertRaisesRegex(SystemError, "multiple"): + _testlimitedcapi.module_from_slots("repeat_gil", FakeSpec()) diff --git a/Modules/_testlimitedcapi/slots.c b/Modules/_testlimitedcapi/slots.c index 34eba5fdf90234..055efaf5236cbd 100644 --- a/Modules/_testlimitedcapi/slots.c +++ b/Modules/_testlimitedcapi/slots.c @@ -314,7 +314,7 @@ type_from_null_slot(PyObject* module, PyObject *args) } static PyObject * -type_from_null_spec_slot(PyObject* module, PyObject *args) +type_from_null_spec_slot(PyObject* Py_UNUSED(module), PyObject *args) { long slot_number; if (!PyArg_ParseTuple(args, "l", &slot_number)) { @@ -329,6 +329,21 @@ type_from_null_spec_slot(PyObject* module, PyObject *args) }); } +static PyObject * +demo_create(PyObject *spec, PyModuleDef *def) +{ + assert(def == NULL); + return Py_NewRef(spec); +} + +static int +demo_exec(PyObject *mod) +{ + return PyModule_AddStringConstant(mod, "exec_done", "yes"); +} + +static PyMethodDef TestMethods[]; + static PyObject * module_from_slots(PyObject* Py_UNUSED(module), PyObject *args) { @@ -337,9 +352,10 @@ module_from_slots(PyObject* Py_UNUSED(module), PyObject *args) if (!PyArg_ParseTuple(args, "sO", &case_name, &spec)) { return NULL; } + PyObject *mod = NULL; #define CASE(NAME) \ if (strcmp(case_name, NAME) == 0) { \ - return PyModule_FromSlotsAndSpec((PySlot[]) { \ + mod = PyModule_FromSlotsAndSpec((PySlot[]) { \ PySlot_DATA(Py_mod_abi, &abi_info), \ ///////////////////////////////////////////////////////////////////////// #define ENDCASE() \ @@ -353,11 +369,264 @@ module_from_slots(PyObject* Py_UNUSED(module), PyObject *args) CASE("foreign_slot") PySlot_DATA(Py_tp_name, "this is not a type"), ENDCASE() + CASE("state_size") + PySlot_SIZE(Py_mod_state_size, 42), + ENDCASE() + CASE("multi_interp") + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + ENDCASE() + CASE("gil") + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + ENDCASE() + CASE("exec") + PySlot_FUNC(Py_mod_exec, demo_exec), + ENDCASE() + CASE("optional_end") + {.sl_flags=PySlot_OPTIONAL}, + ENDCASE() + CASE("invalid") + {.sl_id=Py_slot_invalid}, + ENDCASE() + CASE("invalid_fbad") + {.sl_id=0xfbad}, + ENDCASE() + CASE("optional_invalid") + {.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL}, + PySlot_SIZE(Py_mod_exec, demo_exec), + ENDCASE() + CASE("optional_invalid_fbad") + {.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL}, + PySlot_SIZE(Py_mod_exec, demo_exec), + ENDCASE() + CASE("old_slot_numbers") + PySlot_FUNC(2, demo_exec), + PySlot_DATA(3, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_FUNC(4, Py_MOD_GIL_NOT_USED), + ENDCASE() + CASE("new_slot_numbers") + PySlot_FUNC(85, demo_exec), + PySlot_FUNC(86, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_FUNC(87, Py_MOD_GIL_NOT_USED), + ENDCASE() + CASE("old_slot_number_create") + PySlot_FUNC(1, demo_create), + PySlot_DATA(3, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_FUNC(4, Py_MOD_GIL_NOT_USED), + ENDCASE() + CASE("new_slot_number_create") + PySlot_FUNC(84, demo_create), + ENDCASE() + CASE("nonstatic_mod_methods") + PySlot_DATA(Py_mod_methods, TestMethods), + ENDCASE() + CASE("intptr_methods") + PySlot_PTR_STATIC(Py_mod_methods, TestMethods), + ENDCASE() + CASE("nested") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_doc, "doc"), + PySlot_END, + })), + ENDCASE() + CASE("nested_max") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_SIZE(Py_mod_state_size, 53), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_doc, "doc"), + PySlot_END + })), + PySlot_END, + })), + PySlot_END, + })), + PySlot_END, + })), + ENDCASE() + CASE("nested_over_limit") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_SIZE(Py_mod_state_size, 53), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_doc, "doc"), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_END + })), + PySlot_END + })), + PySlot_END, + })), + PySlot_END, + })), + PySlot_END, + })), + ENDCASE() + CASE("nested_old") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_doc, "doc"}, + {0}, + })), + ENDCASE() + CASE("nested_old_max") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_state_size, (void*)(intptr_t)53}, + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_doc, "doc"}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })), + ENDCASE() + CASE("nested_old_over_limit") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_state_size, (void*)(intptr_t)53}, + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_doc, "doc"}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })), + ENDCASE() + CASE("nested_pingpong") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_state_size, (void*)(intptr_t)53}, + {Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_doc, "doc"), + PySlot_END + })}, + {0}, + })), + PySlot_END, + })}, + {0}, + })), + ENDCASE() + CASE("repeat_create") + PySlot_DATA(Py_mod_create, demo_create), + PySlot_DATA(Py_mod_create, demo_create), + PySlot_DATA(Py_mod_create, demo_create), + ENDCASE() + CASE("repeat_gil") + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + ENDCASE() + CASE("repeat_exec") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_FUNC(Py_mod_exec, demo_exec), + ENDCASE() #undef CASE #undef ENDCASE - PyErr_Format(PyExc_AssertionError, "bad case: %s", case_name); - return NULL; + if (!mod) { + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_AssertionError, "bad case: %s", case_name); + return NULL; + } + return NULL; + } + if (PyModule_Check(mod)) { + Py_ssize_t size; + if (PyModule_GetStateSize(mod, &size) < 0) { + Py_DECREF(mod); + return NULL; + } + if (PyModule_AddIntConstant(mod, "state_size", size) < 0) { + Py_DECREF(mod); + return NULL; + } + if (PyModule_Exec(mod) < 0) { + return NULL; + } + } + return mod; +} + +static PyObject * +module_from_null_slot(PyObject* Py_UNUSED(module), PyObject *args) +{ + long slot_number; + PyObject *spec; + if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) { + return NULL; + } + return PyModule_FromSlotsAndSpec((PySlot[]) { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "mymod"), + PySlot_PTR_STATIC(slot_number, NULL), + PySlot_END + }, spec); +} + +static PyObject * +module_from_null_def_slot(PyObject* Py_UNUSED(module), PyObject *args) +{ + long slot_number; + PyObject *spec; + if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) { + return NULL; + } + static PyModuleDef_Slot slots[] = { + {0, NULL}, + {0}, + }; + static PyModuleDef def = { + .m_name = "mymod", + .m_slots = slots, + }; + // hack: def is supposed to be constant. + // Don't do this at home; use PyModule_FromSlotsAndSpec throwaway + // definitions! + slots[0].slot = slot_number; + return PyModule_FromDefAndSpec2(&def, spec, PYTHON_ABI_VERSION); +} + +static PyObject * +module_from_def_nonstatic_nested(PyObject* Py_UNUSED(module), PyObject *spec) +{ + static PyModuleDef_Slot subsubslots[] = { + {Py_mod_exec, demo_exec}, + {0}, + }; + static PySlot subslots[] = { + PySlot_DATA(Py_mod_slots, subsubslots), + PySlot_END, + }; + static PyModuleDef_Slot slots[] = { + {Py_slot_subslots, subslots}, + {0}, + }; + static PyModuleDef def = { + .m_name = "mymod", + .m_slots = slots, + }; + return PyModule_FromDefAndSpec2(&def, spec, PYTHON_ABI_VERSION); } static PyMethodDef TestMethods[] = { @@ -365,6 +634,9 @@ static PyMethodDef TestMethods[] = { {"type_from_null_slot", type_from_null_slot, METH_VARARGS}, {"type_from_null_spec_slot", type_from_null_spec_slot, METH_VARARGS}, {"module_from_slots", module_from_slots, METH_VARARGS}, + {"module_from_null_slot", module_from_null_slot, METH_VARARGS}, + {"module_from_null_def_slot", module_from_null_def_slot, METH_VARARGS}, + {"module_from_def_nonstatic_nested", module_from_def_nonstatic_nested, METH_O}, {NULL}, }; diff --git a/Python/slots.c b/Python/slots.c index 38608d1bc09ff3..0cf64502a4c3b9 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -211,6 +211,14 @@ _PySlotIterator_Next(_PySlotIterator *it) advance(it); continue; } + if ((it->states[0].slot_struct_kind == _PySlot_KIND_MOD) + && (it->state->slot_struct_kind == _PySlot_KIND_SLOT) + && !(result->sl_flags & PySlot_STATIC)) + { + PyErr_Format(PyExc_SystemError, + "slots included from PyModuleDef must be static"); + goto error; + } it->recursion_level++; MSG("recursing into level %d", it->recursion_level); if (it->recursion_level >= _PySlot_MAX_NESTING) { diff --git a/Python/slots.toml b/Python/slots.toml index 95b2371a0c795c..33e77264a82072 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -691,7 +691,7 @@ name = 'Py_mod_exec' kind = 'mod' dtype = 'func' duplicates = 'allow' # only alowed in PyModuleDef.m_slots -nulls = 'reject' +nulls = 'deprecated' [86] name = 'Py_mod_multiple_interpreters' From d979cfd1d4603bddf4fd44c7a9693405a04527a9 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 17:05:33 +0200 Subject: [PATCH 34/36] Adjust for static/NULL handling --- Doc/extending/first-extension-module.rst | 12 ++++++++---- Doc/includes/capi-extension/spammodule-01.c | 8 ++++---- Include/internal/pycore_slots_generated.h | 4 ++-- Lib/test/test_capi/test_misc.py | 4 ++-- Lib/test/test_capi/test_slots.py | 4 ++-- Lib/test/test_cext/extension.c | 8 ++++---- Modules/_testcapi/module.c | 8 ++++---- Modules/_testmultiphase.c | 2 +- Python/slots.c | 2 +- Python/slots.toml | 2 +- 10 files changed, 29 insertions(+), 25 deletions(-) diff --git a/Doc/extending/first-extension-module.rst b/Doc/extending/first-extension-module.rst index 73f9a6e0f1b6a6..894f5bdbb8f09c 100644 --- a/Doc/extending/first-extension-module.rst +++ b/Doc/extending/first-extension-module.rst @@ -259,7 +259,7 @@ Rather than ``NULL``, the export hook should return the information needed to create a module. Let's start with the basics: the name and docstring. -The information should be defined in a ``static`` array of +The information should be defined in an array of :c:type:`PySlot` entries, which are essentially key-value pairs. Define this array just before your export hook: @@ -268,12 +268,16 @@ Define this array just before your export hook: PyABIInfo_VAR(abi_info); static PySlot spam_slots[] = { - PySlot_DATA(Py_mod_abi, &abi_info), - PySlot_DATA(Py_mod_name, "spam"), - PySlot_DATA(Py_mod_doc, "A wonderful module with an example function"), + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, "spam"), + PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"), PySlot_END }; +The :c:macro:`PySlot_STATIC_DATA` macro is used when the slot value +(here: ``&abi_info``, ``"spam"``, and the docstring) is a pointer to constant, +statically allocated data. + The ``PyABIInfo_VAR(abi_info);`` macro and the :c:data:`Py_mod_abi` slot are a bit of boilerplate that helps prevent extensions compiled for a different version of Python from crashing the interpreter. diff --git a/Doc/includes/capi-extension/spammodule-01.c b/Doc/includes/capi-extension/spammodule-01.c index 6827c068912b61..8ddb416e7d6cd0 100644 --- a/Doc/includes/capi-extension/spammodule-01.c +++ b/Doc/includes/capi-extension/spammodule-01.c @@ -38,10 +38,10 @@ static PyMethodDef spam_methods[] = { PyABIInfo_VAR(abi_info); static PySlot spam_slots[] = { - PySlot_DATA(Py_mod_abi, &abi_info), - PySlot_DATA(Py_mod_name, "spam"), - PySlot_DATA(Py_mod_doc, "A wonderful module with an example function"), - PySlot_DATA(Py_mod_methods, spam_methods), + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, "spam"), + PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"), + PySlot_STATIC_DATA(Py_mod_methods, spam_methods), PySlot_END }; diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index 4898150b855ca5..92b53c4abc0162 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -690,8 +690,8 @@ _PySlot_get_null_handling(uint16_t slot_id) case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 73: case 74: case 75: case 76: case 77: case 78: - case 79: case 80: case 81: case 82: case 84: case 85: case 88: case 89: - case 90: case 91: + case 79: case 80: case 81: case 82: case 84: case 88: case 89: case 90: + case 91: return _PySlot_PROBLEM_DEPRECATED; default: return _PySlot_PROBLEM_REJECT; diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index f613bae1ce3352..3debc6369e89fb 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -923,8 +923,8 @@ def test_tp_bases_slot(self): def test_tp_bases_slot_none(self): self.assertRaisesRegex( - SystemError, - "Py_tp_bases is not a tuple", + TypeError, + "metaclass conflict", _testcapi.create_heapctype_with_none_bases_slot ) diff --git a/Lib/test/test_capi/test_slots.py b/Lib/test/test_capi/test_slots.py index 1c6fd69da314da..156d3f3eda5049 100644 --- a/Lib/test/test_capi/test_slots.py +++ b/Lib/test/test_capi/test_slots.py @@ -285,8 +285,8 @@ def test_null_slot_handling(self, slot_number): if slot_number in {3, 86, 4, 87, 102}: # Py_mod_mult.interp., Py_mod_gil, Py_mod_state_size return - elif slot_number > 85: - # new slots + elif slot_number in {2, 85} or slot_number > 85: + # Py_mod_exec, new slots ctx = self.assertRaisesRegex(SystemError, "NULL not allowed") ctx_old = ctx else: diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index 53911e4d0f3eda..89c8ec7efbffc9 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -122,11 +122,11 @@ PyDoc_STRVAR(_testcext_doc, "C test extension."); PyABIInfo_VAR(abi_info); static PySlot _testcext_slots[] = { - PySlot_DATA(Py_mod_abi, &abi_info), - PySlot_DATA(Py_mod_name, STR(MODULE_NAME)), - PySlot_DATA(Py_mod_doc, (void*)(char*)_testcext_doc), + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, STR(MODULE_NAME)), + PySlot_STATIC_DATA(Py_mod_doc, (void*)(char*)_testcext_doc), PySlot_FUNC(Py_mod_exec, (void*)_testcext_exec), - PySlot_FUNC(Py_mod_methods, _testcext_methods), + PySlot_STATIC_DATA(Py_mod_methods, _testcext_methods), PySlot_END, }; diff --git a/Modules/_testcapi/module.c b/Modules/_testcapi/module.c index 3d43ea96fbc6d4..b2ddaa10829755 100644 --- a/Modules/_testcapi/module.c +++ b/Modules/_testcapi/module.c @@ -94,7 +94,7 @@ module_from_slots_methods(PyObject *self, PyObject *spec) { PySlot slots[] = { PySlot_DATA(Py_mod_abi, &abi_info), - PySlot_DATA(Py_mod_methods, a_methoddef_array), + PySlot_STATIC_DATA(Py_mod_methods, a_methoddef_array), PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), PySlot_END, @@ -243,8 +243,8 @@ module_from_slots_repeat_slot(PyObject *self, PyObject *spec) } const PySlot slots[] = { PySlot_DATA(Py_mod_abi, &abi_info), - PySlot_PTR(slot_id, "anything"), - PySlot_PTR(slot_id, "anything_else"), + PySlot_PTR_STATIC(slot_id, "anything"), + PySlot_PTR_STATIC(slot_id, "anything_else"), PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), PySlot_END, @@ -261,7 +261,7 @@ module_from_slots_null_slot(PyObject *self, PyObject *spec) } const PySlot slots[] = { PySlot_DATA(Py_mod_abi, &abi_info), - PySlot_PTR(slot_id, NULL), + PySlot_PTR_STATIC(slot_id, NULL), PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), PySlot_END, diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index fc00dddb34f65e..43ee2e5a204bc2 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -1240,7 +1240,7 @@ PyModExport__test_from_modexport_smoke(void) PySlot_DATA(Py_mod_doc, "the expected docstring"), PySlot_FUNC(Py_mod_exec, modexport_smoke_exec), PySlot_SIZE(Py_mod_state_size, (void*)sizeof(int)), - PySlot_FUNC(Py_mod_methods, methods), + PySlot_STATIC_DATA(Py_mod_methods, methods), PySlot_FUNC(Py_mod_state_free, modexport_smoke_free), PySlot_DATA(Py_mod_token, (void*)&modexport_smoke_test_token), PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), diff --git a/Python/slots.c b/Python/slots.c index 0cf64502a4c3b9..2fd45399dc1f78 100644 --- a/Python/slots.c +++ b/Python/slots.c @@ -318,7 +318,7 @@ handle_first_run(_PySlotIterator *it) { PyErr_Format( PyExc_SystemError, - "%s must requires PySlot_STATIC", + "%s requires PySlot_STATIC", _PySlot_GetName(id)); return -1; } diff --git a/Python/slots.toml b/Python/slots.toml index 33e77264a82072..95b2371a0c795c 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -691,7 +691,7 @@ name = 'Py_mod_exec' kind = 'mod' dtype = 'func' duplicates = 'allow' # only alowed in PyModuleDef.m_slots -nulls = 'deprecated' +nulls = 'reject' [86] name = 'Py_mod_multiple_interpreters' From 3505d78787d1d69fafbda2ceca66eb8c92c3b43a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 17:11:12 +0200 Subject: [PATCH 35/36] Don't spam --- Include/internal/pycore_slots_generated.h | 326 +++++++++++++++++++--- Python/slots_generated.c | 141 ++++++++-- Tools/build/generate_slots.py | 37 ++- 3 files changed, 411 insertions(+), 93 deletions(-) diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index 92b53c4abc0162..c94bb21410e51a 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -15,18 +15,99 @@ _PySlot_resolve_type_slot(uint16_t slot_id) return Py_mp_ass_subscript; case 4: return Py_mp_length; - case 0: case 5: case 6: case 7: case 8: case 9: case 10: case 11: - case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: - case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: - case 28: case 29: case 30: case 31: case 32: case 33: case 34: case 35: - case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: - case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: - case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: - case 60: case 61: case 62: case 63: case 64: case 65: case 66: case 67: - case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: - case 76: case 77: case 78: case 79: case 80: case 81: case 82: case 83: - case 88: case 89: case 90: case 91: case 92: case 93: case 95: case 96: - case 97: case 98: case 99: case 107: case 108: + case Py_slot_end: + case Py_mp_subscript: + case Py_nb_absolute: + case Py_nb_add: + case Py_nb_and: + case Py_nb_bool: + case Py_nb_divmod: + case Py_nb_float: + case Py_nb_floor_divide: + case Py_nb_index: + case Py_nb_inplace_add: + case Py_nb_inplace_and: + case Py_nb_inplace_floor_divide: + case Py_nb_inplace_lshift: + case Py_nb_inplace_multiply: + case Py_nb_inplace_or: + case Py_nb_inplace_power: + case Py_nb_inplace_remainder: + case Py_nb_inplace_rshift: + case Py_nb_inplace_subtract: + case Py_nb_inplace_true_divide: + case Py_nb_inplace_xor: + case Py_nb_int: + case Py_nb_invert: + case Py_nb_lshift: + case Py_nb_multiply: + case Py_nb_negative: + case Py_nb_or: + case Py_nb_positive: + case Py_nb_power: + case Py_nb_remainder: + case Py_nb_rshift: + case Py_nb_subtract: + case Py_nb_true_divide: + case Py_nb_xor: + case Py_sq_ass_item: + case Py_sq_concat: + case Py_sq_contains: + case Py_sq_inplace_concat: + case Py_sq_inplace_repeat: + case Py_sq_item: + case Py_sq_length: + case Py_sq_repeat: + case Py_tp_alloc: + case Py_tp_base: + case Py_tp_bases: + case Py_tp_call: + case Py_tp_clear: + case Py_tp_dealloc: + case Py_tp_del: + case Py_tp_descr_get: + case Py_tp_descr_set: + case Py_tp_doc: + case Py_tp_getattr: + case Py_tp_getattro: + case Py_tp_hash: + case Py_tp_init: + case Py_tp_is_gc: + case Py_tp_iter: + case Py_tp_iternext: + case Py_tp_methods: + case Py_tp_new: + case Py_tp_repr: + case Py_tp_richcompare: + case Py_tp_setattr: + case Py_tp_setattro: + case Py_tp_str: + case Py_tp_traverse: + case Py_tp_members: + case Py_tp_getset: + case Py_tp_free: + case Py_nb_matrix_multiply: + case Py_nb_inplace_matrix_multiply: + case Py_am_await: + case Py_am_aiter: + case Py_am_anext: + case Py_tp_finalize: + case Py_am_send: + case Py_tp_vectorcall: + case Py_tp_token: + case Py_bf_getbuffer: + case Py_bf_releasebuffer: + case Py_mp_ass_subscript: + case Py_mp_length: + case Py_slot_subslots: + case Py_tp_slots: + case Py_tp_name: + case Py_tp_basicsize: + case Py_tp_extra_basicsize: + case Py_tp_itemsize: + case Py_tp_flags: + case Py_tp_metaclass: + case Py_tp_module: return slot_id; default: return Py_slot_invalid; @@ -45,9 +126,22 @@ _PySlot_resolve_mod_slot(uint16_t slot_id) return Py_mod_multiple_interpreters; case 4: return Py_mod_gil; - case 0: case 84: case 85: case 86: case 87: case 92: case 94: case 100: - case 101: case 102: case 103: case 104: case 105: case 106: case 109: - case 110: + case Py_slot_end: + case Py_mod_create: + case Py_mod_exec: + case Py_mod_multiple_interpreters: + case Py_mod_gil: + case Py_slot_subslots: + case Py_mod_slots: + case Py_mod_name: + case Py_mod_doc: + case Py_mod_state_size: + case Py_mod_methods: + case Py_mod_state_traverse: + case Py_mod_state_clear: + case Py_mod_state_free: + case Py_mod_abi: + case Py_mod_token: return slot_id; default: return Py_slot_invalid; @@ -654,19 +748,90 @@ static inline _PySlot_PROBLEM_HANDLING _PySlot_get_duplicate_handling(uint16_t slot_id) { switch (slot_id) { - case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: - case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: - case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: - case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: - case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: - case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: - case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: - case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: - case 70: case 71: case 73: case 74: case 75: case 76: case 77: case 78: - case 79: case 80: case 81: case 82: case 83: case 88: case 89: case 90: - case 91: + case Py_mp_subscript: + case Py_nb_absolute: + case Py_nb_add: + case Py_nb_and: + case Py_nb_bool: + case Py_nb_divmod: + case Py_nb_float: + case Py_nb_floor_divide: + case Py_nb_index: + case Py_nb_inplace_add: + case Py_nb_inplace_and: + case Py_nb_inplace_floor_divide: + case Py_nb_inplace_lshift: + case Py_nb_inplace_multiply: + case Py_nb_inplace_or: + case Py_nb_inplace_power: + case Py_nb_inplace_remainder: + case Py_nb_inplace_rshift: + case Py_nb_inplace_subtract: + case Py_nb_inplace_true_divide: + case Py_nb_inplace_xor: + case Py_nb_int: + case Py_nb_invert: + case Py_nb_lshift: + case Py_nb_multiply: + case Py_nb_negative: + case Py_nb_or: + case Py_nb_positive: + case Py_nb_power: + case Py_nb_remainder: + case Py_nb_rshift: + case Py_nb_subtract: + case Py_nb_true_divide: + case Py_nb_xor: + case Py_sq_ass_item: + case Py_sq_concat: + case Py_sq_contains: + case Py_sq_inplace_concat: + case Py_sq_inplace_repeat: + case Py_sq_item: + case Py_sq_length: + case Py_sq_repeat: + case Py_tp_alloc: + case Py_tp_base: + case Py_tp_bases: + case Py_tp_call: + case Py_tp_clear: + case Py_tp_dealloc: + case Py_tp_del: + case Py_tp_descr_get: + case Py_tp_descr_set: + case Py_tp_getattr: + case Py_tp_getattro: + case Py_tp_hash: + case Py_tp_init: + case Py_tp_is_gc: + case Py_tp_iter: + case Py_tp_iternext: + case Py_tp_methods: + case Py_tp_new: + case Py_tp_repr: + case Py_tp_richcompare: + case Py_tp_setattr: + case Py_tp_setattro: + case Py_tp_str: + case Py_tp_traverse: + case Py_tp_getset: + case Py_tp_free: + case Py_nb_matrix_multiply: + case Py_nb_inplace_matrix_multiply: + case Py_am_await: + case Py_am_aiter: + case Py_am_anext: + case Py_tp_finalize: + case Py_am_send: + case Py_tp_vectorcall: + case Py_tp_token: + case Py_bf_getbuffer: + case Py_bf_releasebuffer: + case Py_mp_ass_subscript: + case Py_mp_length: return _PySlot_PROBLEM_DEPRECATED; - case 85: case 109: + case Py_mod_exec: + case Py_mod_abi: return _PySlot_PROBLEM_ALLOW; default: return _PySlot_PROBLEM_REJECT; @@ -677,21 +842,100 @@ static inline _PySlot_PROBLEM_HANDLING _PySlot_get_null_handling(uint16_t slot_id) { switch (slot_id) { - case 0: case 1: case 2: case 3: case 4: case 56: case 83: case 86: - case 87: case 92: case 93: case 96: case 97: case 98: case 99: - case 102: + case Py_slot_end: + case Py_tp_doc: + case Py_tp_token: + case Py_mod_multiple_interpreters: + case Py_mod_gil: + case Py_slot_subslots: + case Py_tp_slots: + case Py_tp_basicsize: + case Py_tp_extra_basicsize: + case Py_tp_itemsize: + case Py_tp_flags: + case Py_mod_state_size: return _PySlot_PROBLEM_ALLOW; - case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: - case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: - case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: - case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: - case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: - case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: - case 53: case 54: case 55: case 57: case 58: case 59: case 60: case 61: - case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: - case 70: case 71: case 73: case 74: case 75: case 76: case 77: case 78: - case 79: case 80: case 81: case 82: case 84: case 88: case 89: case 90: - case 91: + case Py_mp_subscript: + case Py_nb_absolute: + case Py_nb_add: + case Py_nb_and: + case Py_nb_bool: + case Py_nb_divmod: + case Py_nb_float: + case Py_nb_floor_divide: + case Py_nb_index: + case Py_nb_inplace_add: + case Py_nb_inplace_and: + case Py_nb_inplace_floor_divide: + case Py_nb_inplace_lshift: + case Py_nb_inplace_multiply: + case Py_nb_inplace_or: + case Py_nb_inplace_power: + case Py_nb_inplace_remainder: + case Py_nb_inplace_rshift: + case Py_nb_inplace_subtract: + case Py_nb_inplace_true_divide: + case Py_nb_inplace_xor: + case Py_nb_int: + case Py_nb_invert: + case Py_nb_lshift: + case Py_nb_multiply: + case Py_nb_negative: + case Py_nb_or: + case Py_nb_positive: + case Py_nb_power: + case Py_nb_remainder: + case Py_nb_rshift: + case Py_nb_subtract: + case Py_nb_true_divide: + case Py_nb_xor: + case Py_sq_ass_item: + case Py_sq_concat: + case Py_sq_contains: + case Py_sq_inplace_concat: + case Py_sq_inplace_repeat: + case Py_sq_item: + case Py_sq_length: + case Py_sq_repeat: + case Py_tp_alloc: + case Py_tp_base: + case Py_tp_bases: + case Py_tp_call: + case Py_tp_clear: + case Py_tp_dealloc: + case Py_tp_del: + case Py_tp_descr_get: + case Py_tp_descr_set: + case Py_tp_getattr: + case Py_tp_getattro: + case Py_tp_hash: + case Py_tp_init: + case Py_tp_is_gc: + case Py_tp_iter: + case Py_tp_iternext: + case Py_tp_methods: + case Py_tp_new: + case Py_tp_repr: + case Py_tp_richcompare: + case Py_tp_setattr: + case Py_tp_setattro: + case Py_tp_str: + case Py_tp_traverse: + case Py_tp_getset: + case Py_tp_free: + case Py_nb_matrix_multiply: + case Py_nb_inplace_matrix_multiply: + case Py_am_await: + case Py_am_aiter: + case Py_am_anext: + case Py_tp_finalize: + case Py_am_send: + case Py_tp_vectorcall: + case Py_mod_create: + case Py_bf_getbuffer: + case Py_bf_releasebuffer: + case Py_mp_ass_subscript: + case Py_mp_length: return _PySlot_PROBLEM_DEPRECATED; default: return _PySlot_PROBLEM_REJECT; diff --git a/Python/slots_generated.c b/Python/slots_generated.c index ccd228c7066ef6..0b946b55b3789f 100644 --- a/Python/slots_generated.c +++ b/Python/slots_generated.c @@ -4,37 +4,116 @@ #include "pycore_slots.h" // _PySlot_names const char *_PySlot_names[] = { - "Py_slot_end", "Py_bf_getbuffer/Py_mod_create", + "Py_slot_end", + "Py_bf_getbuffer/Py_mod_create", "Py_bf_releasebuffer/Py_mod_exec", "Py_mp_ass_subscript/Py_mod_multiple_interpreters", - "Py_mp_length/Py_mod_gil", "Py_mp_subscript", "Py_nb_absolute", - "Py_nb_add", "Py_nb_and", "Py_nb_bool", "Py_nb_divmod", "Py_nb_float", - "Py_nb_floor_divide", "Py_nb_index", "Py_nb_inplace_add", - "Py_nb_inplace_and", "Py_nb_inplace_floor_divide", "Py_nb_inplace_lshift", - "Py_nb_inplace_multiply", "Py_nb_inplace_or", "Py_nb_inplace_power", - "Py_nb_inplace_remainder", "Py_nb_inplace_rshift", - "Py_nb_inplace_subtract", "Py_nb_inplace_true_divide", "Py_nb_inplace_xor", - "Py_nb_int", "Py_nb_invert", "Py_nb_lshift", "Py_nb_multiply", - "Py_nb_negative", "Py_nb_or", "Py_nb_positive", "Py_nb_power", - "Py_nb_remainder", "Py_nb_rshift", "Py_nb_subtract", "Py_nb_true_divide", - "Py_nb_xor", "Py_sq_ass_item", "Py_sq_concat", "Py_sq_contains", - "Py_sq_inplace_concat", "Py_sq_inplace_repeat", "Py_sq_item", - "Py_sq_length", "Py_sq_repeat", "Py_tp_alloc", "Py_tp_base", "Py_tp_bases", - "Py_tp_call", "Py_tp_clear", "Py_tp_dealloc", "Py_tp_del", - "Py_tp_descr_get", "Py_tp_descr_set", "Py_tp_doc", "Py_tp_getattr", - "Py_tp_getattro", "Py_tp_hash", "Py_tp_init", "Py_tp_is_gc", "Py_tp_iter", - "Py_tp_iternext", "Py_tp_methods", "Py_tp_new", "Py_tp_repr", - "Py_tp_richcompare", "Py_tp_setattr", "Py_tp_setattro", "Py_tp_str", - "Py_tp_traverse", "Py_tp_members", "Py_tp_getset", "Py_tp_free", - "Py_nb_matrix_multiply", "Py_nb_inplace_matrix_multiply", "Py_am_await", - "Py_am_aiter", "Py_am_anext", "Py_tp_finalize", "Py_am_send", - "Py_tp_vectorcall", "Py_tp_token", "Py_mod_create", "Py_mod_exec", - "Py_mod_multiple_interpreters", "Py_mod_gil", "Py_bf_getbuffer", - "Py_bf_releasebuffer", "Py_mp_ass_subscript", "Py_mp_length", - "Py_slot_subslots", "Py_tp_slots", "Py_mod_slots", "Py_tp_name", - "Py_tp_basicsize", "Py_tp_extra_basicsize", "Py_tp_itemsize", - "Py_tp_flags", "Py_mod_name", "Py_mod_doc", "Py_mod_state_size", - "Py_mod_methods", "Py_mod_state_traverse", "Py_mod_state_clear", - "Py_mod_state_free", "Py_tp_metaclass", "Py_tp_module", "Py_mod_abi", - "Py_mod_token", NULL + "Py_mp_length/Py_mod_gil", + "Py_mp_subscript", + "Py_nb_absolute", + "Py_nb_add", + "Py_nb_and", + "Py_nb_bool", + "Py_nb_divmod", + "Py_nb_float", + "Py_nb_floor_divide", + "Py_nb_index", + "Py_nb_inplace_add", + "Py_nb_inplace_and", + "Py_nb_inplace_floor_divide", + "Py_nb_inplace_lshift", + "Py_nb_inplace_multiply", + "Py_nb_inplace_or", + "Py_nb_inplace_power", + "Py_nb_inplace_remainder", + "Py_nb_inplace_rshift", + "Py_nb_inplace_subtract", + "Py_nb_inplace_true_divide", + "Py_nb_inplace_xor", + "Py_nb_int", + "Py_nb_invert", + "Py_nb_lshift", + "Py_nb_multiply", + "Py_nb_negative", + "Py_nb_or", + "Py_nb_positive", + "Py_nb_power", + "Py_nb_remainder", + "Py_nb_rshift", + "Py_nb_subtract", + "Py_nb_true_divide", + "Py_nb_xor", + "Py_sq_ass_item", + "Py_sq_concat", + "Py_sq_contains", + "Py_sq_inplace_concat", + "Py_sq_inplace_repeat", + "Py_sq_item", + "Py_sq_length", + "Py_sq_repeat", + "Py_tp_alloc", + "Py_tp_base", + "Py_tp_bases", + "Py_tp_call", + "Py_tp_clear", + "Py_tp_dealloc", + "Py_tp_del", + "Py_tp_descr_get", + "Py_tp_descr_set", + "Py_tp_doc", + "Py_tp_getattr", + "Py_tp_getattro", + "Py_tp_hash", + "Py_tp_init", + "Py_tp_is_gc", + "Py_tp_iter", + "Py_tp_iternext", + "Py_tp_methods", + "Py_tp_new", + "Py_tp_repr", + "Py_tp_richcompare", + "Py_tp_setattr", + "Py_tp_setattro", + "Py_tp_str", + "Py_tp_traverse", + "Py_tp_members", + "Py_tp_getset", + "Py_tp_free", + "Py_nb_matrix_multiply", + "Py_nb_inplace_matrix_multiply", + "Py_am_await", + "Py_am_aiter", + "Py_am_anext", + "Py_tp_finalize", + "Py_am_send", + "Py_tp_vectorcall", + "Py_tp_token", + "Py_mod_create", + "Py_mod_exec", + "Py_mod_multiple_interpreters", + "Py_mod_gil", + "Py_bf_getbuffer", + "Py_bf_releasebuffer", + "Py_mp_ass_subscript", + "Py_mp_length", + "Py_slot_subslots", + "Py_tp_slots", + "Py_mod_slots", + "Py_tp_name", + "Py_tp_basicsize", + "Py_tp_extra_basicsize", + "Py_tp_itemsize", + "Py_tp_flags", + "Py_mod_name", + "Py_mod_doc", + "Py_mod_state_size", + "Py_mod_methods", + "Py_mod_state_traverse", + "Py_mod_state_clear", + "Py_mod_state_free", + "Py_tp_metaclass", + "Py_tp_module", + "Py_mod_abi", + "Py_mod_token", + NULL }; diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py index d916d9ff546bb5..a5f87a19209ee4 100755 --- a/Tools/build/generate_slots.py +++ b/Tools/build/generate_slots.py @@ -140,20 +140,6 @@ def block(self, header=None, end=''): self.indent = old_indent self.out('}' + end) - def spam(self, segments): - """Write *segments*, putting as many as will fit on a single line""" - maxlen = 79-len(self.indent) - segments = iter(segments) - current_line = next(segments) - for segment in segments: - if len(current_line) + 1 + len(segment) > maxlen: - self.out(current_line) - current_line = segment - else: - current_line += ' ' + segment - if current_line: - self.out(current_line) - def write_public_header(f, slots): out = CWriter(f) @@ -206,8 +192,9 @@ def add_case(slot): out(f'case {slot.id}:') out(f' return {new_slot.name};') elif slot.kind in {kind, 'slot'}: - good_slots.append(f'case {slot.id}:') - out.spam(good_slots) + good_slots.append(f'case {slot.name}:') + for case in good_slots: + out(case) out(f' return slot_id;') out(f'default:') out(f' return Py_slot_invalid;') @@ -275,11 +262,14 @@ def add_case(slot): with out.block('switch (slot_id)'): results = collections.defaultdict(list) for slot in slots: + if slot.kind == 'compat': + continue handling = slot.duplicate_handling - results[handling.upper()].append(f'case {slot.id}:') + results[handling.upper()].append(f'case {slot.name}:') results.pop('REJECT') for handling, cases in results.items(): - out.spam(cases) + for case in cases: + out(case) out(f' return _PySlot_PROBLEM_{handling};') out(f'default:') out(f' return _PySlot_PROBLEM_REJECT;') @@ -290,16 +280,19 @@ def add_case(slot): with out.block('switch (slot_id)'): results = collections.defaultdict(list) for slot in slots: + if slot.kind == 'compat': + continue handling = slot.null_handling if handling is None: if slot.kind != 'compat' and slot.dtype in {'ptr', 'func'}: handling = 'reject' else: handling = 'allow' - results[handling.upper()].append(f'case {slot.id}:') + results[handling.upper()].append(f'case {slot.name}:') results.pop('REJECT') for handling, cases in results.items(): - out.spam(cases) + for case in cases: + out(case) out(f' return _PySlot_PROBLEM_{handling};') out(f'default:') out(f' return _PySlot_PROBLEM_REJECT;') @@ -323,7 +316,9 @@ def write_c(f, slots): out('#include "pycore_slots.h" // _PySlot_names') out() with out.block(f'const char *_PySlot_names[] =', end=';'): - out.spam([f'"{slot.name}",' for slot in slots] + ['NULL']) + for slot in slots: + out(f'"{slot.name}",') + out('NULL') @contextlib.contextmanager From b47a219f6347c6e47afbc937cc1b80608f3fa418 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 27 Apr 2026 17:41:45 +0200 Subject: [PATCH 36/36] Fixups --- Doc/c-api/extension-modules.rst | 6 +++--- Doc/c-api/type.rst | 16 ++++++++-------- Include/internal/pycore_slots_generated.h | 1 + Modules/_testlimitedcapi/slots.c | 2 +- Python/slots.toml | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Doc/c-api/extension-modules.rst b/Doc/c-api/extension-modules.rst index b498ec89ac4bd7..34ee86c7876ae7 100644 --- a/Doc/c-api/extension-modules.rst +++ b/Doc/c-api/extension-modules.rst @@ -84,9 +84,9 @@ For example, a module called ``spam`` would be defined like this:: PyABIInfo_VAR(abi_info); static PySlot spam_slots[] = { - PySlot_DATA(Py_mod_abi, &abi_info), - PySlot_DATA(Py_mod_name, "spam"), - PySlot_DATA(Py_mod_init, spam_init_function), + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, "spam"), + PySlot_FUNC(Py_mod_init, spam_init_function), ... PySlot_END }; diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 683a0dcd4f1a69..c1e4fc27d57269 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -417,12 +417,12 @@ The following function is used to create :ref:`heap types `: However, sometimes slot values are not statically known at compile time. For example, slots like :c:data:`Py_tp_bases`, :c:data:`Py_tp_metaclass` and :c:data:`Py_tp_module` require live Python objects. - In this case, it is recommended to put such slots on the heap, + In this case, it is recommended to put such slots on the stack, and use :c:macro:`Py_slot_subslots` to refer to an array of static slots. For example:: static const PySlot my_slots[] = { - PySlot_DATA(Py_tp_name, "MyClass"), + PySlot_STATIC_DATA(Py_tp_name, "MyClass"), PySlot_FUNC(Py_tp_repr, my_repr_func), ... PySlot_END @@ -430,7 +430,7 @@ The following function is used to create :ref:`heap types `: PyObject *make_my_class(PyObject *module) { PySlot all_slots[] = { - PySlot_DATA(Py_slot_subslots, my_slots), + PySlot_STATIC_DATA(Py_slot_subslots, my_slots), PySlot_DATA(Py_tp_module, module), PySlot_END }; @@ -442,7 +442,7 @@ modified, for example by setting attributes on them, as with classes defined in Python code. Sometimes, such modifications are necessary to fully initialize a type, but you may wish to prevent users from changing the type after -the initialisation is done: +the initialization is done: .. c:function:: int PyType_Freeze(PyTypeObject *type) @@ -537,7 +537,7 @@ Slot values may not be ``NULL``, except for the following: for details. The following slots correspond to fields in the underlying type structure, -but need extra remarks for use slots: +but need extra remarks for use as slots: .. c:macro:: Py_tp_name @@ -554,7 +554,7 @@ but need extra remarks for use slots: CPython processes slots in order. It is recommended to put ``Py_tp_name`` at the beginning of the slots array, so that if processing of a later slots fails, error messages - can include the class name. + can include the name. .. versionadded:: next @@ -590,8 +590,8 @@ but need extra remarks for use slots: The value must be positive. To specify that instances need no additional size (that is, size should be - inherited), omit the :c:macro:`!Py_tp_extra_basicsize` slot; do not set it - to zero. + inherited), omit the :c:macro:`!Py_tp_extra_basicsize` slot rather than + set it to zero. Specifying both :c:macro:`Py_tp_basicsize` and :c:macro:`!Py_tp_extra_basicsize` is an error. diff --git a/Include/internal/pycore_slots_generated.h b/Include/internal/pycore_slots_generated.h index c94bb21410e51a..73a77070038cef 100644 --- a/Include/internal/pycore_slots_generated.h +++ b/Include/internal/pycore_slots_generated.h @@ -849,6 +849,7 @@ _PySlot_get_null_handling(uint16_t slot_id) case Py_mod_gil: case Py_slot_subslots: case Py_tp_slots: + case Py_mod_slots: case Py_tp_basicsize: case Py_tp_extra_basicsize: case Py_tp_itemsize: diff --git a/Modules/_testlimitedcapi/slots.c b/Modules/_testlimitedcapi/slots.c index 055efaf5236cbd..4a04d59c5b6aac 100644 --- a/Modules/_testlimitedcapi/slots.c +++ b/Modules/_testlimitedcapi/slots.c @@ -26,7 +26,7 @@ demo_getbuffer(PyObject *exporter, Py_buffer *view, int flags) return PyBuffer_FillInfo(view, exporter, "buf", 4, 1, flags); } -// releasebufferproc: release buffer; increment a counter +// releasebufferproc: release buffer; decrement a counter static void demo_releasebuffer(PyObject *exporter, Py_buffer *view) { diff --git a/Python/slots.toml b/Python/slots.toml index 95b2371a0c795c..626a44d2f2c80e 100644 --- a/Python/slots.toml +++ b/Python/slots.toml @@ -751,7 +751,7 @@ nulls = 'allow' name = 'Py_mod_slots' kind = 'mod' dtype = 'ptr' -n.ulls = 'allow' +nulls = 'allow' [95] name = 'Py_tp_name'