Skip to content

Please restore _Py_InitializeMain or provide an alternate API for it #142417

Closed

Description

@kovidgoyal

As discussed in #129033, opening a new issue at the request of @vstinner. See #129033 (comment) for the reasons this API is needed.

In short, a way to run python code and insert a meta importer into sys.meta_path before the "main" phase of initialization runs is needed. This allows, for instance, to bundle all python code as pre-compiled bytecode into a single mmapped file and directly load it from there for improved performance and hygiene.

I am aware that the state of the interpreter after "core" initialization is not entirely specified, however, in practice it is complete enough to write a meta path importer.

This facility has been used for many years by at least two projects that I know of: bypy (link to source code) https://github.com/kovidgoyal/bypy/blob/master/bypy/freeze/bypy-freeze.h#L835 and PyOxidizer: https://github.com/indygreg/PyOxidizer/blob/1ceca8664c71f39e849ce4873e00d821504b32bd/pyembed/src/technotes.rs#L155

I dont much care about this API in particular, but I would like some way to hook into the initialization process after "core" initialization and before "main" initialization.

Some context about use case. bypy is used in the calibre and kitty programs, both of which embed a "frozen" Python interpreter. _Py_InitializeMain() allows that frozen importer to import all its python code, including stdlib code from a single mmaped file contianing pre-compiled bytecode. This has obvious performance benefits especially on windows.

Linked PRs

Activity

  1. vstinner commented on Dec 8, 2025

    @vstinner
    Member

    If this feature is used, we should add a clean C API to use it, rather than just restore the private API to move back to the status quo.

    I dislike config._init_main=0 to stop the initialization at the "core" phase, and then having to call _Py_InitializeMain() to "finish" the initialization to the main phase.

    IMO a simple callback would be enough for bypy and PyOxidizer use cases: it would be called between core and main initialization phases. In practice, it would be called once the Python interpreter is initialized and before the first import. Something like:

    PyStatus PyConfig_SetInitCallback(PyStatus (*callback)(void *arg), void *arg);
    
    int PyInitConfig_SetInitCallback(PyInitConfig *config, PyStatus (*callback)(void *arg), void *arg);

    @kovidgoyal: Would it fit your use cases?

    cc @ncoghlan @ericsnowcurrently

  2. vstinner commented on Dec 8, 2025

    @vstinner
    Member

    For the 3.14 branch, we can restore the private _Py_InitializeMain() function, but only in Python 3.14.3. It means that Python 3.14.0, 3.14.1 and 3.14.2 cannot be used.

  3. kovidgoyal commented on Dec 8, 2025

    @kovidgoyal
    Author

    Yes, I can make that work in bypy. It will require a little bit of
    re-writing, but no big deal. Far more important for me is to have an
    official stable API for this. Whatever shape that API takes I can adapt
    to it, provided it allows me to run my callback immediately before the
    first import but after the interpreter is initialized. Your proposed
    PyConfig_SetInitCallback() would be fine for me.

    Personally, I am anyway patching Python 3.14 currently to restore that
    function (since I embed private python in my apps, that's easy to do).
    So to me it does not matter what happens in 3.14 lifecycle or later. I
    cant speak for other projects, of course.

  4. added 5 commits that reference this issue on Dec 8, 2025
  5. added a commit that references this issue on Dec 10, 2025
  6. added 3 commits that reference this issue on Jan 15, 2026
  7. vstinner commented on Jan 15, 2026

    @vstinner
    Member

    @encukou and @zooba dislike the callback approach (PR gh-142420), so I wrote PR gh-143877 to add Py_InitializeMain() function and rename PyConfig._init_main to PyConfig.init_main.

  8. kovidgoyal commented on Jan 15, 2026

    @kovidgoyal
    Author

    OK, works for me.

  9. zooba commented on Jan 15, 2026

    @zooba
    Member

    For the record, I also dislike adding it as a public API, and would prefer to just restore the private one until we've designed initialization properly.

  10. vstinner commented on Feb 6, 2026

    @vstinner
    Member

    @zooba:

    For the record, I also dislike adding it as a public API, and would prefer to just restore the private one until we've designed initialization properly.

    What do you mean by designing initialization properly? IMO the initialization callback is step towards a better initialization design (PR gh-142420).

    IMO adding back the private API is step backwards, I would prefer avoid that.

  11. zooba commented on Feb 6, 2026

    @zooba
    Member

    "A step" is not the better design. Do the entire design and let's discuss it as a whole, and if we all agree that it's better overall, then we'll implement the whole design.

    Until then, we should preserve what people are currently using.

  12. ncoghlan commented on Feb 23, 2026

    @ncoghlan
    Contributor

    I agree with @zooba on this one (unbreak users first, then add the cleaner design after discussing it without time pressure, then potentially remove the legacy private API only after folks consuming that private API have had the chance to add the relevant conditional usage of the new API).

    While I think we can probably do something cleaner with a public unstable API that exposes the relevant substeps in a nicer way than the current private API, exposing this stage of the startup as a regular public API seems like a bad idea (I know I proposed that myself way back in PEP 432, but there are a bunch of good reasons we never actually implemented that design, and these days I consider this one of them).

  13. added a commit that references this issue on Mar 3, 2026
  14. vstinner commented on Mar 3, 2026

    @vstinner
    Member

    The majority prefers restoring the private _Py_InitializeMain() function, rather than my proposed public C APIs: @encukou (comment), @zooba (comment) and @ncoghlan (comment).

    So I wrote PR gh-145472 to restore the private _Py_InitializeMain() function in Python 3.15.

  15. added a commit that references this issue on Mar 4, 2026
  16. vstinner commented on Mar 4, 2026

    @vstinner
    Member

    Please restore _Py_InitializeMain or provide an alternate API for it

    The change 31343cf restores _Py_InitializeMain() function.

    I close the issue.

  17. kovidgoyal commented on Mar 4, 2026

    @kovidgoyal
    Author

    Thanks!

  18. encukou commented on Mar 4, 2026

    @encukou
    Member

    The majority prefers restoring the private _Py_InitializeMain() function, rather than my proposed public C APIs

    Well, I see your proposal as a first draft, which now has time to growr. Hopefully we can make things better and not break users.

  19. added a commit that references this issue on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions