Skip to content

Conversation

@bazaah
Copy link
Contributor

@bazaah bazaah commented Oct 16, 2025

Description

This is a rebased version of #5142.

Within, we switch to using PEP-489 multi-phase initialization for pyo3 modules, over the previous single-phase initialization method.

This work lays the foundation for several major upsides for pyo3 (and consumers therein):

  1. Improved control over pymodule initialization via rusty wrappers around the Py_mod_exec callback
  2. Per module state, allowing consumers to avoid the use of rust statics when managing pymodule state
  3. Support for subinterpreters

To be clear, this PR does not implement these, but it is a necessary base step towards them.

Of note, we also switch to gil_used = false by default in this PR, which may require consumers to explicitly opt in, if their pymodule is not thread safe. I expect this to be a relatively rare occurrence given rust's stance on thread safety.

Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Thanks very much for picking this up, it's been a constant want to move forward with this but only so many things I can proceed on at once!

Have placed a full build label on the PR, so if you push any changes it'll trigger a full run. Last I recall, GraalPy testing was not working properly so that'll be what we need to understand before we can merge.

Comment on lines 177 to 179
// TODO: remove this once we default to `gil_used = false` (i.e. assume free-threading
// support in extensions). This is purely a convenience so that users only need to
// annotate the top-level module.
Copy link
Member

Choose a reason for hiding this comment

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

I would be keen to switch the default to gil_used = false in PyO3 0.28, which would allow us to simplify here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll leave as is until I understand the graalpy failure, but it should be easy to drop the gil_used stuff out before final review

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed the gil_used_once stuff in ee6ebc3, and switched to gil_used = false by default in 84a512c.

I think these need a note in... somewhere. The migration guide maybe?

@bazaah bazaah force-pushed the feature/multi-phase-init branch from c84e6e5 to f056e32 Compare October 17, 2025 12:37
@bazaah bazaah force-pushed the feature/multi-phase-init branch from f056e32 to edb4193 Compare October 17, 2025 17:32
@davidhewitt davidhewitt added the CI-no-fail-fast If one job fails, allow the rest to keep testing label Oct 17, 2025
@bazaah
Copy link
Contributor Author

bazaah commented Oct 17, 2025

Interesting errors, I think this is because of

hint: Wheels are required for ... because building from source is disabled for all packages (i.e., with --no-build)

&

hint: You require GraalPy 3.11 (graalpy311), but we only found wheels for gevent (v23.9.0) with the following Python implementation tags: cp38, cp39, cp310, cp311, cp312

And I doubt there are prebuilts for the esoteric triples happening here. I'll look into this locally

@davidhewitt
Copy link
Member

I think those warnings are red herrings, we should be skipping tests for any packages which don't support graalpy.

@bazaah
Copy link
Contributor Author

bazaah commented Oct 20, 2025

Found the issue, eventually (thanks strace):

Function not implemented in GraalPy: PyModule_FromDefAndSpec2
Native stacktrace:
/home/<REDACTED>/.local/share/uv/python/graalpy-3.11.0-linux-x86_64-gnu/lib/graalpy24.2/libpython-native.so(+0x6e128) [0x7f7188390128]
/home/<REDACTED>/.local/share/uv/python/graalpy-3.11.0-linux-x86_64-gnu/lib/graalpy24.2/libpython-native.so(PyModule_FromDefAndSpec2+0xd) [0x7f718839223d]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x656a9) [0x7f717159d6a9]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x5ef78) [0x7f7171596f78]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x4f433) [0x7f7171587433]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x7b37c) [0x7f71715b337c]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x7c3c6) [0x7f71715b43c6]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x7d41b) [0x7f71715b541b]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x7ad98) [0x7f71715b2d98]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x72bf7) [0x7f71715aabf7]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x4f2d4) [0x7f71715872d4]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x4eb97) [0x7f7171586b97]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x70592) [0x7f71715a8592]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x4ea85) [0x7f7171586a85]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x4687a) [0x7f717157e87a]
/home/<REDACTED>/pyo3/examples/maturin-starter/.nox/python/lib/python3.11/site-packages/maturin_starter/maturin_starter.graalpy242-311-native-x86_64-linux.so(+0x409d6) [0x7f71715789d6]

Not sure how we want to handle this @davidhewitt?

@bazaah
Copy link
Contributor Author

bazaah commented Oct 20, 2025

Looks like graalpy supports these from v25.0.0 onwards: oracle/graalpython@9faded9

@davidhewitt
Copy link
Member

Aha great find! In that case I think we should drop support for GraalPy 3.11, it seems justified if it allows us to get a big fix in.

That can be done as a separate PR and then we can rebase here.

@bazaah
Copy link
Contributor Author

bazaah commented Oct 20, 2025

Okay, can you give me a quick rundown of what that entails? crate feature/cfgs, workflow bump?

@ngoldbaum
Copy link
Contributor

#5116 is probably a good example to look at

@bazaah
Copy link
Contributor Author

bazaah commented Oct 20, 2025

I get a clean test suite locally with graalpy-3.12.0-linux-x86_64-gnu, so once #5542 lands I'll cleanup this PR & switch to gil_used = false

@bazaah bazaah force-pushed the feature/multi-phase-init branch from b7bb908 to fc9dcee Compare October 21, 2025 08:42
@bazaah
Copy link
Contributor Author

bazaah commented Oct 21, 2025

Not sure how to deal with the codecov failure -- it seems it mostly thinks PyModuleSlotsBuilder::with_gil_used isn't covered, but it's just wrong as far as I can tell?

@bazaah bazaah marked this pull request as ready for review October 21, 2025 12:00
@bazaah bazaah force-pushed the feature/multi-phase-init branch from 84a512c to cf095d8 Compare October 22, 2025 09:20
Comment on lines 196 to 198
// Because the array is c-style, the empty elements should be zeroed.
// `std::mem::zeroed()` requires msrv 1.75 for const
values: [ZEROED_SLOT; N],
Copy link
Contributor

Choose a reason for hiding this comment

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

we recently bumped msrv to 1.83

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switch to mem::zeroed in 01b44ed, though I think the explicitness of ZEROED_SLOT is better, personally

This code was for user convenience when gil_used defaulted to true, but
since we're changing that to false, it is no longer needed.
@bazaah bazaah force-pushed the feature/multi-phase-init branch from cf095d8 to 01b44ed Compare October 23, 2025 11:17
Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Thanks very much for helping push this over the line! I am happy with this code as-is, and I'll follow up by writing migration notes for users both related to gil_used and for the change to multi-phase init.

PyO3 0.28 will probably be shipping around December, so perhaps with some more iteration we can figure out what's needed for users to (unsafely) opt-in to subinterpreter support in time.

(We have a lot of globals which are not subinterpreter safe, so it might be that we need to iterate gradually and accept it'll be sometime next year when the support lands.)

@davidhewitt davidhewitt added this pull request to the merge queue Oct 26, 2025
Merged via the queue into PyO3:main with commit 5589b17 Oct 26, 2025
82 of 84 checks passed
@bazaah bazaah deleted the feature/multi-phase-init branch October 27, 2025 09:14
@bazaah
Copy link
Contributor Author

bazaah commented Oct 27, 2025

Thanks David!

I'll push it for as long as I have time. I think next up is looking into how to support a public interface to module state, creating an inventory of the aforementioned globals, and then a plan for how we'll handle them. I expect module state will play a part in that plan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI-build-full CI-no-fail-fast If one job fails, allow the rest to keep testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants