Skip to content

Prevent module reinitialisation with multi-phase init #1978

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

Merged
merged 1 commit into from
May 28, 2025

Conversation

AA-Turner
Copy link
Contributor

cc @nedbat

xref:

Changes written by a human ;-)

The ctracer module still uses static types (e.g. CTracerType), so is not isolated and may have undefined behaviour or crashes on use in subintepreters or after Py_Finalize cycles. Here, we raise an explicit ImportError for such cases.

Also, change the Py_GIL_DISABLED check to a version check for the Py_mod_gil slot (it is only the unstable API that is guarded behind Py_GIL_DISABLED), use PyMODINIT_FUNC for the init function, and use designated initialisers (I find them easier to read).

A

@nedbat
Copy link
Owner

nedbat commented May 28, 2025

Thanks, human!

@nedbat nedbat merged commit 490ff2d into nedbat:master May 28, 2025
44 checks passed
@AA-Turner AA-Turner deleted the multi-phase-manual branch May 28, 2025 13:45
@nedbat
Copy link
Owner

nedbat commented May 31, 2025

Hmm, turns out this causes a problem in the coverage measurement of coverage itself. If I comment out the re-import check everything seems to work, but I need to understand more.

@AA-Turner
Copy link
Contributor Author

I added the check & error as multi-phase assumes that modules are safe for subinterpreters by default. The relevant documentation is https://docs.python.org/dev/howto/isolating-extensions.html. On a cursory examination I think CTracerType & CFileDispositionType would need to be converted to heap types. I don't know how coverage's self-measurement works though, and there might be a simpler fix there.

A

@nedbat
Copy link
Owner

nedbat commented May 31, 2025

This isn't about subinterpreters explicitly. The self-measurement involves deleting sys.modules["coverage"] and importing it again, which is when the failure happens. So it's all within one process. Would I still need to change the types? Or maybe just avoid initting them twice? There are also static interned strings which should only be interned once.

@AA-Turner
Copy link
Contributor Author

AA-Turner commented May 31, 2025

Sorry for the crossed wires. I was referring to this note:

All modules created using multi-phase initialization are expected to support sub-interpreters, or otherwise explicitly 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 Py_mod_multiple_interpreters slot.

Per that same section, sharing Python objects between module instances would likely cause crashes or undefined behavior.. The side effect of the explicit error is that deleting the module from sys.modules and then re-importing it is expected to & will fail, as the import system attempts to re-initialise it in the same process. Clearly unwanted behaviour here, though.

Would importlib.reload work instead of deleting modules from sys.modules? That function has no effect for extension modules & so wouldn't casue issues. Alternatively, could the ctracer extension module be exempted from the deletion?

A

@nedbat
Copy link
Owner

nedbat commented May 31, 2025

I'm assuming that the warning is actually about sharing mutable Python objects between instances. In my case, they are types and interned strings.

This commit (8381ea7) seems good, but I welcome more experienced views. (Updated to use a mutex: 2d39c8c (Update: never mind, PyMutex_etc is new in 3.13))

@nedbat
Copy link
Owner

nedbat commented Jun 11, 2025

This is now released as part of coverage 7.9.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants