Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3b13a93
Add header for internal slot iterator API
encukou Jan 15, 2026
0396743
Add public API and a generated list of slots
encukou Jan 15, 2026
6da3bb1
Add slot iterator implementation & use it for types
encukou Jan 15, 2026
a8e018f
Unify duplicate & NULL handling
encukou Jan 16, 2026
b96acd4
Simplify slot handling in typeobject.c
encukou Jan 16, 2026
f3a6d7c
Port PyType_GetSlot; remove typeslots.inc
encukou Jan 16, 2026
5f13d2e
Port module creation
encukou Jan 16, 2026
4660f2f
Simplify internal API
encukou Jan 16, 2026
6c29000
Switch modexport over to new slots
encukou Jan 19, 2026
dfb342c
Fix up NULL handling
encukou Jan 19, 2026
110464d
Move PyType_FromMetaclass to slots
encukou Jan 19, 2026
6fb0046
PyType_FromSlots
encukou Apr 24, 2026
866eafc
slots.csv update
encukou Apr 24, 2026
325f1a8
squash up
encukou Apr 24, 2026
e22e3af
Put slot info in generated switches
encukou Apr 24, 2026
fea6e21
Handle PyType_Spec specially
encukou Jan 21, 2026
a89405c
Clean up the generator
encukou Jan 21, 2026
114a4fa
Remove slots.csv
encukou Jan 21, 2026
8dc5d88
Prettier generation
encukou Apr 24, 2026
796dd75
Tighten module creation
encukou Apr 24, 2026
b942f10
Fix math
encukou Apr 24, 2026
6a53b4c
Remove PySlot_HAS_FALLBACK
encukou Apr 24, 2026
f352558
Bump version
encukou Apr 24, 2026
bd78c06
Better comments
encukou Apr 24, 2026
181df86
Limit deprecations to new-style slots
encukou Apr 24, 2026
644ae46
Start on docs
encukou Apr 26, 2026
0433d1e
Require some slots to be static
encukou Apr 27, 2026
b3131a3
Add to Stable ABI
encukou Apr 27, 2026
b35230d
blurb, versionadded, whatsnew
encukou Apr 27, 2026
ae0ea29
Don't deprecate mixing the bases slots/args
encukou Apr 27, 2026
2b4be46
Add notes to stable_abi.toml
encukou Apr 27, 2026
ac7277f
Type tests
encukou Apr 27, 2026
c0427a0
Tests for modules
encukou Apr 27, 2026
d979cfd
Adjust for static/NULL handling
encukou Apr 27, 2026
3505d78
Don't spam
encukou Apr 27, 2026
b47a219
Fixups
encukou Apr 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Include/internal/pycore_uop_ids.h generated
Include/internal/pycore_uop_metadata.h generated
Include/opcode.h generated
Include/opcode_ids.h generated
Include/slots_generated.h generated
Include/token.h generated
Lib/_opcode_metadata.py generated
Lib/idlelib/help.html generated
Expand All @@ -99,7 +100,6 @@ Lib/token.py generated
Misc/sbom.spdx.json generated
Modules/_testinternalcapi/test_cases.c.h generated
Modules/_testinternalcapi/test_targets.h generated
Objects/typeslots.inc generated
PC/python3dll.c generated
Parser/parser.c generated
Parser/token.c generated
Expand All @@ -110,6 +110,7 @@ Python/generated_cases.c.h generated
Python/optimizer_cases.c.h generated
Python/opcode_targets.h generated
Python/record_functions.c.h generated
Python/slots_generated.c generated
Python/stdlib_module_names.h generated
Tools/peg_generator/pegen/grammar_parser.py generated
aclocal.m4 generated
Expand Down
16 changes: 8 additions & 8 deletions Doc/c-api/extension-modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Extension export hook

The export hook must be an exported function with the following signature:

.. c:function:: PyModuleDef_Slot *PyModExport_modulename(void)
.. c:function:: PySlot *PyModExport_modulename(void)

For modules with ASCII-only names, the :ref:`export hook <extension-export-hook>`
must be named :samp:`PyModExport_{<name>}`,
Expand All @@ -57,7 +57,7 @@ Python's *punycode* encoding with hyphens replaced by underscores. In Python:
suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
return b'PyModExport' + suffix

The export hook returns an array of :c:type:`PyModuleDef_Slot` entries,
The export hook returns an array of :c:type:`PySlot` entries,
terminated by an entry with a slot ID of ``0``.
These slots describe how the module should be created and initialized.

Expand All @@ -75,20 +75,20 @@ It is recommended to define the export hook function using a helper macro:
Declare an extension module export hook.
This macro:

* specifies the :c:expr:`PyModuleDef_Slot*` return type,
* specifies the :c:expr:`PySlot*` return type,
* adds any special linkage declarations required by the platform, and
* for C++, declares the function as ``extern "C"``.

For example, a module called ``spam`` would be defined like this::

PyABIInfo_VAR(abi_info);

static PyModuleDef_Slot spam_slots[] = {
{Py_mod_abi, &abi_info},
{Py_mod_name, "spam"},
{Py_mod_init, spam_init_function},
static PySlot spam_slots[] = {
PySlot_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
Expand Down
1 change: 1 addition & 0 deletions Doc/c-api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ document the API functions in detail.
refcounting.rst
exceptions.rst
extension-modules.rst
slots.rst
utilities.rst
abstract.rst
concrete.rst
Expand Down
123 changes: 80 additions & 43 deletions Doc/c-api/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,16 @@ Module Objects
unencodable filenames, use :c:func:`PyModule_GetFilenameObject` instead.


.. _c_module_slots:
.. _pymoduledef_slot:

Module definition
-----------------

Modules created using the C API are typically defined using an
array of :dfn:`slots`.
The slots provide a "description" of how a module should be created.
array of :c:type:`PySlot` structs, which provides a "description" of how a
module should be created.
See :ref:`capi-slots` for more information on slots in general.

.. versionchanged:: 3.15

Expand All @@ -158,30 +160,12 @@ Unless specified otherwise, the same slot ID may not be repeated
in an array of slots.


.. c:type:: PyModuleDef_Slot

.. c:member:: int slot

A slot ID, chosen from the available ``Py_mod_*`` values explained below.

An ID of 0 marks the end of a :c:type:`!PyModuleDef_Slot` array.

.. c:member:: void* value

Value of the slot, whose meaning depends on the slot ID.

The value may not be NULL.
To leave a slot out, omit the :c:type:`PyModuleDef_Slot` entry entirely.

.. versionadded:: 3.5


Metadata slots
..............

.. c:macro:: Py_mod_name

:c:type:`Slot ID <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
Expand All @@ -196,7 +180,7 @@ Metadata slots

.. 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`.
Expand All @@ -211,7 +195,7 @@ Feature slots

.. 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.

Expand All @@ -222,8 +206,8 @@ Feature slots

PyABIInfo_VAR(abi_info);

static PyModuleDef_Slot mymodule_slots[] = {
{Py_mod_abi, &abi_info},
static PySlot mymodule_slots[] = {
PySlot_DATA(Py_mod_abi, &abi_info),
...
};

Expand All @@ -237,7 +221,7 @@ Feature slots

.. 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

Expand Down Expand Up @@ -267,7 +251,7 @@ Feature slots

.. 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

Expand Down Expand Up @@ -296,7 +280,7 @@ Creation and initialization slots

.. 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:

Expand Down Expand Up @@ -346,7 +330,7 @@ Creation and initialization slots

.. 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.
Expand Down Expand Up @@ -375,7 +359,7 @@ Creation and initialization slots

.. 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`.

Expand Down Expand Up @@ -446,12 +430,12 @@ To retrieve the state from a given module, use the following functions:
Slots for defining module state
...............................

The following :c:member:`PyModuleDef_Slot.slot` IDs are available for
The following :c:member:`slot IDs <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
Expand All @@ -468,7 +452,7 @@ defining the module state.

.. 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,
Expand All @@ -491,7 +475,7 @@ defining the module state.

.. 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:
Expand Down Expand Up @@ -519,7 +503,7 @@ defining the module state.

.. 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:
Expand Down Expand Up @@ -578,7 +562,7 @@ A module's token -- and the *your_token* value to use in the above code -- is:

.. c:macro:: Py_mod_token

:c:type:`Slot ID <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:
Expand Down Expand Up @@ -617,14 +601,14 @@ Creating extension modules dynamically
The following functions may be used to create an extension module dynamically,
rather than from an extension's :ref:`export hook <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
Expand All @@ -640,10 +624,6 @@ rather than from an extension's :ref:`export hook <extension-export-hook>`.
must be called to fully initialize a module.
(See also :ref:`multi-phase-initialization`.)

The *slots* array only needs to be valid for the duration of the
:c:func:`!PyModule_FromSlotsAndSpec` call.
In particular, it may be heap-allocated.

.. versionadded:: 3.15

.. c:function:: int PyModule_Exec(PyObject *module)
Expand Down Expand Up @@ -738,6 +718,8 @@ remove it.
.. c:member:: PyModuleDef_Slot* m_slots

An array of additional slots, terminated by a ``{0, NULL}`` entry.
Note that the entries use the older :c:type:`PyModuleDef_Slot` structure,
rather than :c:type:`PySlot`.

If the array contains slots corresponding to :c:type:`PyModuleDef`
members, the values must match.
Expand All @@ -752,6 +734,39 @@ remove it.

.. c:member:: inquiry m_reload

.. c:namespace:: NULL

.. c:type:: PyModuleDef_Slot

Older structure defining additional slots of a module.

Note that a :c:type:`!PyModuleDef_Slot` array may be included in a
:c:type:`!PySlot` array using :c:macro:`Py_mod_slots`,
and vice versa using :c:macro:`Py_slot_subslots`.

Each :c:type:`!PyModuleDef_Slot` structure ``modslot`` is interpreted
as the following :c:type:`PySlot` structure::

(PySlot){
.sl_id=modslot.slot,
.sl_flags=PySlot_INTPTR | sub_static,
.sl_ptr=modslot.value
}

where ``sub_static`` is ``PySlot_STATIC`` if the slot requires
the flag (such as for :c:macro:`Py_mod_methods`), or if this flag
is present on the "parent" :c:macro:`!Py_mod_slots` slot (if any).

.. c:member:: int slot

Corresponds to :c:member:`PySlot.sl_id`.

.. c:member:: void* value

Corresponds to :c:member:`PySlot.sl_ptr`.

.. versionadded:: 3.5

.. c:member:: traverseproc m_traverse
inquiry m_clear
freefunc m_free
Expand All @@ -774,6 +789,14 @@ remove it.
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`
Expand Down Expand Up @@ -813,6 +836,10 @@ struct:

.. versionadded:: 3.5

.. soft-deprecated:: next

Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code.

.. c:function:: PyObject * PyModule_FromDefAndSpec2(PyModuleDef *def, PyObject *spec, int module_api_version)

Create a new module object, given the definition in *def* and the
Expand All @@ -833,12 +860,22 @@ struct:

.. versionadded:: 3.5

.. soft-deprecated:: next

Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code.

.. c:function:: int PyModule_ExecDef(PyObject *module, PyModuleDef *def)

Process any execution slots (:c:data:`Py_mod_exec`) given in *def*.

.. versionadded:: 3.5

.. soft-deprecated:: next

To run a module's own execution slots, prefer :c:func:`PyModule_Exec`,
which works on modules that were not created from a
:c:type:`PyModuleDef` structure.

.. c:macro:: PYTHON_API_VERSION
PYTHON_API_STRING

Expand Down
Loading
Loading