Skip to content

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

Merged
merged 2 commits into from
May 31, 2025

Conversation

henryiii
Copy link
Collaborator

@henryiii henryiii commented May 31, 2025

Description

  • fix: allow subinterp support to be disabled
  • ci: drop main tests on main

Pulled out of #5705.

Suggested changelog entry:

  • Allow subinterpreter support to be disabled if defined to 0. This is mostly an emergency workaround, and is not exposed in CMake.

@henryiii henryiii marked this pull request as ready for review May 31, 2025 01:37
@henryiii henryiii requested a review from Copilot May 31, 2025 01:54
Copy link
Contributor

@Copilot Copilot AI left a 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

@henryiii henryiii force-pushed the henryiii/fix/nosubinterp branch from 98ebffa to 1b45bab Compare May 31, 2025 02:47
henryiii added 2 commits May 30, 2025 22:56
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
@henryiii henryiii force-pushed the henryiii/fix/nosubinterp branch from 1b45bab to ba78f4c Compare May 31, 2025 02:56
@henryiii henryiii mentioned this pull request May 31, 2025
@henryiii henryiii changed the title fix: no subinterp fix: allow subinterpreters to be manually disabled May 31, 2025
@henryiii henryiii merged commit 21c9dd1 into pybind:master May 31, 2025
82 checks passed
@henryiii henryiii deleted the henryiii/fix/nosubinterp branch May 31, 2025 19:44
@github-actions github-actions bot added the needs changelog Possibly needs a changelog entry label May 31, 2025
Comment on lines +261 to +265
# 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
Copy link
Contributor

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

Copy link
Collaborator

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.

Copy link
Collaborator Author

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. :)

Copy link
Collaborator Author

@henryiii henryiii Jun 1, 2025

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.

Copy link
Contributor

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

Copy link
Collaborator

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.

Copy link
Contributor

@XuehaiPan XuehaiPan Jun 2, 2025

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

Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator

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

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

Successfully merging this pull request may close these issues.

3 participants