Skip to content

Commit 994347e

Browse files
committed
Porting guide, example, etc.
1 parent 1ce57c3 commit 994347e

2 files changed

Lines changed: 195 additions & 7 deletions

File tree

peps/pep-0793.rst

Lines changed: 131 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ Specification
152152

153153
The export hook
154154
---------------
155+
155156
When importing an extension module, Python will now first look for an export hook
156157
like this:
157158

@@ -213,10 +214,11 @@ future.
213214
The ``name`` will be used *instead of* the ``Py_mod_name`` slot (just like
214215
``PyModule_FromDefAndSpec`` ignores ``PyModuleDef.m_name``).
215216

216-
To simplify the implementation, the slots arrays for both
217-
``PyModule_FromSlotsAndSpec`` and the new export hook will only allow up to one
218-
``Py_mod_exec`` slot.
219-
(Arrays in ``PyModuleDef.m_slots`` may have more; this will not change.)
217+
The slots arrays for both ``PyModule_FromSlotsAndSpec`` and the new export hook
218+
will only allow up to one ``Py_mod_exec`` slot.
219+
Arrays in ``PyModuleDef.m_slots`` may have more; this will not change.
220+
This limitation is easy to work around and multiple ``exec`` slots are rarely
221+
used [#multiexec]_.
220222

221223
For modules created without a ``PyModuleDef``, the ``Py_mod_create`` function
222224
will be called with ``NULL`` for the second argument (*def*).
@@ -380,12 +382,117 @@ If an existing module is ported to use the new mechanism, then
380382
We claim that how a module was defined is an implementation detail of that
381383
module, so this should not be considered a breaking change.
382384

385+
Similarly, ``PyType_GetModuleByDef`` will not match modules that are not
386+
defined using a *def*.
387+
The new ``PyType_GetModuleByToken`` function may be used instead.
388+
383389
The ``Py_mod_create`` function may now be called with ``NULL`` for the second
384390
argument.
385391
This could trip people porting from *def* to *slots*, so it needs to be
386392
mentioned in porting notes.
387393

388394

395+
Forward compatibility
396+
---------------------
397+
398+
If a module defines the new export hook, CPython versions that implement this
399+
PEP will ignore the traditional ``PyInit_*`` hook.
400+
401+
Extensions that straddle vPython ersions are expected to define both hooks;
402+
each build of CPython will “pick” the newest one that it supports.
403+
404+
405+
.. _pep793-porting-notes:
406+
407+
Porting guide
408+
-------------
409+
410+
Here is a guide to convert an existing module to the new API, including
411+
some tricky edge cases.
412+
It should be moved to a HOWTO in the documentation.
413+
414+
#. Scan your code for uses of ``PyModule_GetDef``. This function will
415+
return ``NULL`` for modules that use the new mechanism. Instead:
416+
417+
* For getting the contents of the module's ``PyModuleDef``, use the C struct
418+
directly. Alternatively, get attributes from the module using, for
419+
example, ``PyModule_GetNameObject``, the ``__doc__`` attribute, and
420+
``PyModule_GetStateSize``.
421+
(Note that Python code can mutate a module's attributes.)
422+
* For testing if a module object is “yours”, use ``PyModule_GetToken``
423+
instead.
424+
Later in this guide, you'll set the token to *be* the existing
425+
``PyModuleDef`` structure.
426+
427+
#. Scan your code for uses of ``PyType_GetModuleByDef``, and replace them by
428+
``PyType_GetModuleByToken``.
429+
Later in this guide, you'll set the token to *be* the existing
430+
``PyModuleDef`` structure.
431+
432+
#. Look at the function identified by ``Py_mod_create``, if any.
433+
Make sure that it does not use its second argument (``PyModuleDef``),
434+
as it will be called with ``NULL``.
435+
Instead of the argument, use the existing ``PyModuleDef`` struct directly.
436+
#. If using multiple ``Py_mod_create`` slots, consolidate them: pick one of
437+
the functions, or write a new one, and call the others from it.
438+
Remove all but one ``Py_mod_create`` slots.
439+
#. Make a copy of the existing ``PyModuleDef_Slot`` array pointed to by
440+
the ``m_slots`` member of your ``PyModuleDef``. If you don't have an
441+
existing slots array, create one like this:
442+
443+
.. code-block:: c
444+
445+
static PyModuleDef_Slot module_slots[] = {
446+
{0}
447+
};
448+
449+
Give this array a unique name.
450+
Further examples will assume that you've named it ``module_slots``.
451+
452+
#. Add slots for all members of the existing ``PyModuleDef`` structure.
453+
See :ref:`pep793-api-summary` for a list of the new slots.
454+
For example, to add a name and docstring:
455+
456+
.. code-block:: c
457+
458+
static PyModuleDef_Slot module_slots[] = {
459+
{Py_mod_name, "mymodule"},
460+
{Py_mod_doc, (char*)PyDoc_STR("my docstring")},
461+
// ... (keep existing slots here)
462+
{0}
463+
};
464+
465+
#. If you switched from ``PyModule_GetDef`` to ``PyModule_GetToken``,
466+
and/or from ``PyType_GetModuleByDef`` to ``PyType_GetModuleByToken``,
467+
add a ``Py_mod_token`` slot pointing to the existing ``PyModuleDef`` struct:
468+
469+
.. code-block:: c
470+
471+
static PyModuleDef_Slot module_slots[] = {
472+
// ... (keep existing slots here)
473+
{Py_mod_token, your_module_def},
474+
{0}
475+
};
476+
477+
478+
#. Add a new export hook.
479+
480+
.. code-block:: c
481+
482+
PyMODEXPORT_FUNC PyModExport_examplemodule(PyObject);
483+
484+
PyMODEXPORT_FUNC
485+
PyModExport_examplemodule(PyObject *spec)
486+
{
487+
return module_slots;
488+
}
489+
490+
The new export hook will be used on Python 3.15 and above.
491+
Once your module no longer supports lower versions, delete the ``PyInit_``
492+
function and any unused data.
493+
494+
495+
389496
Security Implications
390497
=====================
391498

@@ -395,10 +502,16 @@ None known
395502
How to Teach This
396503
=================
397504

398-
In addition to regular reference docs, a guide for porting a module from
399-
*def* to *slots* will be added to the documentation.
505+
In addition to regular reference docs, the :ref:`pep793-porting-notes` should
506+
be added as a new HOWTO.
507+
508+
509+
Example
510+
=======
511+
512+
.. literalinclude:: pep-0793/examplemodule.c
513+
:language: c
400514

401-
We'll rewrite the "Extending and Embedding" tutorial to use this.
402515

403516

404517
Reference Implementation
@@ -475,6 +588,17 @@ The inittab is used for embedding, where a common/stable ABI is not that
475588
important. So, it might be OK to leave this to a later change.
476589

477590

591+
Footnotes
592+
=========
593+
594+
.. [#multiexec] A `quick survey <https://github.com/python/peps/pull/4435/files#r2105731314>`_
595+
of multiple ``Py_mod_exec`` slots found zero uses in the top 15,000 PyPI
596+
projects, and three in the stardard library (including tests for the
597+
feature).
598+
The easy workaround is consolidating the ``exec`` functions; see
599+
:ref:`pep793-porting-notes` for details.
600+
601+
478602
Copyright
479603
=========
480604

peps/pep-0793/examplemodule.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
Example module with C-level module-global state, and a simple function to
3+
update and query it.
4+
Once compiled and renamed to not include a version tag (for example
5+
examplemodule.so on Linux), this will run succesfully on both regular
6+
and free-threaded builds.
7+
8+
Python usage:
9+
10+
import examplemodule
11+
print(examplemodule.increment_value()) # 0
12+
print(examplemodule.increment_value()) # 1
13+
print(examplemodule.increment_value()) # 2
14+
print(examplemodule.increment_value()) # 3
15+
16+
*/
17+
18+
// Avoid CPython-version-specific ABI (inline functions & macros):
19+
#define Py_LIMITED_API 0x030f0000 // 3.15
20+
21+
#include <Python.h>
22+
23+
typedef struct {
24+
int value;
25+
} examplemodule_state;
26+
27+
PyObject *
28+
increment_value(PyObject *module, PyObject *_ignored)
29+
{
30+
examplemodule_state *state = PyModule_GetState(module);
31+
int result = ++(state->value);
32+
return PyLong_FromLong(result);
33+
}
34+
35+
PyMethodDef examplemodule_methods[] = {
36+
{"increment_value", increment_value, METH_NOARGS},
37+
{NULL}
38+
};
39+
40+
int examplemodule_exec(PyObject *module) {
41+
examplemodule_state *state = PyModule_GetState(module);
42+
state->value = -1;
43+
return 0;
44+
}
45+
46+
PyDoc_STRVAR(examplemodule_doc, "Example extension.");
47+
48+
static PyModuleDef_Slot examplemodule_slots[] = {
49+
{Py_mod_name, "examplemodule"},
50+
{Py_mod_doc, (char*)examplemodule_doc},
51+
{Py_mod_exec, (void*)examplemodule_exec},
52+
{Py_mod_methods, examplemodule_methods},
53+
{Py_mod_state_size, (void*)sizeof(examplemodule_state)},
54+
{0}
55+
};
56+
57+
// Avoid "implicit declaration of function" warning:
58+
PyMODEXPORT_FUNC PyModExport_examplemodule(PyObject *);
59+
60+
PyMODEXPORT_FUNC
61+
PyModExport_examplemodule(PyObject *spec)
62+
{
63+
return examplemodule_slots;
64+
}

0 commit comments

Comments
 (0)