Skip to content

Commit 8a2e5aa

Browse files
committed
Soft-deprecate the old API, and make PyType_GetModuleByDef take token
1 parent 56d89df commit 8a2e5aa

File tree

2 files changed

+86
-19
lines changed

2 files changed

+86
-19
lines changed

peps/pep-0387.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ Basic policy for backwards compatibility
114114
platforms).
115115

116116

117+
.. _pep387-soft-deprecation:
118+
117119
Soft Deprecation
118120
================
119121

peps/pep-0793.rst

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ To make this viable, we also specify new module slot types to replace
2626

2727
We also add an API for defining modules from slots dynamically.
2828

29+
The existing API (``PyInit_*``) is soft-deprecated.
30+
(That is: it will continue to work without warnings, and it'll fe fully
31+
documented and supported, but we plan to not add any new features to it.)
32+
2933

3034
Background & Motivation
3135
=======================
@@ -147,6 +151,27 @@ Unlike types, the import mechanism often has a pointer that's known to be
147151
suitable as a token value; in these cases it can provide a default token.
148152
Thus, module tokens do not need a variant of the inelegant ``Py_TP_USE_SPEC``.
149153

154+
To help extensions that straddle Python versions, ``PyModuleDef`` addresses
155+
are used as default tokens, and where it's reasonable, they are made
156+
interchangeable with tokens.
157+
158+
159+
Soft-deprecating the existing export hook
160+
-----------------------------------------
161+
162+
The only reason for authors of *existing* extensions to switch to the
163+
API proposed here is that it allows a single module for both free-threaded
164+
and non-free-threaded builds.
165+
It is important that Python *allows* that, but for many existing modules,
166+
it is nowhere near worth losing compatibility with 3.14 and lower versions.
167+
168+
It is much too early to plan deprecation of the old API.
169+
170+
Instead, this PEP proposes to stop adding new features to the ``PyInit_*``
171+
scheme.
172+
After all, the perfect time for extension authors to switch is when they want
173+
to modify module initialization anyway.
174+
150175

151176
Specification
152177
=============
@@ -264,6 +289,10 @@ If specified, using a new ``Py_mod_token`` slot, the module token must:
264289

265290
(Typically, it should point to a static constant.)
266291

292+
When the address of a ``PyModuleDef`` is used as as a module's token,
293+
the module should behave as if it was created from that ``PyModuleDef``.
294+
In particular, the module state must have matching layout and semantics.
295+
267296
Modules created using the ``PyModule_FromSlotsAndSpec`` or the
268297
``PyModExport_<NAME>`` export hook can use a new ``Py_mod_token`` slot
269298
to set the token.
@@ -288,8 +317,15 @@ will return 0 on success and -1 on failure:
288317
int PyModule_GetToken(PyObject *, void **token_p)
289318
290319
A new ``PyType_GetModuleByToken`` function will be added, with a signature
291-
like ``PyType_GetModuleByDef`` but a ``void *token`` argument,
292-
and the same behaviour except matching tokens, rather than only defs.
320+
like the existing ``PyType_GetModuleByDef`` but a ``void *token`` argument,
321+
and the same behaviour except matching tokens rather than only defs.
322+
323+
For easier backwards compatibility, the existing ``PyType_GetModuleByDef``
324+
will be changed to work exactly like ``PyType_GetModuleByToken`` -- that is,
325+
it will allow a token (cast to a ``PyModuleDef *`` pointer) as the
326+
*def* argument.
327+
(The ``PyModule_GetDef`` function will not get a similar change, as users may
328+
access members of its result.)
293329

294330

295331
New slots
@@ -333,6 +369,14 @@ via a pointer; the function will return 0 on success and -1 on failure:
333369
int PyModule_GetStateSize(PyObject *, Py_ssize_t *result);
334370
335371
372+
Soft-deprecating the existing export hook
373+
-----------------------------------------
374+
375+
The ``PyInit_*`` export hook will be
376+
:ref:`soft-deprecated <pep387-soft-deprecation>`.
377+
378+
379+
336380
.. _pep793-api-summary:
337381

338382
New API summary
@@ -383,9 +427,14 @@ If an existing module is ported to use the new mechanism, then
383427
We claim that how a module was defined is an implementation detail of that
384428
module, so this should not be considered a breaking change.
385429

386-
Similarly, ``PyType_GetModuleByDef`` will not match modules that are not
387-
defined using a *def*.
388-
The new ``PyType_GetModuleByToken`` function may be used instead.
430+
Similarly, the ``PyType_GetModuleByDef`` function may stop matching modules
431+
whose definition changed. Module authors may avoid this by explicitly
432+
explicitly setting a *def* as the *token*.
433+
434+
``PyType_GetModuleByDef`` will now accept a module token as the *def* argument.
435+
We specify a suitable restriction on using ``PyModuleDef`` addresses as tokens,
436+
and non-``PyModuleDef`` pointers were previously invalid input,
437+
so this is not a backwards-compatibility issue.
389438

390439
The ``Py_mod_create`` function may now be called with ``NULL`` for the second
391440
argument.
@@ -428,11 +477,15 @@ wrappers, the :ref:`pep793-shim` below may be more useful.
428477
Later in this guide, you'll set the token to *be* the existing
429478
``PyModuleDef`` structure.
430479

431-
#. Scan your code for uses of ``PyType_GetModuleByDef``, and replace them by
432-
``PyType_GetModuleByToken``.
480+
#. Optionally, scan your code for uses of ``PyType_GetModuleByDef``,
481+
and replace them by ``PyType_GetModuleByToken``.
433482
Later in this guide, you'll set the token to *be* the existing
434483
``PyModuleDef`` structure.
435484

485+
(You may skip this step if targetting Python versions that don't expose
486+
``PyType_GetModuleByToken``, since ``PyType_GetModuleByDef`` is
487+
backwards-compatible. )
488+
436489
#. Look at the function identified by ``Py_mod_create``, if any.
437490
Make sure that it does not use its second argument (``PyModuleDef``),
438491
as it will be called with ``NULL``.
@@ -467,18 +520,17 @@ wrappers, the :ref:`pep793-shim` below may be more useful.
467520
};
468521
469522
#. If you switched from ``PyModule_GetDef`` to ``PyModule_GetToken``,
470-
and/or from ``PyType_GetModuleByDef`` to ``PyType_GetModuleByToken``,
523+
and/or if you use ``PyType_GetModuleByDef`` or ``PyType_GetModuleByToken``,
471524
add a ``Py_mod_token`` slot pointing to the existing ``PyModuleDef`` struct:
472525

473526
.. code-block:: c
474527
475528
static PyModuleDef_Slot module_slots[] = {
476529
// ... (keep existing slots here)
477-
{Py_mod_token, your_module_def},
530+
{Py_mod_token, &your_module_def},
478531
{0}
479532
};
480533
481-
482534
#. Add a new export hook.
483535

484536
.. code-block:: c
@@ -492,31 +544,43 @@ wrappers, the :ref:`pep793-shim` below may be more useful.
492544
}
493545
494546
The new export hook will be used on Python 3.15 and above.
495-
Once your module no longer supports lower versions, delete the ``PyInit_``
496-
function and any unused data.
547+
Once your module no longer supports lower versions:
497548

549+
#. Delete the ``PyInit_`` function.
550+
551+
#. If the existing ``PyModuleDef`` struct is used *only* for ``Py_mod_token``
552+
and/or ``PyType_GetModuleByToken``, you may remove the ``Py_mod_token``
553+
line and replace ``&your_module_def`` by ``module_slots`` everywhere else.
554+
555+
#. Delete any unused data.
556+
The ``PyModuleDef`` struct and the original slots array are likely to be
557+
unused.
558+
559+
560+
.. _pep793-shim:
498561

499562
Backwards compatibility shim
500563
----------------------------
501564

502-
It is possible to write generic function that implements the existing export
565+
It is possible to write generic function that implements the “old” export
503566
hook (``PyInit_``) in terms of the API proposed here.
504567

505-
The following implemntation can be copy-pasted; only the names
506-
``PyInit_examplemodule`` (twice) and ``PyModExport_examplemodule`` need adjusting.
568+
The following implementation can be copied and pasted to a project; only the
569+
names ``PyInit_examplemodule`` (twice) and ``PyModExport_examplemodule`` should
570+
need adjusting.
507571

508572
When added to the :ref:`pep793-example` below and compiled with a
509573
non-free-threaded build of this PEP's reference implementation, the resulting
510-
extension is compatible with regular builds 3.9+ in addition to a
574+
extension is compatible with non-free-threading 3.9+ builds, in addition to a
511575
free-threading build of the reference implementation.
512576
(The module must be named without a version tag, e.g. ``examplemodule.so``,
513577
and be placed on ``sys.path``.)
514578

515579
Full support for creating such modules will require backports of some new
516580
API, and support in build/install tools. This is out of scope of this PEP.
517-
(In particular, the demo “cheats” uses a subset Limited API 3.15 that
518-
*happens to work* on 3.9; it *should* use Limited API 3.9 with backport
519-
shims for new API like ``Py_mod_name``.)
581+
(In particular, the demo “cheats” by using a subset of Limited API 3.15 that
582+
*happens to work* on 3.9; a proper implementation would use Limited API 3.9
583+
with backport shims for new API like ``Py_mod_name``.)
520584

521585
This implementation places a few additional requirements on the slots array:
522586

@@ -589,6 +653,7 @@ Possible Future Directions
589653

590654
These ideas are out of scope for *this* proposal.
591655

656+
592657
Improving slots in general
593658
--------------------------
594659

0 commit comments

Comments
 (0)