-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: change PYBIND11_MODULE to use multi-phase init (PEP 489) #5574
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
Conversation
5f6ff46
to
7723088
Compare
Use slots to specify advanced init options (currently just Py_GIL_NOT_USED). Adds a new function `initialize_multiphase_module_def` to module_, similar to the existing (unchanged) create_extension_module.
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're always using squash-merge.
To make reviewing easier (especially in follow-on PRs), could you please never force push?
include/pybind11/detail/common.h
Outdated
PYBIND11_MAYBE_UNUSED; \ | ||
PYBIND11_MAYBE_UNUSED \ | ||
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \ | ||
static std::vector<PyModuleDef_Slot> PYBIND11_CONCAT(pybind11_module_slots_, name); \ |
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.
Slight worries: Could we get into trouble during process teardown?
Concretely, could there be a situation in which slots.data()
is referenced after this static
variable was deleted already?
Probably not ... but maybe?
I'd want to err on the side of "abundance of caution".
I think we should use the standard trick of leaking a pointer. It's really simple, all doubts removed. Untested:
static auto *PYBIND11_CONCAT(pybind11_module_slots_, name) = new std::vector<PyModuleDef_Slot>();
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'd prefer not to leak a pointer unless we are sure it is needed. Isn't this already the lifetime of the module, though?
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.
Yes, this is a static global for the lifetime of the module... it ends up referenced inside the initialized PyModuleDef, which is defined right above it. So I don't think there's any way that the PyModuleDef could outlive it.
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.
It's standard best practice for non-POD static
objects.
There is no good reason for taking any risks (running destructors during process teardown; or freeing in the wrong order). The memory will be freed by the kernel for sure.
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 switched it to an std::array of slots, which is POD (since PyModuleDef_Slot is POD), so it has no destructor.... it's more along the lines of the original suggestion of using a named constant instead of a magic number for the size.... the size is part of the typedef (with a comment, ofc).
PS: Generally, I would not force push to answer a review comment, only to rebase or squash unimportant details. For those, I strongly recommend and encourage force pushing normally, so we aren't all of the same opinion.1 For addressing a review, though, you want the commit history clearly displayed. But @rwgk prefers avoiding it entirely, and he's the main reviewer, so please avoid in pybind11. :) Footnotes
|
Overall that works best for me, but I don't want to be an extremist :-) |
... by using an std::array for the slots.
I prefer not to force push but I've contributed to other projects where they really want everything squashed all the time... 🤷♂️ |
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.
Looks great to me, thanks!
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.
Thanks @b-pass!
I made a slight edit to the PR description. This sure is worth mentioning in the changelog. |
Description
Changed PYBIND11_MODULE to use multi-phase init. Multi-phase init (slots) is required for sub-interpreter support in (see #5564). This splits the init portion from that PR out into this smaller PR as requested by @rwgk .
This PR Adds a new function
init_module_def
tomodule_
, similar to the existing (unchanged)create_extension_module
.init_module_def
takes a variadic final argument to allow for future initialization tags (no tags are added in this PR, thepy::mod_gil_not_used
tag works with this new function).This also has the minor side benefit of not needing to use the unstable API (
PyUnstable_Module_SetGIL
) for thePy_MOD_GIL_NOT_USED
option. However, themodule_::create_extension_module
function has not been changed and so it still uses the unstable API.No tests are changed, the arguments to the macro are unchanged, and all behavior is expected to be the same. Multi-phase init has been supported since Python 3.5, so no compatibility issues are expected either.
Suggested changelog entry:
* Changed ``PYBIND11_MODULE`` macro implementation to perform multi-phase module initialization (PEP 489) behind the scenes.