Concerns About PyUnstable_Module_SetGIL()

As I noted, implementing multi-phase init is fairly trivial [1]:

  1. move the content of the module’s init function to a corresponding “module exec” function (dropping the call to create the module object)
  2. set the module def’s m_slots field to an array with:
    • a Py_mod_exec slot, set to the new module exec function
    • a Py_mod_multiple_interpreters slot [2], set to Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED [3]
    • to try free-threading, a Py_mod_gil slot [4], set to Py_MOD_GIL_NOT_USED
  3. set def.m_size to 0 (if negative)
  4. update the module init function to only return PyModuleDef_Init(def); for the corresponding module def

(Also see Module Objects — Python 3.13.3 documentation and https://docs.python.org/3/howto/isolating-extensions.html.)

There’s no need to do any of the isolation stuff (heap types, module state, etc.) immediately.

At that point, the only extra step to run on a free-threading build would be to add the Py_mod_gil slot, set to Py_MOD_GIL_USED , etc.

IMHO, the free-threading build should always treat single-phase init modules as though they had Py_MOD_GIL_USED set.


  1. my original outline of the steps: https://github.com/python/cpython/pull/116882#discussion_r1586449047 ↩︎

  2. the default is Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED ↩︎

  3. think of Py_mod_multiple_interpreters as a hypothetical Py_mod_isolated, where Py_MOD_PER_INTERPRETER_GIL_SUPPORTED means “isolated” and Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED means “not isolated” ↩︎

  4. the default is Py_MOD_GIL_USED ↩︎

6 Likes