Skip to content

PEP 793: PyModExport: A new entry point for C extension modules #4435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

encukou
Copy link
Member

@encukou encukou commented May 23, 2025

Basic requirements (all PEP Types)

  • Read and followed PEP 1 & PEP 12
  • File created from the latest PEP template
  • PEP has next available number, & set in filename (pep-NNNN.rst), PR title (PEP 123: <Title of PEP>) and PEP header
  • Title clearly, accurately and concisely describes the content in 79 characters or less
  • Core dev/PEP editor listed as Author or Sponsor, and formally confirmed their approval
  • Author, Status (Draft), Type and Created headers filled out correctly
  • PEP-Delegate, Topic, Requires and Replaces headers completed if appropriate
  • Required sections included
    • Abstract (first section)
    • Copyright (last section; exact wording from template required)
  • Code is well-formatted (PEP 7/PEP 8) and is in code blocks, with the right lexer names if non-Python
  • PEP builds with no warnings, pre-commit checks pass and content displays as intended in the rendered HTML
  • Authors/sponsor added to .github/CODEOWNERS for the PEP

Standards Track requirements

  • PEP topic discussed in a suitable venue with general agreement that a PEP is appropriate
  • Suggested sections included (unless not applicable)
    • Motivation
    • Rationale
    • Specification
    • Backwards Compatibility
    • Security Implications
    • How to Teach This
    • Reference Implementation
    • Rejected Ideas
    • Open Issues
  • Python-Version set to valid (pre-beta) future Python version, if relevant
  • Any project stated in the PEP as supporting/endorsing/benefiting from the PEP formally confirmed such
  • Right before or after initial merging, PEP discussion thread created and linked to in Discussions-To and Post-History

📚 Documentation preview 📚: https://pep-previews--4435.org.readthedocs.build/pep-0793/

@encukou encukou requested a review from a team as a code owner May 23, 2025 16:39
@AA-Turner AA-Turner added the new-pep A new draft PEP submitted for initial review label May 23, 2025
Copy link
Member

@AA-Turner AA-Turner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

General points:

  • I think the PEP would benefit from more examples. Perhaps a full example of a module only using PyModExport_*, an example on porting, and an example on using both export hooks at once, e.g. for a module supporting Python 3.11--3.15?
  • The text is very heavy on parenthetical asides. I've suggested removing some, but in general I think where full sentences are in brackets, the text would be stronger by properly incorporating the thought and removing the brackets.
  • Please replace typographical quotation marks with "/', Sphinx/Docutils insert them automatically.
  • Naming (sorry!): Currently, PyMod* is only used by PyModInitFunction. Would PyModuleExport be considered?

A

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General: I think the PEP would benefit from a full worked example, perhaps in How to Teach This. It's been noted that one of the problems with multi-phase is a lack of guidance on how to do a migration. A simple example of a 'spam' module would help readers understand what the end state is meant to look like, and could be repurposed for the planned documentation.


On failure, the export hook must return NULL with an exception set.
This will cause the import to fail.
(Python will not fall back to ``PyInit_*`` on error.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps explicitly mention that defining both PyModExport_<NAME> and PyInit_<NAME> is allowed, and indeed expected for modules bridging Python versions?


If found, Python will call the hook with the appropriate
``importlib.machinery.ModuleSpec`` object as *spec*.
To support duck-typing, extensions should not type-check this object, and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To support duck-typing, extensions should not type-check this object, and
To support duck-typing, extension modules should not type-check this object, and

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“Extension” could mean the extension author, extension code, the shared library, the module object -- take your pick.
In this instance, any of that is appropriate, so I don't want to use a more specific term.

Comment on lines +188 to +190
(We expect functions to export a static constant, or one of several constants
chosen depending on, for example, ``Py_Version``. Dynamic behaviour should
generally happen in the ``Py_mod_create`` and ``Py_mod_exec`` functions.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(We expect functions to export a static constant, or one of several constants
chosen depending on, for example, ``Py_Version``. Dynamic behaviour should
generally happen in the ``Py_mod_create`` and ``Py_mod_exec`` functions.)
We expect functions to export a static constant, or one of several constants
chosen depending on, for example, ``Py_Version``. Dynamic behaviour should
generally happen in the ``Py_mod_create`` and ``Py_mod_exec`` functions.


.. code-block:: c

PyObject *PyModule_FromSlotsAndSpec(PyModuleDef_Slot *, PyObject *spec)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The export hook is part of the API, but the functions are defined in extension modules. Worth including?

Suggested change
PyObject *PyModule_FromSlotsAndSpec(PyModuleDef_Slot *, PyObject *spec)
PyModuleDef_Slot *PyModExport_<NAME>(PyObject *spec);
PyObject *PyModule_FromSlotsAndSpec(PyModuleDef_Slot *, PyObject *spec)

Comment on lines +367 to +368
Backwards Compatibility
=======================
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be greater emphasis on compatibility code for extension modules supporting older versions, but wanting to use the better/newer mechanism for 3.15+. Either in Backwards Compatibility or How to Teach This.

For instance, the multiple Py_mod_exec is solvable with a new function that calls all existing functions sequentially. Any suggested mechanisms #if ... for defining both a PyInit and PyModExport export hook? Anything that could go into pythoncapi-compat to make transition easier?

Comment on lines +450 to +451
A workaround is to check ``Py_Version`` in the export function,
and return a slot array suitable for the current interpreter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, use #ifdef Py_<slot> or #if PY_VERSION_HEX >= ....

Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Copy link
Member Author

@encukou encukou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the reviews! I went through some of the comments, will get to the rest later.
kid is waking up, I left some replies terse; please imagine a most friendly & respectful tone :)

I think the PEP would benefit from more examples. Perhaps a full example of a module only using PyModExport_*, an example on porting, and an example on using both export hooks at once, e.g. for a module supporting Python 3.11--3.15?

Agree, but these should end up in docs and I don't want people reading them here after acceptance. I'll add them to the reference implementation and point the PEP there.

The text is very heavy on parenthetical asides. I've suggested removing some, but in general I think where full sentences are in brackets, the text would be stronger by properly incorporating the thought and removing the brackets.

I use them to separate the hard spec from “mere“ implications/explanations. They are very helpful for me, and I like to think they're not too distracting for the reader.

Please replace typographical quotation marks with "/', Sphinx/Docutils insert them automatically.

I don't see a reason to do that. Any future editor is free to use " instead, it's not like Python identifiers which you need to repeat exactly somewhere else.

Naming (sorry!): Currently, PyMod* is only used by PyModInitFunction. Would PyModuleExport be considered?

That's a point for the discussion :)

(Yes, I considered it, but my conclusion is not strong enough for a Rejected Ideas entry and the question is not important enough for Open Questions. I'm literally waiting for the discussion thread here.)

Comment on lines +311 to +313
The slot will be optional, but extensions will be strongly encouraged to
include it for the benefit of future APIs, external tooling,
and debugging/introspection.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or maybe

Suggested change
The slot will be optional, but extensions will be strongly encouraged to
include it for the benefit of future APIs, external tooling,
and debugging/introspection.
The slot will be optional, but extension authors are strongly encouraged to
include it for the benefit of future APIs, external tooling, debugging,
and introspection.

Comment on lines +301 to +304
All new slots -- these and ``Py_tp_token`` discussed above -- may not be
repeated in the slots array, and may not be used in a
``PyModuleDef.m_slots`` array.
They may not have a ``NULL`` value (instead, the slot can be omitted entirely).
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to specifically point out that it's both the ones from the previous paragraph and Py_tp_token. This behaviour is “good“ for both (for reasons either obvious or mentioned in the Py_tp_token section), but the reviewer should think about them separately.

Comment on lines +221 to +224
For modules created without a ``PyModuleDef``, the ``Py_mod_create`` function
will be called with ``NULL`` for the second argument.
(In the future, if we find a use case for passing the input slots array, a new
slot with an updated signature can be added.)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For modules created without a ``PyModuleDef``, the ``Py_mod_create`` function
will be called with ``NULL`` for the second argument.
(In the future, if we find a use case for passing the input slots array, a new
slot with an updated signature can be added.)
For modules created without a ``PyModuleDef``, the ``Py_mod_create`` function
will be called with ``NULL`` for the second argument (*def*).
(In the future, if we find a use case for passing the input slots array, a new
slot with an updated signature can be added.)

Comment on lines +216 to +219
To simplify the implementation, the slots arrays for both
``PyModule_FromSlotsAndSpec`` and the new export hook will only allow up to one
``Py_mod_exec`` slot.
(Arrays in ``PyModuleDef.m_slots`` may have more; this will not change.)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that is the main argument. I don't want to maintain the code for that :)
I can do what I did for other minor inconveniences:

Suggested change
To simplify the implementation, the slots arrays for both
``PyModule_FromSlotsAndSpec`` and the new export hook will only allow up to one
``Py_mod_exec`` slot.
(Arrays in ``PyModuleDef.m_slots`` may have more; this will not change.)
To simplify the implementation, the slots arrays for both
``PyModule_FromSlotsAndSpec`` and the new export hook will only allow up to one
``Py_mod_exec`` slot.
(Arrays in ``PyModuleDef.m_slots`` may have more; this will not change.)
This limitation might be lifted in the future.

FWIW, I'd like to lift the other limitations in this PEP -- in dedicated PRs with loads of tests. This limitation isn't worth it IMO.


If found, Python will call the hook with the appropriate
``importlib.machinery.ModuleSpec`` object as *spec*.
To support duck-typing, extensions should not type-check this object, and
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“Extension” could mean the extension author, extension code, the shared library, the module object -- take your pick.
In this instance, any of that is appropriate, so I don't want to use a more specific term.


.. code-block:: c

PyModuleDef_Slot *PyModExport_<NAME>(PyObject *spec);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! I'll leave it for the API summary.

Comment on lines +150 to +153
Specification
=============


Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proper title for this subsection would be PyModExport: A new entry point for C extension modules. But it's already under that heading :)
This is the main thing; the rest is supporting API.


New API summary
---------------

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Python will load a new module export hook, with two variants:
.. code-block:: c
PyModuleDef_Slot *PyModExport_<NAME>(PyObject *spec);
PyModuleDef_Slot *PyModExportU_<ENCODED_NAME>(PyObject *spec);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new-pep A new draft PEP submitted for initial review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants