[3.14] gh-134160: Improve multi-phase init note on isolation & subinterpreters (GH-134775) (#134932)

gh-134160: Improve multi-phase init note on isolation & subinterpreters (GH-134775)

(cherry picked from commit eb145fabbd)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
This commit is contained in:
Miss Islington (bot) 2025-05-31 19:41:07 +02:00 committed by GitHub
parent d6f356e1ab
commit f734531bbe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 13 deletions

View file

@ -288,22 +288,40 @@ An alternate way to specify extensions is to request "multi-phase initialization
Extension modules created this way behave more like Python modules: the Extension modules created this way behave more like Python modules: the
initialization is split between the *creation phase*, when the module object initialization is split between the *creation phase*, when the module object
is created, and the *execution phase*, when it is populated. is created, and the *execution phase*, when it is populated.
The distinction is similar to the :py:meth:`!__new__` and :py:meth:`!__init__` methods The distinction is similar to the :py:meth:`~object.__new__` and
of classes. :py:meth:`~object.__init__` methods of classes.
Unlike modules created using single-phase initialization, these modules are not Unlike modules created using single-phase initialization, these modules are not
singletons: if the *sys.modules* entry is removed and the module is re-imported, singletons.
a new module object is created, and the old module is subject to normal garbage For example, if the :py:attr:`sys.modules` entry is removed and the module
collection -- as with Python modules. is re-imported, a new module object is created, and typically populated with
By default, multiple modules created from the same definition should be fresh method and type objects.
independent: changes to one should not affect the others. The old module is subject to normal garbage collection.
This means that all state should be specific to the module object (using e.g. This mirrors the behavior of pure-Python modules.
using :c:func:`PyModule_GetState`), or its contents (such as the module's
:attr:`~object.__dict__` or individual classes created with :c:func:`PyType_FromSpec`). Additional module instances may be created in
:ref:`sub-interpreters <sub-interpreter-support>`
or after after Python runtime reinitialization
(:c:func:`Py_Finalize` and :c:func:`Py_Initialize`).
In these cases, sharing Python objects between module instances would likely
cause crashes or undefined behavior.
To avoid such issues, each instance of an extension module should
be *isolated*: changes to one instance should not implicitly affect the others,
and all state, including references to Python objects, should be specific to
a particular module instance.
See :ref:`isolating-extensions-howto` for more details and a practical guide.
A simpler way to avoid these issues is
:ref:`raising an error on repeated initialization <isolating-extensions-optout>`.
All modules created using multi-phase initialization are expected to support All modules created using multi-phase initialization are expected to support
:ref:`sub-interpreters <sub-interpreter-support>`. Making sure multiple modules :ref:`sub-interpreters <sub-interpreter-support>`, or otherwise explicitly
are independent is typically enough to achieve this. signal a lack of support.
This is usually achieved by isolation or blocking repeated initialization,
as above.
A module may also be limited to the main interpreter using
the :c:data:`Py_mod_multiple_interpreters` slot.
To request multi-phase initialization, the initialization function To request multi-phase initialization, the initialization function
(PyInit_modulename) returns a :c:type:`PyModuleDef` instance with non-empty (PyInit_modulename) returns a :c:type:`PyModuleDef` instance with non-empty

View file

@ -168,7 +168,7 @@ possible, consider explicit locking.
If it is necessary to use process-global state, the simplest way to If it is necessary to use process-global state, the simplest way to
avoid issues with multiple interpreters is to explicitly prevent a avoid issues with multiple interpreters is to explicitly prevent a
module from being loaded more than once per process—see module from being loaded more than once per process—see
`Opt-Out: Limiting to One Module Object per Process`_. :ref:`isolating-extensions-optout`.
Managing Per-Module State Managing Per-Module State
@ -207,6 +207,8 @@ An example of a module with per-module state is currently available as
example module initialization shown at the bottom of the file. example module initialization shown at the bottom of the file.
.. _isolating-extensions-optout:
Opt-Out: Limiting to One Module Object per Process Opt-Out: Limiting to One Module Object per Process
-------------------------------------------------- --------------------------------------------------