GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-142417: Add PyInitConfig_SetInitCallback() function by vstinner · Pull Request #142420 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Dec 8, 2025 •
edited by github-actions Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Add init_callback and init_callback_arg members to PyConfig.

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

📚 Documentation preview 📚: https://cpython-previews--142420.org.readthedocs.build/

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner
pythongh-142417: Add PyInitConfig_SetInitCallback() function
5ff278c
Add init_callback and init_callback_arg members to PyConfig.
@vstinner
vstinner requested review from AA-Turner, FFY00, ZeroIntensity and ericsnowcurrently as code owners December 8, 2025 15:38
@bedevere-app bedevere-app Bot added the awaiting core review label Dec 8, 2025
@bedevere-app bedevere-app Bot mentioned this pull request Dec 8, 2025
Please restore _Py_InitializeMain or provide an alternate API for it #142417
Closed
@vstinner

vstinner commented Dec 8, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

Current documentation:

Set an initialization callback. It allows executing code as soon as the
Python interpreter is initialized, before the first import. For example, it
can be used to add a meta path importer into sys.meta_path.

Python is not fully initialized yet when the callback is called. For
example, sys.stdout may not exist yet.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Dec 9, 2025

Copy link
Copy Markdown
Member Author

@indygreg: Hi, would such C API fit your needs for PyOxidizer?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Dec 10, 2025

Copy link
Copy Markdown
Member Author

cc @encukou @serhiy-storchaka

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Dec 10, 2025

Copy link
Copy Markdown
Member

Python is not fully initialized yet when the callback is called. For example, sys.stdout may not exist yet.

That is extremely vague. If there are no concrete promises about what's available, and what APIs can be called, new versions of CPython are theoretically allowed to "break" callers.
For example: what can you import at this point?

Is the callback better than restoring older private "multi-phase" API?
Should it be unstable?
Is it possible that we'll need to add more callbacks at different points of the initialization?

Would it be better to add API specifically for adding a simple metapath entry?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Dec 10, 2025

Copy link
Copy Markdown
Member Author

That is extremely vague. If there are no concrete promises about what's available, and what APIs can be called, new versions of CPython are theoretically allowed to "break" callers.

It's vague on purpose, but you're right that it should be better specified.

Python 3.13 documentation specify what is available at the "core" phase and what is the "main" phase: https://docs.python.org/3.13/c-api/init_config.html#multi-phase-initialization-private-provisional-api

For example: what can you import at this point?

Good question. I just tested: you can import built-in modules at this point, but only built-in modules. importlib is not fully initialized yet.

Is the callback better than restoring older private "multi-phase" API?

The old API (Python 3.13 and older) allows not executing the "main" initialization phase, but IMO it makes no sense (there is not use case for that). What's needed is only to inject code (callback) between the "core" and the "main" initialization phase. IMO a callback API better fits the use case.

Is it possible that we'll need to add more callbacks at different points of the initialization?

I'm not aware of other points which would be interesting during the initialization.

Would it be better to add API specifically for adding a simple metapath entry?

The bypy code to inject the sys.meta_path hook is non-trivial:

https://github.com/kovidgoyal/bypy/blob/c53e47497c5cb3ae9d7eb640b93a28a64fddb96a/bypy/freeze/bypy-freeze.h#L753-L783

The PyOxidizer code is also complex:

https://github.com/indygreg/PyOxidizer/blob/1ceca8664c71f39e849ce4873e00d821504b32bd/pyembed/src/interpreter.rs#L264-L316

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
Enhance documentation
e62e56c
@vstinner

vstinner commented Dec 10, 2025

Copy link
Copy Markdown
Member Author

Python 3.13 documentation specify what is available at the "core" phase and what is the "main" phase: https://docs.python.org/3.13/c-api/init_config.html#multi-phase-initialization-private-provisional-api

I completed the documentation using Python 3.13 doc.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Dec 10, 2025

Copy link
Copy Markdown
Member

Good question. I just tested: you can import built-in modules at this point, but only built-in modules. importlib is not fully initialized yet.

That's worrying: it means that if we rewrite a module in Python, we break people's code. (For example, the bypy hook depends on marshal being built-in. In this case I guess they could switch to PyMarshal_ReadObjectFromString, but, that's still a code change...)

I completed the documentation using Python 3.13 doc.

What's worrying for metapath entries is that "importlib", "Path Configuration" and "sys.path initialization" come after the hook call. It sounds like any change in this are could break users of the hook.


This does look like it should be unstable at least.

But, I believe it would be best to bring the old private API back (deprecated) while a better alternative is designed:

  • I don't see the callback being a substantially better design, TBH.
  • Since the API was specified in a PEP, and was added before PEP-387 & PEP-689 which add better processes than "provisional" underscore-prefixed APIs, I think it should have had a deprecation period.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Dec 11, 2025

Copy link
Copy Markdown
Member Author

cc @ericsnowcurrently

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Dec 16, 2025

Copy link
Copy Markdown
Member Author

What's worrying for metapath entries is that "importlib", "Path Configuration" and "sys.path initialization" come after the hook call. It sounds like any change in this are could break users of the hook.

These things changed multiple times since PEP 587 (PyConfig C API) has been implemented, and it was just fine with users of the "Multi-Phase Initialization Private Provisional API". For example, the path configuration has been reimplemented in Python in Python 3.11 (Modules/getpath.py).

Since the API was specified in a PEP, and was added before PEP-387 & PEP-689 which add better processes than "provisional" underscore-prefixed APIs, I think it should have had a deprecation period.

Well, a provisional API has a different backward compatibility contract: https://docs.python.org/dev/glossary.html#term-provisional-API

A provisional API is one which has been deliberately excluded from the standard library’s backwards compatibility guarantees.

Anyway, the API has already been removed from Python 3.14.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Dec 17, 2025

Copy link
Copy Markdown
Member

A provisional API is one which has been deliberately excluded from the standard library’s backwards compatibility guarantees.

Anyway, the API has already been removed from Python 3.14.

It also says:

Such changes will not be made gratuitously – they will occur only if serious fundamental flaws are uncovered that were missed prior to the inclusion of the API.

What are the serious fundamental flaws that were missed?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
Merge branch 'main' into init_callback
3609183
@vstinner

vstinner commented Jan 7, 2026

Copy link
Copy Markdown
Member Author

That's worrying: it means that if we rewrite a module in Python, we break people's code. (...)

and

What's worrying for metapath entries is that "importlib", "Path Configuration" and "sys.path initialization" come after the hook call. It sounds like any change in this are could break users of the hook.

These issues already existed in Python 3.13 with the provisional _init_main=0 / _Py_InitializeMain() API. Proposed PyInitConfig_SetInitCallback() has the same issues than adding back _Py_InitializeMain() to Python 3.15. As I wrote previously, it's not an issue in practice, and this code already changed multiple times without breaking projects using _Py_InitializeMain().

But, I believe it would be best to bring the old private API back (deprecated) while a better alternative is designed

I removed the _Py_InitializeMain() API because I failed to find any user of this provisional/private API. But there are at least two users in the wild (bypy and PyOxidizer), so I would prefer to add a documented and tested public API to replace it.

I don't see the callback being a substantially better design, TBH.

Python 3.13 API allows to not execute the "main" initialization phase. This is not needed by 3rd party projects and I would like to avoid that. PyInitConfig_SetInitCallback() ensures that Python is always initialized the same way (always execute the "main" initialization phase).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Jan 8, 2026

Copy link
Copy Markdown
Member

These issues already existed in Python 3.13 with the provisional _init_main=0 / _Py_InitializeMain() API. Proposed PyInitConfig_SetInitCallback() has the same issues than adding back _Py_InitializeMain() to Python 3.15.

Right.
My question is: how is the new proposed API better? Is it worth breaking projects?

Python 3.13 API allows to not execute the "main" initialization phase.

Is that the only benefit?

I removed the _Py_InitializeMain() API because I failed to find any user of this provisional/private API. But there are at least two users in the wild (bypy and PyOxidizer), so I would prefer to add a documented and tested public API to replace it.

Where did you look?
PyPI is not a very good place to find users of embedding API & runtime initialization.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Jan 8, 2026

Copy link
Copy Markdown
Member Author

I propose replacing _Py_InitializeMain() with PyInitConfig_SetInitCallback() because I don't want to expose the concepts of "core initialization" and "main initialization" in the API. A callback is easier to document and explain.

Where did you look?

I don't recall, but I likely looked in PyPI top 15,000 projects.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Jan 8, 2026

Copy link
Copy Markdown
Member

I don't want to expose the concepts of "core initialization" and "main initialization" in the API.

Fair. I don't think that qualifies as a serious fundamental flaw though.

I likely looked in PyPI top 15,000 projects.

Yeah. The extension module index isn't a good place for users of runtime initialization API.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zooba

zooba commented Jan 12, 2026

Copy link
Copy Markdown
Member

I don't want to expose the concepts of "core initialization" and "main initialization" in the API

I think we should, though not necessarily under these names.

There's no reasons why embedders shouldn't be able to "initialize up to pre-importlib", do stuff, and then "initialize importlib and anything after it". That's more or less what we currently do, where the "do stuff" is running getpath.py.

It would also help our own tools, where we need to half-initialize the runtime in order to freeze modules during build, etc.

It might not directly fix the problem being looked at here, but it's the design we've been aiming towards for a decade now, so it ought to be the goal. If that means bringing back the API that was removed in the meantime, that seems fine. The only thing we gain by rushing initialisation API changes is cruft, so we're better off keeping the cruft we already have and getting the API design right.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zooba

zooba commented Jan 12, 2026

Copy link
Copy Markdown
Member

Oh, I'll also point out that I think it makes perfect structural sense for getpath.py to belong to python.exe rather than libpython.so, which would imply that it's possible to do the three steps using only the public API.

For practical reasons, we wouldn't make that a real move, but structurally I think it helps to visualise importlib as part of "Python the command-line tool" rather than "Python the embeddable library" (since when embedding, you often want a different kind of importer than importlib's defaults).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Jan 15, 2026

Copy link
Copy Markdown
Member Author

@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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
vstinner marked this pull request as draft January 15, 2026 17:04
@bedevere-app bedevere-app Bot removed the awaiting core review label Jan 15, 2026
@FFY00

FFY00 commented Jan 15, 2026

Copy link
Copy Markdown
Member

Oh, I'll also point out that I think it makes perfect structural sense for getpath.py to belong to python.exe rather than libpython.so, which would imply that it's possible to do the three steps using only the public API.

For practical reasons, we wouldn't make that a real move, but structurally I think it helps to visualise importlib as part of "Python the command-line tool" rather than "Python the embeddable library" (since when embedding, you often want a different kind of importer than importlib's defaults).

Unfortunately, I think that would break a number of use-cases, but we could definitely move the C API towards that.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zooba

zooba commented Jan 15, 2026

Copy link
Copy Markdown
Member

Unfortunately, I think that would break a number of use-cases, but we could definitely move the C API towards that

I assume this is agreeing with the half of my post saying that we couldn't actually remove getpath from libpython? It's unclear whether you're agreeing that removing getpath would break people who assume it'll be there or not, since more than half the quoted text is saying that it would break people if we actually did it.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gpshead
gpshead reviewed Jan 24, 2026
View reviewed changes

@gpshead gpshead left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

(I'm currently reviewing the API at a high level from the docs only)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Doc/c-api/init_config.rst Outdated
Comment on lines +246 to +251
Set an initialization callback. It allows executing code as soon as the
Python interpreter is initialized, before the first import. For example, it
can be used to add a meta path importer into :data:`sys.meta_path`.

When the callback is called, Python is only partially initialized. What's
available at this point:

@gpshead gpshead Jan 24, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

"executing code as soon as the Python interpreter is initialized"
vs
"Python is only partially initialized. What's available ...:"

two different values of "initialized" here. That's a small list of what is available. Should we bother allowing people to call into Python at all or calling equivalent C Python C APIs from such an API? it feels maybe vaguely defined and hard to support and like something we might change the hyrum's law shape of what is available here?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Feb 6, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I changed the documentation to:

It allows executing code during the Python interpreter, before the first import.

@gpshead:

Should we bother allowing people to call into Python at all or calling equivalent C Python C APIs from such an API?

Most of the C API is available when the callback is called, including PyRun_SimpleString() high-level function.

@gpshead:

it feels maybe vaguely defined and hard to support and like something we might change the hyrum's law shape of what is available here?

It's vaguely defined on purpose, since the Python runtime is a complex beast, and it's hard to fully described its state while it's being initialized.

What can we do to make such API more future proof?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Doc/c-api/init_config.rst Outdated
Comment on lines +270 to +271
A single callback can be registered. If this function is called more than
once, the previous callback is overridden.

@gpshead gpshead Jan 24, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

We should probably start out by returning an error in this case. This is useful for embedding, it feels like multiple calls is always a bug until someone can make an argument otherwise.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

zooba reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@vstinner vstinner Feb 6, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Ok, I modified the API to fail if it's called twice.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner
Address Gregory's review
b220558
@vstinner

vstinner commented Feb 6, 2026

Copy link
Copy Markdown
Member Author

I updated my PR to address @gpshead's review.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Mar 3, 2026

Copy link
Copy Markdown
Member Author

Closed in favor of PR gh-145472.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner closed this Mar 3, 2026
@vstinner
vstinner deleted the init_callback branch March 3, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@gpshead gpshead gpshead left review comments
@ericsnowcurrently ericsnowcurrently Awaiting requested review from ericsnowcurrently ericsnowcurrently is a code owner
@ZeroIntensity ZeroIntensity Awaiting requested review from ZeroIntensity ZeroIntensity is a code owner
@FFY00 FFY00 Awaiting requested review from FFY00 FFY00 is a code owner
@AA-Turner AA-Turner Awaiting requested review from AA-Turner AA-Turner is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@vstinner @encukou @zooba @FFY00 @gpshead
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.