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
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
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
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 <extension-export-hook>`
must be named :samp:`PyModExport_{<name>}`,
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.
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"``.
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_STATIC_DATA(Py_mod_abi, &abi_info),
+ PySlot_STATIC_DATA(Py_mod_name, "spam"),
+ PySlot_FUNC(Py_mod_init, spam_init_function),
...
- {0, NULL},
+ PySlot_END
};
PyMODEXPORT_FUNC
refcounting.rst
exceptions.rst
extension-modules.rst
+ slots.rst
utilities.rst
abstract.rst
concrete.rst
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
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 <PyModuleDef_Slot.slot>` for the name of the new module,
+ :c:member:`Slot ID <PySlot.sl_id>` for the name of the new module,
as a NUL-terminated UTF8-encoded ``const char *``.
Note that modules are typically created using a
.. c:macro:: Py_mod_doc
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for the docstring of the new
+ :c:type:`Slot ID <PySlot.sl_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`.
.. c:macro:: Py_mod_abi
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value points to
+ :c:member:`Slot ID <PySlot.sl_id>` whose value points to
a :c:struct:`PyABIInfo` structure describing the ABI that
the extension is using.
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),
...
};
.. c:macro:: Py_mod_multiple_interpreters
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value is one of:
+ :c:member:`Slot ID <PySlot.sl_id>` whose value is one of:
.. c:namespace:: NULL
.. c:macro:: Py_mod_gil
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value is one of:
+ :c:member:`Slot ID <PySlot.sl_id>` whose value is one of:
.. c:namespace:: NULL
.. c:macro:: Py_mod_create
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function that creates
+ :c:member:`Slot ID <PySlot.sl_id>` for a function that creates
the module object itself.
The function must have the signature:
.. c:macro:: Py_mod_exec
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function that will
+ :c:member:`Slot ID <PySlot.sl_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.
.. c:macro:: Py_mod_methods
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a table of module-level
+ :c:member:`Slot ID <PySlot.sl_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`.
Slots for defining module state
...............................
-The following :c:member:`PyModuleDef_Slot.slot` IDs are available for
+The following :c:member:`slot IDs <PySlot.sl_id>` are available for
defining the module state.
.. c:macro:: Py_mod_state_size
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for the size of the module state,
+ :c:member:`Slot ID <PySlot.sl_id>` for the size of the module state,
in bytes.
Setting the value to a non-negative value means that the module can be
.. c:macro:: Py_mod_state_traverse
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a traversal function to call
+ :c:member:`Slot ID <PySlot.sl_id>` for a traversal function to call
during GC traversal of the module object.
The signature of the function, and meanings of the arguments,
.. c:macro:: Py_mod_state_clear
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a clear function to call
+ :c:member:`Slot ID <PySlot.sl_id>` for a clear function to call
during GC clearing of the module object.
The signature of the function is:
.. c:macro:: Py_mod_state_free
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function to call during
+ :c:member:`Slot ID <PySlot.sl_id>` for a function to call during
deallocation of the module object.
The signature of the function is:
.. c:macro:: Py_mod_token
- :c:type:`Slot ID <PyModuleDef_Slot.slot>` for the module token.
+ :c:member:`Slot ID <PySlot.sl_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:
The following functions may be used to create an extension module dynamically,
rather than from an extension's :ref:`export hook <extension-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 <pymoduledef_slot>`
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
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)
.. 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.
.. 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
The type of ``PyModuleDef`` objects.
+.. c:macro:: Py_mod_slots
+
+ :c:member:`Slot ID <PySlot.sl_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`
.. 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
.. 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
--- /dev/null
+.. highlight:: c
+
+.. _capi-slots:
+
+Definition slots
+================
+
+To define :ref:`module objects <moduleobjects>` and
+:ref:`classes <creating-heap-types>` 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
+-- 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.
+
+.. 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
+
+ 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,attr
+ 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 2020 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 macro 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 arbitrary union members in
+ literals; instead, 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
.. _typeobjects:
Type Objects
-------------
+============
.. index:: pair: object; type
* :py:mod:`weakref`
-Creating Heap-Allocated Types
-.............................
-
-The following functions and structs are used to create
-:ref:`heap types <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 <heap-types>` from the *spec*
- (see :c:macro:`Py_TPFLAGS_HEAPTYPE`).
+The following function is used to create :ref:`heap types <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 <heap-types>` 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.
* :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 stack,
+ and use :c:macro:`Py_slot_subslots` to refer to an array of static slots.
+ For example::
+
+ static const PySlot my_slots[] = {
+ PySlot_STATIC_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_STATIC_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 initialization 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`
+
+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)
+* :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 <pymemberdef-offsets>`)
+
+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 <pymemberdef-offsets>`
+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.
+If both are specified, 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 <limited-c-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:: 3.15
+ The :c:data:`Py_tp_bases` slot may be set to a single type object,
+ making it equivalent to the :c:data:`Py_tp_base` slot.
+ Previously, a tuple of types was required.
+
+The following slots correspond to fields in the underlying type structure,
+but need extra remarks for use as slots:
+
+.. c:macro:: Py_tp_name
+
+ :c:member:`Slot ID <PySlot.sl_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 name.
+
+ .. versionadded:: next
+
+.. c:macro:: Py_tp_basicsize
+
+ :c:member:`Slot ID <PySlot.sl_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 <PySlot.sl_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 rather than
+ 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 <PySlot.sl_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 <PySlot.sl_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
+
+The following slots do not correspond to public fields in the
+underlying structures:
+
+.. c:macro:: Py_tp_metaclass
+
+ :c:member:`Slot ID <PySlot.sl_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 <PySlot.sl_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 <PySlot.sl_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 <PySlot.sl_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 <heap-types>` 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.
+
+ 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() <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)
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)
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)
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
.. 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 <pyslot_type_slot_ids>` 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
.. 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
-
- <!-- Keep old URL fragments working (see gh-97908) -->
- <span id='c.PyType_Slot.PyType_Slot.slot'></span>
- <span id='c.PyType_Slot.PyType_Slot.pfunc'></span>
-
-.. 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:
+ <!-- Keep old URL fragments working (see gh-97908) -->
+ <span id='c.PyType_Slot.PyType_Slot.slot'></span>
+ <span id='c.PyType_Slot.PyType_Slot.pfunc'></span>
- * :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 <pymemberdef-offsets>`)
+ (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 <pymemberdef-offsets>`
- 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 <limited-c-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`.
.. c:member:: const char* PyTypeObject.tp_name
+ See :c:macro:`Py_tp_name` for the corresponding
+ :c:member:`Slot ID <PySlot.sl_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
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 <PySlot.sl_id>`.
+
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
.. c:member:: unsigned long PyTypeObject.tp_flags
+ See :c:macro:`Py_tp_flags` for the corresponding
+ :c:member:`Slot ID <PySlot.sl_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
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,,
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,,
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,,
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,,
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,,
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,,
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,,
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.
+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:
.. 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_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.
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.
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_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
};
/// Export hook prototype
* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
<whatsnew315-pybyteswriter>`
* :pep:`803`: :ref:`Stable ABI for Free-Threaded Builds <whatsnew315-abi3t>`
+* :pep:`820`: :ref:`PySlot: Unified slot system for the C API <whatsnew315-pyslot>`
* :ref:`The JIT compiler has been significantly upgraded <whatsnew315-jit>`
* :ref:`Improved error messages <whatsnew315-improved-error-messages>`
* :ref:`The official Windows 64-bit binaries now use the tail-calling interpreter
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 <extension-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
#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"
#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 */
/* 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,
--- /dev/null
+#ifndef _Py_PYCORE_SLOTS_H
+#define _Py_PYCORE_SLOTS_H
+
+#ifndef Py_BUILD_CORE
+# error "this header requires Py_BUILD_CORE define"
+#endif
+
+#include <stdbool.h>
+
+/* Slot data 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;
+
+/* 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,
+ _PySlot_KIND_COMPAT,
+ _PySlot_KIND_SLOT,
+} _PySlot_KIND;
+
+typedef enum _PySlot_PROBLEM_HANDLING {
+ _PySlot_PROBLEM_ALLOW,
+ _PySlot_PROBLEM_DEPRECATED,
+ _PySlot_PROBLEM_REJECT,
+} _PySlot_PROBLEM_HANDLING;
+
+PyAPI_DATA(const char *const) _PySlot_names[];
+
+#define _PySlot_MAX_NESTING 5
+
+/* State for one nesting level of a slots iterator */
+typedef struct _PySlotIterator_state {
+ union {
+ // tagged by slot_struct_kind:
+ 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;
+} _PySlotIterator_state;
+
+#define _PySlot_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 / _PySlot_SEEN_ENTRY_BITS + 1];
+ _PySlot_KIND kind;
+ uint8_t recursion_level;
+ 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;
+
+} _PySlotIterator;
+
+/* Initialize an iterator using a PySlot array */
+PyAPI_FUNC(void)
+_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, const void *slots,
+ _PySlot_KIND kind);
+
+/* Reset a *successfully exhausted* iterator to the beginning.
+ * The *slots* must be the same as for the previous
+ * `_PySlotIterator_InitWithKind` call.
+ * (Unlike creating a new iterator, we can skip some validation after Rewind.)
+ */
+PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, const 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 rewinding.)
+ */
+PyAPI_FUNC(bool) _PySlotIterator_SawSlot(_PySlotIterator *, int);
+
+static inline const char *
+_PySlot_GetName(uint16_t id)
+{
+ if (id >= _Py_slot_COUNT) {
+ return "<unknown_slot>";
+ }
+ 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
--- /dev/null
+/* 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 Py_bf_getbuffer;
+ case 2:
+ return Py_bf_releasebuffer;
+ case 3:
+ return Py_mp_ass_subscript;
+ case 4:
+ return Py_mp_length;
+ 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;
+ }
+}
+
+static inline uint16_t
+_PySlot_resolve_mod_slot(uint16_t slot_id)
+{
+ switch (slot_id) {
+ case 1:
+ return Py_mod_create;
+ case 2:
+ return Py_mod_exec;
+ case 3:
+ return Py_mod_multiple_interpreters;
+ case 4:
+ return Py_mod_gil;
+ 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;
+ }
+}
+
+static inline void*
+_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)
+{
+ switch (slot_id) {
+ 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;
+}
+
+static inline void
+_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht, PySlot slot)
+{
+ switch (slot.sl_id) {
+ 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;
+ }
+}
+
+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 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 Py_mod_exec:
+ case Py_mod_abi:
+ 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 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_mod_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 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;
+ }
+}
+
+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 */
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
-
#endif /* New in 3.5 */
/* for Py_mod_multiple_interpreters: */
#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);
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, 15)
+PyAPI_FUNC(PyObject *) PyType_FromSlots(struct PySlot *slots);
+#endif
/* Generic type check */
PyAPI_FUNC(int) PyType_IsSubtype(PyTypeObject *, PyTypeObject *);
typedef struct PyMethodDef PyMethodDef;
typedef struct PyGetSetDef PyGetSetDef;
typedef struct PyMemberDef PyMemberDef;
+typedef struct PySlot PySlot;
typedef struct _object PyObject;
typedef struct _longobject PyLongObject;
--- /dev/null
+#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;
+ _Py_ANONYMOUS union {
+ uint32_t sl_reserved; // must be 0
+ };
+ _Py_ANONYMOUS union {
+ void *sl_ptr;
+ _Py_funcptr_t sl_func;
+ Py_ssize_t sl_size;
+ int64_t sl_int64;
+ uint64_t sl_uint64;
+ };
+};
+
+#define PySlot_OPTIONAL 0x0001
+#define PySlot_STATIC 0x0002
+#define PySlot_INTPTR 0x0004
+
+#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=(_Py_funcptr_t)(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 {0}
+
+
+// Macros without designated initializers (for C++11 and below):
+
+#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
-/* Do not renumber the file; these numbers are part of the stable ABI. */
-#define Py_bf_getbuffer 1
-#define Py_bf_releasebuffer 2
-#define Py_mp_ass_subscript 3
-#define Py_mp_length 4
+/* 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) NEW
+#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_am_await 77
#define Py_am_aiter 78
#define Py_am_anext 79
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000
-/* New in 3.5 */
#define Py_tp_finalize 80
-#endif
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
-/* New in 3.10 */
#define Py_am_send 81
-#endif
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
-/* New in 3.14 */
#define Py_tp_vectorcall 82
-#endif
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
-/* New in 3.14 */
#define Py_tp_token 83
-#endif
+#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 /* _PY_HAVE_SLOTS_GENERATED_H */
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
)
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)
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,
)
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,
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):
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"""
- 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)
--- /dev/null
+from test.support import import_helper, subTests
+import contextlib
+import unittest
+import types
+import sys
+
+_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
+_testcapi = import_helper.import_module('_testcapi')
+
+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.assertGreaterEqual(cls.__basicsize__, 256)
+
+ cls = _testlimitedcapi.type_from_slots("extra_basicsize")
+ self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
+
+ cls = _testlimitedcapi.type_from_slots("itemsize")
+ self.assertGreaterEqual(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.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
+
+ cls = _testlimitedcapi.type_from_slots("optional_invalid_fbad")
+ self.assertGreaterEqual(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 PyType_Spec 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())
+
+ 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())
+
+ 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)
+
+ @subTests("slot_number", [4, 87])
+ def test_compat_slot_number_gil(self, slot_number):
+ spec = FakeSpec()
+ gil_enabled = sys._is_gil_enabled()
+ mod = _testlimitedcapi.module_from_gil_slot(slot_number, spec)
+ self.assertEqual(gil_enabled, sys._is_gil_enabled())
+
+ 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"):
+ _testcapi.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 in {2, 85} or slot_number > 85:
+ # Py_mod_exec, 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:
+ _testcapi.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())
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_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_STATIC_DATA(Py_mod_methods, _testcext_methods),
+ PySlot_END,
};
_Py_COMP_DIAG_POP
"PyType_Freeze",
"PyType_FromMetaclass",
"PyType_FromModuleAndSpec",
+ "PyType_FromSlots",
"PyType_FromSpec",
"PyType_FromSpecWithBases",
"PyType_GenericAlloc",
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 \
$(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 \
$(srcdir)/Include/traceback.h \
$(srcdir)/Include/tupleobject.h \
- $(srcdir)/Include/typeslots.h \
$(srcdir)/Include/unicodeobject.h \
$(srcdir)/Include/warnings.h \
$(srcdir)/Include/weakrefobject.h \
$(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_slots_generated.h \
$(srcdir)/Include/internal/pycore_stats.h \
$(srcdir)/Include/internal/pycore_strhex.h \
$(srcdir)/Include/internal/pycore_stackref.h \
# "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
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: Python/slots.toml
+ # Regenerate {Python,Include}/slots_generated.{c,h}
+ # from Python/slots.toml 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)
--- /dev/null
+Implement :pep:`820`: Unified slot system for the C API.
[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]
[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'
[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)
[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]
[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]
added = '3.15'
[macro.Py_END_CRITICAL_SECTION2]
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.
+
[function.PyLong_GetNativeLayout]
added = '3.15'
[function.PyLong_Export]
@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
static PyObject *
module_from_slots_empty(PyObject *self, PyObject *spec)
{
- PyModuleDef_Slot slots[] = {
- {0},
+ PySlot slots[] = {
+ PySlot_END,
};
return PyModule_FromSlotsAndSpec(slots, 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);
}
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);
}
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);
}
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) {
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_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,
};
return PyModule_FromSlotsAndSpec(slots, spec);
}
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) {
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) {
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) {
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);
}
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_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,
};
return PyModule_FromSlotsAndSpec(slots, 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_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,
};
return PyModule_FromSlotsAndSpec(slots, 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);
}
.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);
}
return PyLong_FromSsize_t(size);
}
+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 = {
+ PyModuleDef_HEAD_INIT,
+ .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_FromDefAndSpec(&def, spec);
+}
+
+static PyObject *
+module_from_def_nonstatic_nested(PyObject* Py_UNUSED(module), PyObject *spec)
+{
+ static PyModuleDef_Slot subsubslots[] = {
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+ {0},
+ };
+ static PySlot subslots[] = {
+ PySlot_DATA(Py_mod_slots, subsubslots),
+ PySlot_END,
+ };
+ static PyModuleDef_Slot slots[] = {
+ {Py_slot_subslots, subslots},
+ {0},
+ };
+ static PyModuleDef def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "mymod",
+ .m_slots = slots,
+ };
+ return PyModule_FromDefAndSpec(&def, spec);
+}
+
static PyMethodDef test_methods[] = {
{"module_from_slots_empty", module_from_slots_empty, METH_O},
{"module_from_slots_minimal", module_from_slots_minimal, METH_O},
{"pymodule_get_def", pymodule_get_def, METH_O},
{"pymodule_get_state_size", pymodule_get_state_size, METH_O},
{"pymodule_exec", pymodule_exec, METH_O},
+ {"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},
};
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;
}
if (_PyTestLimitedCAPI_Init_Set(mod) < 0) {
return NULL;
}
+ if (_PyTestLimitedCAPI_Init_Slots(mod) < 0) {
+ return NULL;
+ }
if (_PyTestLimitedCAPI_Init_Sys(mod) < 0) {
return NULL;
}
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);
--- /dev/null
+#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; decrement 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_remainder, 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_remainder, 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_remainder, 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_remainder, 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_remainder, 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_SystemError, "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((uint16_t)slot_number, NULL),
+ PySlot_END
+ });
+}
+
+static PyObject *
+type_from_null_spec_slot(PyObject* Py_UNUSED(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 *
+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)
+{
+ PyObject *spec;
+ char *case_name;
+ if (!PyArg_ParseTuple(args, "sO", &case_name, &spec)) {
+ return NULL;
+ }
+ PyObject *mod = NULL;
+#define CASE(NAME) \
+ if (strcmp(case_name, NAME) == 0) { \
+ mod = PyModule_FromSlotsAndSpec((PySlot[]) { \
+ PySlot_DATA(Py_mod_abi, &abi_info), \
+ PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), \
+ /////////////////////////////////////////////////////////////////////////
+#define ENDCASE() \
+ PySlot_END \
+ }, spec); \
+ } \
+ /////////////////////////////////////////////////////////////////////////
+
+ CASE("basic")
+ ENDCASE()
+ 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("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")
+ // 1: see old_slot_number_create case
+ PySlot_FUNC(2, demo_exec),
+ PySlot_DATA(3, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+ // 4: see module_from_gil_slot function
+ ENDCASE()
+ CASE("new_slot_numbers")
+ // 84: see new_slot_number_create case
+ PySlot_FUNC(85, demo_exec),
+ PySlot_FUNC(86, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+ // 87: see module_from_gil_slot function
+ ENDCASE()
+ CASE("old_slot_number_create")
+ PySlot_FUNC(1, demo_create),
+ 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
+ if (!mod) {
+ if (!PyErr_Occurred()) {
+ PyErr_Format(PyExc_SystemError, "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", (long)size) < 0) {
+ Py_DECREF(mod);
+ return NULL;
+ }
+ if (PyModule_Exec(mod) < 0) {
+ return NULL;
+ }
+ }
+ return mod;
+}
+
+static PyObject *
+module_from_gil_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_PTR_STATIC((uint16_t)slot_number, Py_MOD_GIL_NOT_USED),
+ PySlot_END
+ }, spec);
+}
+
+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;
+ }
+ uint16_t maybe_gil_slot = Py_mod_gil;
+ if ((slot_number == 4) || (slot_number == 87)) {
+ // Do not repeat the GIL slot
+ maybe_gil_slot = Py_slot_invalid;
+ }
+ return PyModule_FromSlotsAndSpec((PySlot[]) {
+ PySlot_DATA(Py_mod_abi, &abi_info),
+ PySlot_DATA(Py_mod_name, "mymod"),
+ PySlot_PTR_STATIC((uint16_t)slot_number, NULL),
+ {
+ .sl_id=maybe_gil_slot,
+ .sl_flags=PySlot_OPTIONAL,
+ .sl_ptr=Py_MOD_GIL_NOT_USED,
+ },
+ PySlot_END
+ }, spec);
+}
+
+static PyMethodDef _TestMethods[] = {
+ {"type_from_slots", type_from_slots, METH_VARARGS},
+ {"module_from_gil_slot", module_from_gil_slot, 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},
+ {"module_from_null_slot", module_from_null_slot, METH_VARARGS},
+ {NULL},
+};
+static PyMethodDef *TestMethods = _TestMethods;
+
+int
+_PyTestLimitedCAPI_Init_Slots(PyObject *m)
+{
+ if (PyModule_AddFunctions(m, TestMethods) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
/**** Problematic modules ****/
static PyModuleDef_Slot slots_bad_large[] = {
- {_Py_mod_LAST_SLOT + 1, NULL},
+ {Py_slot_invalid, NULL},
{0, NULL},
};
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_NOT_USED),
+ PySlot_END,
};
return slots;
}
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;
}
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;
}
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
}
-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
{"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_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),
+ PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+ PySlot_END,
};
return slots;
}
#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()
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 */
+module_from_slots_and_spec(
+ const PySlot *slots,
PyObject *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;
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);
+ }
+ it.name = name;
- 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) \
- do { \
- if (!(TYPE)(cur_slot->value)) { \
- PyErr_Format( \
- PyExc_SystemError, \
- "module %s: %s must not be NULL", \
- name, SLOTNAME); \
- goto error; \
- } \
- DEST = (TYPE)(cur_slot->value); \
- } while (0); \
- /////////////////////////////////////////////////////////////////
-
- // Macro to copy a non-NULL, non-repeatable slot to def_like.
-#define COPY_DEF_SLOT(SLOTNAME, TYPE, 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; \
- if (orig_value != new_value) { \
- PyErr_Format( \
- PyExc_SystemError, \
- "module %s: %s conflicts with " \
- "PyModuleDef." #MEMBER, \
- name, SLOTNAME); \
- goto error; \
- } \
- } \
- COPY_NONNULL_SLOT(SLOTNAME, TYPE, (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) \
- do { \
- if (DEST) { \
- PyErr_Format( \
- PyExc_SystemError, \
- "module %s has more than one %s slot", \
- name, SLOTNAME); \
- goto error; \
- } \
- COPY_NONNULL_SLOT(SLOTNAME, TYPE, DEST) \
- } while (0); \
- /////////////////////////////////////////////////////////////////
-
- // Define the whole common case
-#define DEF_SLOT_CASE(SLOT, TYPE, MEMBER) \
- case SLOT: \
- COPY_DEF_SLOT(#SLOT, TYPE, MEMBER); \
- break; \
- /////////////////////////////////////////////////////////////////
- switch (cur_slot->slot) {
+ 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);
+ if (m_exec) {
+ PyErr_Format(
+ PyExc_SystemError,
+ "module %s has multiple Py_mod_exec slots",
+ name);
+ goto error;
+ }
+ m_exec = (_Py_modexecfunc)it.current.sl_func;
}
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;
+ {
+ uint64_t val = it.current.sl_uint64;
+ requires_gil = (val != (uint64_t)Py_MOD_GIL_NOT_USED);
}
- requires_gil = (cur_slot->value != Py_MOD_GIL_NOT_USED);
- has_gil_slot = 1;
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)
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 "
name);
goto error;
}
- COPY_NONDEF_SLOT("Py_mod_token", void*, token);
+ token = it.current.sl_ptr;
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;
- }
+ // 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 && !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,"
}
#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 (!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)
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 "
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(
"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
int
PyModule_ExecDef(PyObject *module, PyModuleDef *def)
{
- PyModuleDef_Slot *cur_slot;
-
if (alloc_state(module) < 0) {
return -1;
}
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_InitLegacy(&it, def->m_slots, _PySlot_KIND_MOD);
+ while (_PySlotIterator_Next(&it)) {
+ _Py_modexecfunc func;
+ switch (it.current.sl_id) {
+ case Py_slot_invalid:
return -1;
- }
- continue;
+ case Py_mod_exec:
+ func = (_Py_modexecfunc)it.current.sl_func;
+ if (run_exec_func(module, func) < 0) {
+ return -1;
+ }
+ break;
}
}
return 0;
#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
#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);
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)
*/
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)
{
return -1;
}
-PyObject *
-PyType_FromMetaclass(
- PyTypeObject *metaclass, PyObject *module,
- PyType_Spec *spec, PyObject *bases_in)
+
+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.
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 */
- 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;
+ Py_ssize_t basicsize = 0;
+ Py_ssize_t extra_basicsize = 0;
+ Py_ssize_t itemsize = 0;
+ int flags = 0;
+ void *token = NULL;
- 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");
- goto finally;
+ bool have_relative_members = false;
+ Py_ssize_t max_relative_offset = 0;
+
+ PyObject *bases_slot = NULL; /* borrowed from the slots */
+
+ _PySlotIterator it;
+
+ if (spec) {
+ assert(!slots);
+ if (spec->basicsize > 0) {
+ basicsize = spec->basicsize;
}
- switch (slot->slot) {
- case Py_tp_members:
- if (nmembers != 0) {
+ 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:
+ bases_slot = it.current.sl_ptr;
+ break;
+ case Py_tp_base:
+ if (!_PySlotIterator_SawSlot(&it, Py_tp_bases)) {
+ bases_slot = it.current.sl_ptr;
+ }
+ break;
+ case Py_tp_basicsize:
+ NO_SPEC;
+ basicsize = it.current.sl_size;
+ if (basicsize <= 0) {
PyErr_SetString(
PyExc_SystemError,
- "Multiple Py_tp_members slots are not supported.");
+ "Py_tp_basicsize must be positive");
goto finally;
}
- for (const PyMemberDef *memb = slot->pfunc; memb->name != NULL; memb++) {
+ break;
+ case Py_tp_extra_basicsize:
+ NO_SPEC;
+ 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:
+ NO_SPEC;
+ 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:
+ NO_SPEC;
+ flags = (int)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) {
+ if (memb->offset < 0) {
PyErr_SetString(
PyExc_SystemError,
- "With Py_RELATIVE_OFFSET, basicsize must be negative.");
- goto finally;
- }
- if (memb->offset < 0 || memb->offset >= -spec->basicsize) {
- 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;
}
}
break;
+ case Py_tp_token:
+ 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 (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;
}
}
+ #undef NO_SPEC
- /* 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) {
+ 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 (!extra_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++;
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),
* 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).
+ * (This is convoluted for backwards compatibility -- preserving priority
+ * of the various ways to specify bases)
*/
- bases = get_bases_tuple(bases_in, spec);
+ if (!bases_in) {
+ bases_in = bases_slot;
+ }
+ if (bases_in) {
+ if (PyTuple_Check(bases_in)) {
+ bases = Py_NewRef(bases_in);
+ }
+ else {
+ bases = PyTuple_Pack(1, bases_in);
+ }
+ }
+ else {
+ bases = PyTuple_Pack(1, &PyBaseObject_Type);
+ }
if (!bases) {
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.)
*/
- 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;
}
}
/* 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,
goto finally;
}
}
-
- Py_ssize_t itemsize = spec->itemsize;
+ if (basicsize == 0) {
+ /* Inherit */
+ basicsize = base->tp_basicsize;
+ }
/* Compute special offsets */
if (res == NULL) {
goto finally;
}
- res_start = (char*)res;
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);
res->_ht_tpname = _ht_tpname;
_ht_tpname = NULL; // Give ownership to the type
+ res->ht_token = token;
+
/* Copy the sizes */
type->tp_basicsize = basicsize;
type->tp_itemsize = itemsize;
- /* Copy all the ordinary slots */
+ /* Second pass of slots: copy most of them into the type */
- for (slot = spec->slots; slot->slot; slot++) {
- switch (slot->slot) {
+ _PySlotIterator_Rewind(&it, spec ? (void*)spec->slots : (void*)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:
{
/* 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;
}
}
break;
- case Py_tp_token:
- {
- res->ht_token = slot->pfunc == Py_TP_USE_SPEC ? spec : slot->pfunc;
- }
- break;
default:
- {
- /* Copy other slots directly */
- PySlot_Offset slotoffsets = pyslot_offsets[slot->slot];
- short slot_offset = slotoffsets.slot_offset;
- if (slotoffsets.subslot_offset == -1) {
- /* Set a slot in the main PyTypeObject */
- *(void**)((char*)res_start + slot_offset) = slot->pfunc;
- }
- else {
- void *procs = *(void**)((char*)res_start + slot_offset);
- short subslot_offset = slotoffsets.subslot_offset;
- *(void**)((char*)procs + subslot_offset) = slot->pfunc;
- }
- }
+ _PySlot_heaptype_apply_field_slot(res, it.current);
break;
}
}
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;
}
else {
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"builtin type %.200s has no __module__ attribute",
- spec->name))
+ it.name))
goto finally;
}
}
return (PyObject*)res;
}
+PyObject *
+PyType_FromSlots(PySlot *slots)
+{
+ return type_from_slots_or_spec(slots, NULL, NULL, NULL, NULL);
+}
+
+PyObject *
+PyType_FromMetaclass(
+ PyTypeObject *metaclass, PyObject *module,
+ PyType_Spec *spec, PyObject *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 *
}
void *
-PyType_GetSlot(PyTypeObject *type, int slot)
+PyType_GetSlot(PyTypeObject *type, int slot_in)
{
- void *parent_slot;
- int slots_len = Py_ARRAY_LENGTH(pyslot_offsets);
-
- if (slot <= 0 || slot >= slots_len) {
- 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)) {
- return NULL;
- }
- }
-
- parent_slot = *(void**)((char*)type + slot_offset);
- if (parent_slot == NULL) {
- return NULL;
- }
- /* Return slot directly if we have no sub slot. */
- if (pyslot_offsets[slot].subslot_offset == -1) {
- return parent_slot;
- }
- return *(void**)((char*)parent_slot + pyslot_offsets[slot].subslot_offset);
+ uint16_t slot = _PySlot_resolve_type_slot(slot_in);
+ return _PySlot_type_getslot(type, slot);
}
PyObject *
+++ /dev/null
-/* 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)},
+++ /dev/null
-#!/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()
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)
<ClCompile Include="..\Python\pytime.c" />
<ClCompile Include="..\Python\qsbr.c" />
<ClCompile Include="..\Python\remote_debugging.c" />
+ <ClCompile Include="..\Python\slots.c" />
+ <ClCompile Include="..\Python\slots_generated.c" />
<ClCompile Include="..\Python\specialize.c" />
<ClCompile Include="..\Python\structmember.c" />
<ClCompile Include="..\Python\suggestions.c" />
<ClCompile Include="..\Python\remote_debugging.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\Python\slots.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Python\slots_generated.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\Python\specialize.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
+ <ClCompile Include="..\Modules\_testlimitedcapi\slots.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\threadstate.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
+ <ClCompile Include="..\Modules\_testlimitedcapi\slots.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\testcapi_long.h" />
<ClCompile Include="..\Modules\_testlimitedcapi\threadstate.c" />
<ClInclude Include="..\Include\internal\pycore_setobject.h" />
<ClInclude Include="..\Include\internal\pycore_signal.h" />
<ClInclude Include="..\Include\internal\pycore_sliceobject.h" />
+ <ClInclude Include="..\Include\internal\pycore_slots.h" />
+ <ClInclude Include="..\Include\internal\pycore_slots_generated.h" />
<ClInclude Include="..\Include\internal\pycore_stackref.h" />
<ClInclude Include="..\Include\internal\pycore_stats.h" />
<ClInclude Include="..\Include\internal\pycore_strhex.h" />
<ClInclude Include="..\Include\refcount.h" />
<ClInclude Include="..\Include\setobject.h" />
<ClInclude Include="..\Include\sliceobject.h" />
+ <ClInclude Include="..\Include\slots.h" />
+ <ClInclude Include="..\Include\slots_generated.h" />
<ClInclude Include="..\Include\structmember.h" />
<ClInclude Include="..\Include\structseq.h" />
<ClInclude Include="..\Include\sysmodule.h" />
<ClCompile Include="..\Python\pythonrun.c" />
<ClCompile Include="..\Python\specialize.c" />
<ClCompile Include="..\Python\suggestions.c" />
+ <ClCompile Include="..\Python\slots.c" />
+ <ClCompile Include="..\Python\slots_generated.c" />
<ClCompile Include="..\Python\structmember.c" />
<ClCompile Include="..\Python\symtable.c" />
<ClCompile Include="..\Python\sysmodule.c">
<ClInclude Include="..\Include\sliceobject.h">
<Filter>Include</Filter>
</ClInclude>
+ <ClInclude Include="..\Include\slots.h">
+ <Filter>Include</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Include\slots_generated.h">
+ <Filter>Include</Filter>
+ </ClInclude>
<ClInclude Include="..\Include\stats.h">
<Filter>Include</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_sliceobject.h">
<Filter>Include\internal</Filter>
</ClInclude>
+ <ClInclude Include="..\Include\internal\pycore_slots.h">
+ <Filter>Include\internal</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Include\internal\pycore_slots_generated.h">
+ <Filter>Include\internal</Filter>
+ </ClInclude>
<ClInclude Include="..\Include\internal\pycore_strhex.h">
<Filter>Include\internal</Filter>
</ClInclude>
<ClCompile Include="..\Python\specialize.c">
<Filter>Python</Filter>
</ClCompile>
+ <ClCompile Include="..\Objects\slots.c">
+ <Filter>Objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Objects\slots_generated.c">
+ <Filter>Objects</Filter>
+ </ClCompile>
<ClCompile Include="..\Python\structmember.c">
<Filter>Python</Filter>
</ClCompile>
/* 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(
--- /dev/null
+/* Common handling of type/module slots
+ */
+
+#include "Python.h"
+
+#include "pycore_slots.h"
+
+#include <stdio.h>
+
+// Iterating through a recursive structure doesn't look great in a debugger.
+// Flip the #if to 1 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 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();
+}
+
+static void
+init_with_kind(_PySlotIterator *it, const 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->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_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, const void *slots,
+ _PySlot_KIND kind)
+{
+ init_with_kind(it, slots, kind, kind);
+}
+
+void
+_PySlotIterator_Rewind(_PySlotIterator *it, const 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);
+ 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 / _PySlot_SEEN_ENTRY_BITS;
+}
+
+static unsigned int
+seen_mask(uint16_t id)
+{
+ return ((unsigned int)1) << (id % _PySlot_SEEN_ENTRY_BITS);
+}
+
+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();
+ }
+}
+
+static int handle_first_run(_PySlotIterator *it);
+
+bool
+_PySlotIterator_Next(_PySlotIterator *it)
+{
+ MSG("next");
+ assert(it);
+ assert(!it->is_at_end);
+ assert(!PyErr_Occurred());
+
+ 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;
+ }
+
+ switch (it->state->slot_struct_kind) {
+ case _PySlot_KIND_SLOT: {
+ MSG("copying PySlot structure");
+ it->current = *it->state->slot;
+ } break;
+ case _PySlot_KIND_TYPE: {
+ MSG("converting PyType_Slot structure");
+ memset(&it->current, 0, sizeof(it->current));
+ 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 = (uint16_t)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);
+
+ 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 %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;
+ }
+ if (result->sl_id == Py_slot_end) {
+ MSG("sentinel slot, flags %x", (unsigned)flags);
+ if (flags & PySlot_OPTIONAL) {
+ 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;
+ }
+
+ 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);
+ 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) {
+ MSG("error (too much nesting)");
+ PyErr_Format(PyExc_SystemError,
+ "%s (slot %d): too many levels of nested slots",
+ _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;
+ 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");
+ /* 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_DTYPE_INT64: {
+ result->sl_int64 = (int64_t)(intptr_t)result->sl_ptr;
+ } break;
+ case _PySlot_DTYPE_UINT64: {
+ result->sl_uint64 = (uint64_t)(intptr_t)result->sl_ptr;
+ } break;
+ case _PySlot_DTYPE_PTR:
+ case _PySlot_DTYPE_FUNC:
+ case _PySlot_DTYPE_VOID:
+ break;
+ }
+ }
+
+ advance(it);
+ switch (_PySlot_get_dtype(result->sl_id)) {
+ case _PySlot_DTYPE_VOID:
+ case _PySlot_DTYPE_PTR:
+ MSG("result: %d (%s): %p",
+ (int)result->sl_id, _PySlot_GetName(result->sl_id),
+ (void*)result->sl_ptr);
+ break;
+ case _PySlot_DTYPE_FUNC:
+ MSG("result: %d (%s): %p",
+ (int)result->sl_id, _PySlot_GetName(result->sl_id),
+ (void*)result->sl_func);
+ break;
+ case _PySlot_DTYPE_SIZE:
+ MSG("result: %d (%s): %zd",
+ (int)result->sl_id, _PySlot_GetName(result->sl_id),
+ (Py_ssize_t)result->sl_size);
+ break;
+ case _PySlot_DTYPE_INT64:
+ MSG("result: %d (%s): %ld",
+ (int)result->sl_id, _PySlot_GetName(result->sl_id),
+ (long)result->sl_int64);
+ break;
+ case _PySlot_DTYPE_UINT64:
+ MSG("result: %d (%s): %lu (0x%lx)",
+ (int)result->sl_id, _PySlot_GetName(result->sl_id),
+ (unsigned long)result->sl_int64,
+ (unsigned long)result->sl_int64);
+ break;
+ }
+ assert (result->sl_id > 0);
+ assert (result->sl_id <= _Py_slot_COUNT);
+ if (it->is_first_run && (handle_first_run(it) < 0)) {
+ goto error;
+ }
+ return result->sl_id != Py_slot_end;
+ }
+ Py_UNREACHABLE();
+
+error:
+ it->current.sl_id = Py_slot_invalid;
+ return true;
+}
+
+/* Validate current slot, and do bookkeeping */
+static int
+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))
+ {
+ PyErr_Format(
+ PyExc_SystemError,
+ "%s requires PySlot_STATIC",
+ _PySlot_GetName(id));
+ return -1;
+ }
+ }
+
+ _PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id);
+ if (null_handling != _PySlot_PROBLEM_ALLOW) {
+ bool is_null = false;
+ switch (_PySlot_get_dtype(id)) {
+ case _PySlot_DTYPE_PTR: {
+ is_null = it->current.sl_ptr == NULL;
+ } break;
+ case _PySlot_DTYPE_FUNC: {
+ is_null = it->current.sl_func == NULL;
+ } break;
+ default: {
+ //Py_UNREACHABLE();
+ } break;
+ }
+ if (is_null) {
+ MSG("slot is NULL but shouldn't");
+ if (null_handling == _PySlot_PROBLEM_REJECT) {
+ MSG("error (NULL rejected)");
+ PyErr_Format(PyExc_SystemError,
+ "NULL not allowed for slot %s",
+ _PySlot_GetName(id));
+ 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");
+ }
+ }
+ }
+
+ _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 (duplicate_handling == _PySlot_PROBLEM_REJECT) {
+ MSG("error (duplicate rejected)");
+ PyErr_Format(
+ PyExc_SystemError,
+ "%s%s%s has multiple %s (%d) slots",
+ kind_name(it->kind),
+ it->name ? " " : "",
+ it->name ? it->name : "",
+ _PySlot_GetName(id),
+ (int)it->current.sl_id);
+ 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");
+ }
+ }
+ }
+ it->seen[seen_index(id)] |= seen_mask(id);
+ return 0;
+}
--- /dev/null
+# 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 switch to slots.csv)
+
+# Entries:
+# name: name of the slot
+# kind:
+# - 'type', 'mod': slots to create a particular kind of object
+# - 'slot': special slots applicable to any kind of object
+# - '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).
+# 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 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]
+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'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+must_be_static = true
+
+[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 = 'reject'
+must_be_static = true
+
+[73]
+name = 'Py_tp_getset'
+kind = 'type'
+is_type_field = true
+dtype = 'ptr'
+duplicates = 'deprecated'
+nulls = 'deprecated'
+must_be_static = true
+
+[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'
+duplicates = 'deprecated'
+nulls = 'allow'
+
+[84]
+name = 'Py_mod_create'
+kind = 'mod'
+dtype = 'func'
+nulls = 'deprecated'
+
+[85]
+name = 'Py_mod_exec'
+kind = 'mod'
+dtype = 'func'
+duplicates = 'allow' # only alowed in PyModuleDef.m_slots
+nulls = 'reject'
+
+[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'
+nulls = 'allow'
+
+[95]
+name = 'Py_tp_name'
+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'
+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'
+must_be_static = true
+
+[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'
+duplicates = 'allow'
+
+[110]
+name = 'Py_mod_token'
+kind = 'mod'
+dtype = 'ptr'
--- /dev/null
+/* Generated by Tools/build/generate_slots.py */
+
+#include "Python.h"
+#include "pycore_slots.h" // _PySlot_names
+
+const char *const _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
+};
"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
+]
--- /dev/null
+#!/usr/bin/python
+"""Generate type/module slot files
+"""
+
+# See the input file (Python/slots.toml) for a description of its format.
+
+import io
+import sys
+import json
+import tomllib
+import argparse
+import functools
+import contextlib
+import collections
+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.toml'
+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'
+
+TABLES = {
+ 'tp': 'ht_type',
+ 'am': 'as_async',
+ 'nb': 'as_number',
+ 'mp': 'as_mapping',
+ 'sq': 'as_sequence',
+ 'bf': 'as_buffer',
+}
+
+
+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):
+ 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', 'reject')
+
+ @functools.cached_property
+ def null_handling(self):
+ try:
+ return self._data['nulls']
+ except KeyError:
+ if self.kind == 'compat':
+ return 'allow'
+ if self.dtype in {'ptr', 'func'}:
+ 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)
+ result = [None] * len(toml_contents)
+ for key, data in toml_contents.items():
+ slot_id = int(key)
+ try:
+ if result[slot_id]:
+ raise ValueError(f'slot ID {slot_id} repeated')
+ 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"""
+ print(self.indent, end='', file=self.file)
+ 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 write_public_header(f, slots):
+ out = CWriter(f)
+ 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) NEW')
+ out(f'#else')
+ out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD')
+ out(f'#endif')
+ out()
+ compat_ids = {}
+ for slot in slots:
+ 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
+ if compat := compat_ids.get(slot.name):
+ slot_id = f'_Py_SLOT_COMPAT_VALUE({compat}, {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 write_private_header(f, slots):
+ out = CWriter(f)
+
+ def add_case(slot):
+ out(out(f' case {slot.id}:'))
+
+ slots_by_name = {slot.name: slot for slot in slots}
+
+ 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)')
+ 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.name};')
+ elif slot.kind in {kind, 'slot'}:
+ 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;')
+ out()
+ out(f'static inline void*')
+ out(f'_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)')
+ 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.name}:')
+ out(f' 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.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()
+ out(f'static inline void')
+ 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.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)')
+ 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:
+ if slot.kind == 'compat':
+ continue
+ handling = slot.duplicate_handling
+ results[handling.upper()].append(f'case {slot.name}:')
+ results.pop('REJECT')
+ for handling, cases in results.items():
+ for case in cases:
+ out(case)
+ 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:
+ 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.name}:')
+ results.pop('REJECT')
+ for handling, cases in results.items():
+ for case in cases:
+ out(case)
+ out(f' return _PySlot_PROBLEM_{handling};')
+ 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 */')
+
+
+def write_c(f, slots):
+ out = CWriter(f)
+ out('#include "Python.h"')
+ out('#include "pycore_slots.h" // _PySlot_names')
+ out()
+ with out.block(f'const char *const _PySlot_names[] =', end=';'):
+ for slot in slots:
+ out(f'"{slot.name}",')
+ out('NULL')
+
+
+@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', '--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.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
+
+ with open(args.input, 'rb') as f:
+ slots = parse_slots(f)
+
+ if args.jsonl:
+ for slot in slots:
+ print(json.dumps(slot.to_dict()))
+
+ 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:
+ write_c(f, slots)
+
+if __name__ == "__main__":
+ main(sys.argv)
Modules/_testcapi/heaptype.c - _testcapimodule -
Modules/_testcapi/mem.c - FmData -
Modules/_testcapi/mem.c - FmHook -
+Modules/_testcapi/module.c module_from_def_nonstatic_nested subslots -
Modules/_testcapi/object.c - MyObject_dealloc_called -
Modules/_testcapi/object.c - MyType -
Modules/_testcapi/structmember.c - test_structmembersType_OldAPI -
Modules/_testinternalcapi.c - Test_EvalFrame_Loads -
Modules/_testinternalcapi/interpreter.c - Test_EvalFrame_Resumes -
Modules/_testinternalcapi/interpreter.c - Test_EvalFrame_Loads -
+Modules/_testlimitedcapi/slots.c - TestMethods -
Modules/_testmultiphase.c - Example_Type_slots -
Modules/_testmultiphase.c - Example_Type_spec -
Modules/_testmultiphase.c - Example_methods -
Modules/_testmultiphase.c - slots_nonmodule_with_exec_slots -
Modules/_testmultiphase.c - testexport_methods -
Modules/_testmultiphase.c - uninitialized_def -
+Modules/_testmultiphase.c PyModExport__test_from_modexport slots -
+Modules/_testmultiphase.c PyModExport__test_from_modexport_gil_used slots -
+Modules/_testmultiphase.c PyModExport__test_from_modexport_create_nonmodule slots -
+Modules/_testmultiphase.c PyModExport__test_from_modexport_create_nonmodule_gil_used slots -
+Modules/_testmultiphase.c PyModExport__test_from_modexport_smoke slots -
+Modules/_testmultiphase.c - modexport_empty_slots -
+Modules/_testmultiphase.c - modexport_minimal_slots -
Modules/_testsinglephase.c - global_state -
Modules/_testsinglephase.c - static_module_circular -
Modules/_xxtestfuzz/_xxtestfuzz.c - _fuzzmodule -
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
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