From 65563fa5f04738058b4c8d0ff58986c5f0ba776e Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 28 Oct 2022 11:46:52 +0200 Subject: [PATCH 01/12] stash pybind11 data structures in interpreter state dictionary --- include/pybind11/detail/internals.h | 35 +++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 6fd61098c4..cdc91bfd45 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -297,6 +297,12 @@ struct type_info { # endif #endif +#if PY_VERSION_HEX < 0x03090000 +# define PYBIND11_INTERPRETER_STATE_GET _PyInterpreterState_Get +#else +# define PYBIND11_INTERPRETER_STATE_GET PyInterpreterState_Get +#endif + #define PYBIND11_INTERNALS_ID \ "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ @@ -423,7 +429,7 @@ inline void translate_local_exception(std::exception_ptr p) { /// Return a reference to the current `internals` data PYBIND11_NOINLINE internals &get_internals() { - auto **&internals_pp = get_internals_pp(); + internals **&internals_pp = get_internals_pp(); if (internals_pp && *internals_pp) { return **internals_pp; } @@ -445,11 +451,24 @@ PYBIND11_NOINLINE internals &get_internals() { #endif error_scope err_scope; - PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID); - auto builtins = handle(PyEval_GetBuiltins()); - if (builtins.contains(id) && isinstance(builtins[id])) { - internals_pp = static_cast(capsule(builtins[id])); + const char *id_cstr = PYBIND11_INTERNALS_ID; + PYBIND11_STR_TYPE id(id_cstr); + + dict state_dict + = reinterpret_borrow(PyInterpreterState_GetDict(PYBIND11_INTERPRETER_STATE_GET())); + if (!state_dict) + pybind11_fail("get_internals(): PyInterpreterState_GetDict() failed!"); + + if (state_dict.contains(id_cstr)) { + object o = state_dict[id]; + // May fail if 'capsule_obj' is not a capsule, or if it has a different + // name. We clear the error status below in that case + internals_pp = static_cast(PyCapsule_GetPointer(o.ptr(), id_cstr)); + if (!internals_pp) + PyErr_Clear(); + } + if (internals_pp) { // We loaded builtins through python's builtins, which means that our `error_already_set` // and `builtin_exception` may be different local classes than the ones set up in the // initial exception translator, below, so add another for our local exception classes. @@ -461,9 +480,7 @@ PYBIND11_NOINLINE internals &get_internals() { (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); #endif } else { - if (!internals_pp) { - internals_pp = new internals *(); - } + internals_pp = new internals *(); auto *&internals_ptr = *internals_pp; internals_ptr = new internals(); #if defined(WITH_THREAD) @@ -485,7 +502,7 @@ PYBIND11_NOINLINE internals &get_internals() { # endif internals_ptr->istate = tstate->interp; #endif - builtins[id] = capsule(internals_pp); + state_dict[id] = capsule(internals_pp, id_cstr); internals_ptr->registered_exception_translators.push_front(&translate_exception); internals_ptr->static_property_type = make_static_property_type(); internals_ptr->default_metaclass = make_default_metaclass(); From ae63391bc4a73163a8178f8849a94fc9af882d5f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Oct 2022 09:52:23 +0000 Subject: [PATCH 02/12] style: pre-commit fixes --- include/pybind11/detail/internals.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index cdc91bfd45..ed49ff15e8 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -298,9 +298,9 @@ struct type_info { #endif #if PY_VERSION_HEX < 0x03090000 -# define PYBIND11_INTERPRETER_STATE_GET _PyInterpreterState_Get +# define PYBIND11_INTERPRETER_STATE_GET _PyInterpreterState_Get #else -# define PYBIND11_INTERPRETER_STATE_GET PyInterpreterState_Get +# define PYBIND11_INTERPRETER_STATE_GET PyInterpreterState_Get #endif #define PYBIND11_INTERNALS_ID \ From 943e0700f100afa78e7d0fc723db8ceb3decddf0 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 31 Oct 2022 21:27:48 +0100 Subject: [PATCH 03/12] Make changes specific to Python 3.8+, incorporate feedback --- include/pybind11/detail/internals.h | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index ed49ff15e8..d5900bb764 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -297,12 +297,6 @@ struct type_info { # endif #endif -#if PY_VERSION_HEX < 0x03090000 -# define PYBIND11_INTERPRETER_STATE_GET _PyInterpreterState_Get -#else -# define PYBIND11_INTERPRETER_STATE_GET PyInterpreterState_Get -#endif - #define PYBIND11_INTERNALS_ID \ "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ @@ -451,21 +445,30 @@ PYBIND11_NOINLINE internals &get_internals() { #endif error_scope err_scope; - const char *id_cstr = PYBIND11_INTERNALS_ID; - PYBIND11_STR_TYPE id(id_cstr); + constexpr const char *id_cstr = PYBIND11_INTERNALS_ID; + str id(id_cstr); + + dict state_dict; +#if PY_VERSION_HEX < 0x03080000 + state_dict = reinterpret_borrow(PyEval_GetBuiltins()); +#elif PY_VERSION_HEX < 0x03090000 + state_dict = reinterpret_borrow(PyInterpreterState_GetDict(_PyInterpreterState_Get())); +#else + state_dict = reinterpret_borrow(PyInterpreterState_GetDict(PyInterpreterState_Get())); +#endif - dict state_dict - = reinterpret_borrow(PyInterpreterState_GetDict(PYBIND11_INTERPRETER_STATE_GET())); - if (!state_dict) - pybind11_fail("get_internals(): PyInterpreterState_GetDict() failed!"); + if (!state_dict) { + pybind11_fail("get_internals(): could not acquire state dictionary!"); + } if (state_dict.contains(id_cstr)) { object o = state_dict[id]; // May fail if 'capsule_obj' is not a capsule, or if it has a different // name. We clear the error status below in that case internals_pp = static_cast(PyCapsule_GetPointer(o.ptr(), id_cstr)); - if (!internals_pp) + if (!internals_pp) { PyErr_Clear(); + } } if (internals_pp) { From 7b3339cf502e09251066a79f84d8a24f073eb797 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 31 Oct 2022 22:06:56 +0100 Subject: [PATCH 04/12] fix segfault --- include/pybind11/detail/internals.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index d5900bb764..443ca19888 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -471,7 +471,7 @@ PYBIND11_NOINLINE internals &get_internals() { } } - if (internals_pp) { + if (internals_pp && *internals_pp) { // We loaded builtins through python's builtins, which means that our `error_already_set` // and `builtin_exception` may be different local classes than the ones set up in the // initial exception translator, below, so add another for our local exception classes. @@ -483,7 +483,9 @@ PYBIND11_NOINLINE internals &get_internals() { (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); #endif } else { - internals_pp = new internals *(); + if (!internals_pp) { + internals_pp = new internals *(); + } auto *&internals_ptr = *internals_pp; internals_ptr = new internals(); #if defined(WITH_THREAD) From a3d6273bb1b389c64e0599be7bbae9845c6cfd45 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 2 Nov 2022 15:28:00 +0100 Subject: [PATCH 05/12] fix C++ test --- tests/test_embed/test_interpreter.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 44dcd1fdb0..8e046859bb 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -169,8 +169,15 @@ TEST_CASE("There can be only one interpreter") { } bool has_pybind11_internals_builtin() { - auto builtins = py::handle(PyEval_GetBuiltins()); - return builtins.contains(PYBIND11_INTERNALS_ID); + py::dict state_dict; +#if PY_VERSION_HEX < 0x03080000 + state_dict = py::reinterpret_borrow(PyEval_GetBuiltins()); +#elif PY_VERSION_HEX < 0x03090000 + state_dict = py::reinterpret_borrow(PyInterpreterState_GetDict(_PyInterpreterState_Get())); +#else + state_dict = py::reinterpret_borrow(PyInterpreterState_GetDict(PyInterpreterState_Get())); +#endif + return state_dict.contains(PYBIND11_INTERNALS_ID); }; bool has_pybind11_internals_static() { From ca372f4a5f7877046e192822909e4a876b8ad670 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Nov 2022 14:29:06 +0000 Subject: [PATCH 06/12] style: pre-commit fixes --- tests/test_embed/test_interpreter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 8e046859bb..f12ba6d268 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -173,9 +173,11 @@ bool has_pybind11_internals_builtin() { #if PY_VERSION_HEX < 0x03080000 state_dict = py::reinterpret_borrow(PyEval_GetBuiltins()); #elif PY_VERSION_HEX < 0x03090000 - state_dict = py::reinterpret_borrow(PyInterpreterState_GetDict(_PyInterpreterState_Get())); + state_dict + = py::reinterpret_borrow(PyInterpreterState_GetDict(_PyInterpreterState_Get())); #else - state_dict = py::reinterpret_borrow(PyInterpreterState_GetDict(PyInterpreterState_Get())); + state_dict + = py::reinterpret_borrow(PyInterpreterState_GetDict(PyInterpreterState_Get())); #endif return state_dict.contains(PYBIND11_INTERNALS_ID); }; From 9859a9138a55464830be1f7a5ef21fac2ace3e98 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 2 Nov 2022 15:44:19 +0100 Subject: [PATCH 07/12] continue to use PyEval_GetBuiltins for PyPy --- include/pybind11/detail/internals.h | 2 +- tests/test_embed/test_interpreter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 443ca19888..0781be09d4 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -449,7 +449,7 @@ PYBIND11_NOINLINE internals &get_internals() { str id(id_cstr); dict state_dict; -#if PY_VERSION_HEX < 0x03080000 +#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) state_dict = reinterpret_borrow(PyEval_GetBuiltins()); #elif PY_VERSION_HEX < 0x03090000 state_dict = reinterpret_borrow(PyInterpreterState_GetDict(_PyInterpreterState_Get())); diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index f12ba6d268..06c92e89bd 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -170,7 +170,7 @@ TEST_CASE("There can be only one interpreter") { bool has_pybind11_internals_builtin() { py::dict state_dict; -#if PY_VERSION_HEX < 0x03080000 +#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) state_dict = py::reinterpret_borrow(PyEval_GetBuiltins()); #elif PY_VERSION_HEX < 0x03090000 state_dict From b30b2208853b23a678de8e687af7f45e4edea0ed Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 2 Nov 2022 22:36:17 +0100 Subject: [PATCH 08/12] incorporated feedback --- include/pybind11/detail/internals.h | 33 +++++++++++++++++---------- tests/test_embed/test_interpreter.cpp | 28 ++++++++--------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 0781be09d4..0f807947c6 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -421,6 +421,26 @@ inline void translate_local_exception(std::exception_ptr p) { } #endif +inline object get_internals_state_dict() { + object state_dict; +#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) + state_dict = reinterpret_borrow(PyEval_GetBuiltins()); +#else +# if PY_VERSION_HEX < 0x03090000 + PyInterpreterState *istate = _PyInterpreterState_Get(); +#else + PyInterpreterState *istate = PyInterpreterState_Get(); +#endif + if (istate) + state_dict = reinterpret_borrow(PyInterpreterState_GetDict(istate)); +#endif + if (!state_dict) { + raise_from(PyExc_SystemError, "get_internals(): could not acquire state dictionary!"); + } + + return state_dict; +} + /// Return a reference to the current `internals` data PYBIND11_NOINLINE internals &get_internals() { internals **&internals_pp = get_internals_pp(); @@ -448,18 +468,7 @@ PYBIND11_NOINLINE internals &get_internals() { constexpr const char *id_cstr = PYBIND11_INTERNALS_ID; str id(id_cstr); - dict state_dict; -#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) - state_dict = reinterpret_borrow(PyEval_GetBuiltins()); -#elif PY_VERSION_HEX < 0x03090000 - state_dict = reinterpret_borrow(PyInterpreterState_GetDict(_PyInterpreterState_Get())); -#else - state_dict = reinterpret_borrow(PyInterpreterState_GetDict(PyInterpreterState_Get())); -#endif - - if (!state_dict) { - pybind11_fail("get_internals(): could not acquire state dictionary!"); - } + dict state_dict = get_internals_state_dict(); if (state_dict.contains(id_cstr)) { object o = state_dict[id]; diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 06c92e89bd..02bb300d63 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -168,18 +168,8 @@ TEST_CASE("There can be only one interpreter") { py::initialize_interpreter(); } -bool has_pybind11_internals_builtin() { - py::dict state_dict; -#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) - state_dict = py::reinterpret_borrow(PyEval_GetBuiltins()); -#elif PY_VERSION_HEX < 0x03090000 - state_dict - = py::reinterpret_borrow(PyInterpreterState_GetDict(_PyInterpreterState_Get())); -#else - state_dict - = py::reinterpret_borrow(PyInterpreterState_GetDict(PyInterpreterState_Get())); -#endif - return state_dict.contains(PYBIND11_INTERNALS_ID); +bool has_pybind11_internals_state_dict() { + return py::detail::get_internals_state_dict().contains(PYBIND11_INTERNALS_ID); }; bool has_pybind11_internals_static() { @@ -190,7 +180,7 @@ bool has_pybind11_internals_static() { TEST_CASE("Restart the interpreter") { // Verify pre-restart state. REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast() == 3); - REQUIRE(has_pybind11_internals_builtin()); + REQUIRE(has_pybind11_internals_state_dict()); REQUIRE(has_pybind11_internals_static()); REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast() == 123); @@ -207,10 +197,10 @@ TEST_CASE("Restart the interpreter") { REQUIRE(Py_IsInitialized() == 1); // Internals are deleted after a restart. - REQUIRE_FALSE(has_pybind11_internals_builtin()); + REQUIRE_FALSE(has_pybind11_internals_state_dict()); REQUIRE_FALSE(has_pybind11_internals_static()); pybind11::detail::get_internals(); - REQUIRE(has_pybind11_internals_builtin()); + REQUIRE(has_pybind11_internals_state_dict()); REQUIRE(has_pybind11_internals_static()); REQUIRE(reinterpret_cast(*py::detail::get_internals_pp()) == py::module_::import("external_module").attr("internals_at")().cast()); @@ -225,13 +215,13 @@ TEST_CASE("Restart the interpreter") { py::detail::get_internals(); *static_cast(ran) = true; }); - REQUIRE_FALSE(has_pybind11_internals_builtin()); + REQUIRE_FALSE(has_pybind11_internals_state_dict()); REQUIRE_FALSE(has_pybind11_internals_static()); REQUIRE_FALSE(ran); py::finalize_interpreter(); REQUIRE(ran); py::initialize_interpreter(); - REQUIRE_FALSE(has_pybind11_internals_builtin()); + REQUIRE_FALSE(has_pybind11_internals_state_dict()); REQUIRE_FALSE(has_pybind11_internals_static()); // C++ modules can be reloaded. @@ -253,7 +243,7 @@ TEST_CASE("Subinterpreter") { REQUIRE(m.attr("add")(1, 2).cast() == 3); } - REQUIRE(has_pybind11_internals_builtin()); + REQUIRE(has_pybind11_internals_state_dict()); REQUIRE(has_pybind11_internals_static()); /// Create and switch to a subinterpreter. @@ -263,7 +253,7 @@ TEST_CASE("Subinterpreter") { // Subinterpreters get their own copy of builtins. detail::get_internals() still // works by returning from the static variable, i.e. all interpreters share a single // global pybind11::internals; - REQUIRE_FALSE(has_pybind11_internals_builtin()); + REQUIRE_FALSE(has_pybind11_internals_state_dict()); REQUIRE(has_pybind11_internals_static()); // Modules tags should be gone. From 2b1de848a4151ac506829a33368780c4d566750d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Nov 2022 21:36:59 +0000 Subject: [PATCH 09/12] style: pre-commit fixes --- include/pybind11/detail/internals.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 0f807947c6..3696e1d5df 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -426,11 +426,11 @@ inline object get_internals_state_dict() { #if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) state_dict = reinterpret_borrow(PyEval_GetBuiltins()); #else -# if PY_VERSION_HEX < 0x03090000 +# if PY_VERSION_HEX < 0x03090000 PyInterpreterState *istate = _PyInterpreterState_Get(); -#else +# else PyInterpreterState *istate = PyInterpreterState_Get(); -#endif +# endif if (istate) state_dict = reinterpret_borrow(PyInterpreterState_GetDict(istate)); #endif From 640afae4163c13990e700bedf8abb3446f978e8d Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 2 Nov 2022 23:17:46 +0100 Subject: [PATCH 10/12] clang-tidy fix --- include/pybind11/detail/internals.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 3696e1d5df..2085fd348e 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -431,8 +431,9 @@ inline object get_internals_state_dict() { # else PyInterpreterState *istate = PyInterpreterState_Get(); # endif - if (istate) + if (istate) { state_dict = reinterpret_borrow(PyInterpreterState_GetDict(istate)); + } #endif if (!state_dict) { raise_from(PyExc_SystemError, "get_internals(): could not acquire state dictionary!"); From 9dd3aeebb567d5c55a7b8047112f7b4a16150a30 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 3 Nov 2022 05:34:50 -0700 Subject: [PATCH 11/12] Enable merging this change without an `internals` version bump or any other disruption. --- include/pybind11/detail/internals.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 2085fd348e..b0c6a22f63 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -423,7 +423,8 @@ inline void translate_local_exception(std::exception_ptr p) { inline object get_internals_state_dict() { object state_dict; -#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) +#if (PYBIND11_INTERNALS_VERSION <= 4 && PY_VERSION_HEX < 0x030C0000) \ + || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) state_dict = reinterpret_borrow(PyEval_GetBuiltins()); #else # if PY_VERSION_HEX < 0x03090000 From b3cd439c59a168f1feca69794f52ffadd4446e6b Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 3 Nov 2022 05:49:01 -0700 Subject: [PATCH 12/12] Repurpose upstream.yml for Python 3.12 testing. --- .github/workflows/upstream.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/upstream.yml b/.github/workflows/upstream.yml index 366284acf4..c3f5b74c49 100644 --- a/.github/workflows/upstream.yml +++ b/.github/workflows/upstream.yml @@ -1,4 +1,3 @@ - name: Upstream on: @@ -7,24 +6,25 @@ on: concurrency: group: upstream-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: false env: PIP_ONLY_BINARY: numpy + PYTEST_TIMEOUT: 300 jobs: standard: - name: "🐍 3.11 latest internals • ubuntu-latest • x64" + name: "🐍 3.12 latest internals • ubuntu-latest • x64" runs-on: ubuntu-latest if: "contains(github.event.pull_request.labels.*.name, 'python dev')" steps: - uses: actions/checkout@v3 - - name: Setup Python 3.11 + - name: Setup Python 3.12 uses: actions/setup-python@v4 with: - python-version: "3.11-dev" + python-version: "3.12-dev" - name: Setup Boost (Linux) if: runner.os == 'Linux'