-
Notifications
You must be signed in to change notification settings - Fork 2.2k
fix: allow subinterpreters to be manually disabled #5708
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
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.
Pull Request Overview
This PR introduces support for disabling subinterpreter features via the PYBIND11_HAS_SUBINTERPRETER_SUPPORT macro and updates tests and CI to handle that configuration.
- Define PYBIND11_HAS_SUBINTERPRETER_SUPPORT as 1 or 0 in
common.h
- Guard headers and internals on the new macro value
- Update tests to skip or importorskip modules, and add CI job with subinterpreter support disabled
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
File | Description |
---|---|
tests/test_multiple_interpreters.py | Fix skip message typo; switch to pytest.importorskip |
tests/test_embed/test_subinterpreter.cpp | Change #ifdef to #if for macro truthiness |
include/pybind11/subinterpreter.h | Replace #if !defined(...) with #if !MACRO |
include/pybind11/detail/internals.h | Switch #ifdef MACRO to #if MACRO |
include/pybind11/detail/common.h | Define PYBIND11_HAS_SUBINTERPRETER_SUPPORT as 1 or 0 |
.github/workflows/ci.yml | Remove push triggers; add CI matrix entry with macro set to 0 |
98ebffa
to
1b45bab
Compare
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
1b45bab
to
ba78f4c
Compare
# if PY_VERSION_HEX >= 0x030C0000 && !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON) | ||
# define PYBIND11_HAS_SUBINTERPRETER_SUPPORT 1 | ||
# else | ||
# define PYBIND11_HAS_SUBINTERPRETER_SUPPORT 0 | ||
# endif |
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.
Changing defined/undefined to 1/0 is a backward-incompatible change, which breaks preprocessor #ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT
.
undefined: false
defined but empty: true
defined as 1: true
defined as 0: false
See also:
/* Define to 1 when compiling for experimental free-threaded builds */
#ifdef Py_GIL_DISABLED
/* We undefine if it was set to zero because all later checks are #ifdef.
* Note that non-Windows builds do not do this, and so every effort should
* be made to avoid defining the variable at all when not desired. However,
* sysconfig.get_config_var always returns a 1 or a 0, and so it seems likely
* that a build backend will define it with the value.
*/
# if Py_GIL_DISABLED == 0
# undef Py_GIL_DISABLED
# endif
#endif
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.
Changing defined/undefined to 1/0 is a backward-incompatible change
I was aware of that when I reviewed this PR. My judgement: That's OK/best in this case, because that define was added only very recently, and was included only in release candidates.
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.
Release candidates exist exactly so that we can make last minute backward in compatible changes to new features. :)
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.
Though, honestly, I hadn't thought of that way of doing it, and if you think it's more consistent, I wouldn't be opposed to 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.
Does it make sense to undefine the macro if it equals 0?
Like CPython does for no-gil on Windows:
/* Define to 1 when compiling for experimental free-threaded builds */
#ifdef Py_GIL_DISABLED
/* We undefine if it was set to zero because all later checks are #ifdef.
* Note that non-Windows builds do not do this, and so every effort should
* be made to avoid defining the variable at all when not desired. However,
* sysconfig.get_config_var always returns a 1 or a 0, and so it seems likely
* that a build backend will define it with the value.
*/
# if Py_GIL_DISABLED == 0
# undef Py_GIL_DISABLED
# endif
#endif
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.
Every other define in pybind11 does work via ifdef, though...
Ah ... I remembered that wrong and didn't double-check.
PYBIND11_HAS_STD_LAUNDER
is a special beast, I wouldn't use that as a role model.
My vote:
- Remove
#else
#define PYBIND11_HAS_SUBINTERPRETER_SUPPORT 0
. - Let's say sorry for the back and forth between release candidates.
- In the (much) long(er) run people will be thankful.
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.
Remove
#else
#define PYBIND11_HAS_SUBINTERPRETER_SUPPORT 0
.
We may need to undefine the macro if it is defined as 0 before the first usage (see #5708 (comment)). The macro might be defined via the C flags, like what this PR did:
-DPYBIND11_HAS_SUBINTERPRETER_SUPPORT=0
Since the PYBIND11_HAS_SUBINTERPRETER_SUPPORT
macro is a new one and has not been officially released yet, I'm fine with whether it is controlled by presence or non-zeroness.
I workaround this with a macro (https://github.com/metaopt/optree/pull/227/files#diff-b552e0451de79baea976f15c4ee5458fa7b6faa75bebb8b3780a90ec3d40a427R56):
// NOLINTNEXTLINE[bugprone-macro-parentheses]
#define NONZERO_OR_EMPTY(MACRO) ((MACRO + 0 != 0) || (0 - MACRO - 1 >= 0))
#if defined(HAS_XXX) && NONZERO_OR_EMPTY(HAS_XXX)
// do something
#else
// do something else
#endif
#undef NONZERO_OR_EMPTY
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.
Applying the "keep it simple" and "be consistent" principles here:
I think this is all we need:
#ifndef PYBIND11_HAS_SUBINTERPRETER_SUPPORT
# if PY_VERSION_HEX >= 0x030C0000 && !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON)
# define PYBIND11_HAS_SUBINTERPRETER_SUPPORT 1
# endif
#endif
- There is nothing inherently wrong with what we have.
- The only reason we want to make a change is to be consistent with the existing macros.
- Doing something special (
#undef
) would be inconsistent. - Adding the
#undef
provision for all macros would add noise. - This really is a very minor issue, I wouldn't want to give it any more attention than we have already, as long as we come out with a consistent solution.
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.
No, I know what's needed, just teaching in an hour, so haven't had time to apply it. It's something like this:
#ifndef PYBIND11_HAS_SUBINTERPRETER_SUPPORT
# if PY_VERSION_HEX >= 0x030C0000 && !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON)
# define PYBIND11_HAS_SUBINTERPRETER_SUPPORT 1
# endif
#else
# if PYBIND11_HAS_SUBINTERPRETER_SUPPORT == 0
# undefine PYBIND11_HAS_SUBINTERPRETER_SUPPORT
# endif
#endif
Then after that, simple ifdefs can be used throughout the code instead of checking this one and only this one for 1/0.
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.
Sounds good!
Looking around some more after seeing @henryiii's suggestion:
I didn't realize before that PYBIND11_HAS_SUBINTERPRETER_SUPPORT
is the only PYBIND11_HAS_
macro in pybind11/detail/common.h for which we have the #ifndef
precondition.
Therefore: It's not inconsistent to add the #undef
.
(There is at least one other non-PYBIND11_HAS_
macro (PYBIND11_EXPORT_EXCEPTION
) that doesn't have such an #undef
, but oh well.)
Description
Pulled out of #5705.
Suggested changelog entry: