From c77140e16bb12f6188f973d9357f1d7f06163da5 Mon Sep 17 00:00:00 2001 From: "Novikov, Arman" Date: Sun, 13 Nov 2022 10:30:36 +0100 Subject: [PATCH 01/19] scoped_interpreter overloaded ctor: PyConfig param --- include/pybind11/embed.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 5b77594b35..14e6a96413 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -264,6 +264,25 @@ class scoped_interpreter { initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path); } +#if PY_VERSION_HEX >= 0x030B0000 + explicit scoped_interpreter(const PyConfig& config, + bool add_program_dir_to_path = true) { + if (Py_IsInitialized() != 0) { + pybind11_fail("The interpreter is already running"); + } + PyStatus status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg : "Failed to init CPython"); + } + if (add_program_dir_to_path) { + PyRun_SimpleString("import sys, os.path; " + "sys.path.insert(0, " + "os.path.abspath(os.path.dirname(sys.argv[0])) " + "if sys.argv and os.path.exists(sys.argv[0]) else '')"); + } + } +#endif + scoped_interpreter(const scoped_interpreter &) = delete; scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } scoped_interpreter &operator=(const scoped_interpreter &) = delete; From 8fd485209dbcdecaf24db4349a251ee34d5b7baf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 13 Nov 2022 10:37:39 +0000 Subject: [PATCH 02/19] style: pre-commit fixes --- include/pybind11/embed.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 14e6a96413..1a69d49460 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -265,14 +265,14 @@ class scoped_interpreter { } #if PY_VERSION_HEX >= 0x030B0000 - explicit scoped_interpreter(const PyConfig& config, - bool add_program_dir_to_path = true) { + explicit scoped_interpreter(const PyConfig &config, bool add_program_dir_to_path = true) { if (Py_IsInitialized() != 0) { pybind11_fail("The interpreter is already running"); } PyStatus status = Py_InitializeFromConfig(&config); if (PyStatus_Exception(status)) { - throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg : "Failed to init CPython"); + throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg + : "Failed to init CPython"); } if (add_program_dir_to_path) { PyRun_SimpleString("import sys, os.path; " From 28cc5661090c7c119a0a39417b006d166740500b Mon Sep 17 00:00:00 2001 From: armnov Date: Tue, 15 Nov 2022 23:34:59 +0300 Subject: [PATCH 03/19] refact: some logics extracted into funcs (precheck_interpreter, _initialize_interpreter); config_guard --- include/pybind11/embed.h | 71 +++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 1a69d49460..d9cbf48fcf 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -88,6 +88,42 @@ inline wchar_t *widen_chars(const char *safe_arg) { PYBIND11_NAMESPACE_END(detail) +inline void precheck_interpreter() { + if (Py_IsInitialized() != 0) { + pybind11_fail("The interpreter is already running"); + } +} + +#if PY_VERSION_HEX >= 0x030B0000 +class config_guard +{ +public: + config_guard(PyConfig& config): m_config{config}{} + ~config_guard(){PyConfig_Clear(&m_config);} +private: + PyConfig& m_config; +}; + +/*! + * \warning should not be called by user + * \todo put it into the scoped_interpreter's private section? + */ +inline void _initialize_interpreter(const PyConfig &config, bool add_program_dir_to_path) { + PyStatus status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg + : "Failed to init CPython"); + } + if (add_program_dir_to_path) { + PyRun_SimpleString("import sys, os.path; " + "sys.path.insert(0, " + "os.path.abspath(os.path.dirname(sys.argv[0])) " + "if sys.argv and os.path.exists(sys.argv[0]) else '')"); + } +} + +#endif + /** \rst Initialize the Python interpreter. No other pybind11 or CPython API functions can be called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The @@ -111,9 +147,7 @@ inline void initialize_interpreter(bool init_signal_handlers = true, int argc = 0, const char *const *argv = nullptr, bool add_program_dir_to_path = true) { - if (Py_IsInitialized() != 0) { - pybind11_fail("The interpreter is already running"); - } + precheck_interpreter(); #if PY_VERSION_HEX < 0x030B0000 @@ -152,6 +186,7 @@ inline void initialize_interpreter(bool init_signal_handlers = true, PySys_SetArgvEx(argc, pysys_argv, static_cast(add_program_dir_to_path)); #else PyConfig config; + config_guard{config}; PyConfig_InitIsolatedConfig(&config); config.isolated = 0; config.use_environment = 1; @@ -161,22 +196,10 @@ inline void initialize_interpreter(bool init_signal_handlers = true, if (PyStatus_Exception(status)) { // A failure here indicates a character-encoding failure or the python // interpreter out of memory. Give up. - PyConfig_Clear(&config); throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg : "Failed to prepare CPython"); } - status = Py_InitializeFromConfig(&config); - PyConfig_Clear(&config); - if (PyStatus_Exception(status)) { - throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg - : "Failed to init CPython"); - } - if (add_program_dir_to_path) { - PyRun_SimpleString("import sys, os.path; " - "sys.path.insert(0, " - "os.path.abspath(os.path.dirname(sys.argv[0])) " - "if sys.argv and os.path.exists(sys.argv[0]) else '')"); - } + _initialize_interpreter(config, add_program_dir_to_path); #endif } @@ -266,20 +289,8 @@ class scoped_interpreter { #if PY_VERSION_HEX >= 0x030B0000 explicit scoped_interpreter(const PyConfig &config, bool add_program_dir_to_path = true) { - if (Py_IsInitialized() != 0) { - pybind11_fail("The interpreter is already running"); - } - PyStatus status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg - : "Failed to init CPython"); - } - if (add_program_dir_to_path) { - PyRun_SimpleString("import sys, os.path; " - "sys.path.insert(0, " - "os.path.abspath(os.path.dirname(sys.argv[0])) " - "if sys.argv and os.path.exists(sys.argv[0]) else '')"); - } + precheck_interpreter(); + _initialize_interpreter(config, add_program_dir_to_path); } #endif From 8770abe739842b3aec9b630c0235032ccf48d60d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 20:36:37 +0000 Subject: [PATCH 04/19] style: pre-commit fixes --- include/pybind11/embed.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index d9cbf48fcf..8e5d7fd963 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -95,13 +95,13 @@ inline void precheck_interpreter() { } #if PY_VERSION_HEX >= 0x030B0000 -class config_guard -{ +class config_guard { public: - config_guard(PyConfig& config): m_config{config}{} - ~config_guard(){PyConfig_Clear(&m_config);} + config_guard(PyConfig &config) : m_config{config} {} + ~config_guard() { PyConfig_Clear(&m_config); } + private: - PyConfig& m_config; + PyConfig &m_config; }; /*! From 58073f1ab45a8f856088b012a72d9e2b04023fd8 Mon Sep 17 00:00:00 2001 From: armnov Date: Wed, 16 Nov 2022 23:07:07 +0300 Subject: [PATCH 05/19] refact: scoped_config, some funcs hidden in detail ns --- include/pybind11/embed.h | 61 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 8e5d7fd963..5b3998e0e9 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -55,6 +55,7 @@ PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \ & variable) // NOLINT(bugprone-macro-parentheses) +#define PYCONFIG_SUPPORT_PY_VERSION (0x030B0000) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) @@ -86,29 +87,14 @@ inline wchar_t *widen_chars(const char *safe_arg) { return widened_arg; } -PYBIND11_NAMESPACE_END(detail) - inline void precheck_interpreter() { if (Py_IsInitialized() != 0) { pybind11_fail("The interpreter is already running"); } } -#if PY_VERSION_HEX >= 0x030B0000 -class config_guard { -public: - config_guard(PyConfig &config) : m_config{config} {} - ~config_guard() { PyConfig_Clear(&m_config); } - -private: - PyConfig &m_config; -}; - -/*! - * \warning should not be called by user - * \todo put it into the scoped_interpreter's private section? - */ -inline void _initialize_interpreter(const PyConfig &config, bool add_program_dir_to_path) { +#if PY_VERSION_HEX >= PYCONFIG_SUPPORT_PY_VERSION +inline void initialize_interpreter(const PyConfig &config, bool add_program_dir_to_path) { PyStatus status = Py_InitializeFromConfig(&config); if (PyStatus_Exception(status)) { throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg @@ -121,7 +107,20 @@ inline void _initialize_interpreter(const PyConfig &config, bool add_program_dir "if sys.argv and os.path.exists(sys.argv[0]) else '')"); } } +#endif +PYBIND11_NAMESPACE_END(detail) + +#if PY_VERSION_HEX >= PYCONFIG_SUPPORT_PY_VERSION +struct scoped_config { + scoped_config() = default; + ~scoped_config() { PyConfig_Clear(&config); }; + + scoped_config(const scoped_config &) = delete; + scoped_config &operator=(const scoped_config &) = delete; + + PyConfig config; +}; #endif /** \rst @@ -147,9 +146,9 @@ inline void initialize_interpreter(bool init_signal_handlers = true, int argc = 0, const char *const *argv = nullptr, bool add_program_dir_to_path = true) { - precheck_interpreter(); + detail::precheck_interpreter(); -#if PY_VERSION_HEX < 0x030B0000 +#if PY_VERSION_HEX < PYCONFIG_SUPPORT_PY_VERSION Py_InitializeEx(init_signal_handlers ? 1 : 0); # if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000 @@ -185,21 +184,21 @@ inline void initialize_interpreter(bool init_signal_handlers = true, PySys_SetArgvEx(argc, pysys_argv, static_cast(add_program_dir_to_path)); #else - PyConfig config; - config_guard{config}; - PyConfig_InitIsolatedConfig(&config); - config.isolated = 0; - config.use_environment = 1; - config.install_signal_handlers = init_signal_handlers ? 1 : 0; - - PyStatus status = PyConfig_SetBytesArgv(&config, argc, const_cast(argv)); + scoped_config scoped_cfg; + PyConfig_InitIsolatedConfig(&scoped_cfg.config); + scoped_cfg.config.isolated = 0; + scoped_cfg.config.use_environment = 1; + scoped_cfg.config.install_signal_handlers = init_signal_handlers ? 1 : 0; + + PyStatus status + = PyConfig_SetBytesArgv(&scoped_cfg.config, argc, const_cast(argv)); if (PyStatus_Exception(status)) { // A failure here indicates a character-encoding failure or the python // interpreter out of memory. Give up. throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg : "Failed to prepare CPython"); } - _initialize_interpreter(config, add_program_dir_to_path); + detail::initialize_interpreter(scoped_cfg.config, add_program_dir_to_path); #endif } @@ -287,10 +286,10 @@ class scoped_interpreter { initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path); } -#if PY_VERSION_HEX >= 0x030B0000 +#if PY_VERSION_HEX >= PYCONFIG_SUPPORT_PY_VERSION explicit scoped_interpreter(const PyConfig &config, bool add_program_dir_to_path = true) { - precheck_interpreter(); - _initialize_interpreter(config, add_program_dir_to_path); + detail::precheck_interpreter(); + detail::initialize_interpreter(config, add_program_dir_to_path); } #endif From fc8ba374d466a7282196dad0bfc8cdab2b5735ec Mon Sep 17 00:00:00 2001 From: armnov Date: Thu, 17 Nov 2022 01:32:01 +0300 Subject: [PATCH 06/19] refact: macro PYBIND11_PYCONFIG_SUPPORT_PY_VERSION + undef --- include/pybind11/embed.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 5b3998e0e9..7a8f970575 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -55,7 +55,7 @@ PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \ & variable) // NOLINT(bugprone-macro-parentheses) -#define PYCONFIG_SUPPORT_PY_VERSION (0x030B0000) +#define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION (0x030B0000) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) @@ -93,7 +93,7 @@ inline void precheck_interpreter() { } } -#if PY_VERSION_HEX >= PYCONFIG_SUPPORT_PY_VERSION +#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION inline void initialize_interpreter(const PyConfig &config, bool add_program_dir_to_path) { PyStatus status = Py_InitializeFromConfig(&config); if (PyStatus_Exception(status)) { @@ -111,7 +111,7 @@ inline void initialize_interpreter(const PyConfig &config, bool add_program_dir_ PYBIND11_NAMESPACE_END(detail) -#if PY_VERSION_HEX >= PYCONFIG_SUPPORT_PY_VERSION +#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION struct scoped_config { scoped_config() = default; ~scoped_config() { PyConfig_Clear(&config); }; @@ -148,7 +148,7 @@ inline void initialize_interpreter(bool init_signal_handlers = true, bool add_program_dir_to_path = true) { detail::precheck_interpreter(); -#if PY_VERSION_HEX < PYCONFIG_SUPPORT_PY_VERSION +#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION Py_InitializeEx(init_signal_handlers ? 1 : 0); # if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000 @@ -286,7 +286,7 @@ class scoped_interpreter { initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path); } -#if PY_VERSION_HEX >= PYCONFIG_SUPPORT_PY_VERSION +#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION explicit scoped_interpreter(const PyConfig &config, bool add_program_dir_to_path = true) { detail::precheck_interpreter(); detail::initialize_interpreter(config, add_program_dir_to_path); @@ -308,4 +308,6 @@ class scoped_interpreter { bool is_valid = true; }; +#undef PYBIND11_PYCONFIG_SUPPORT_PY_VERSION + PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) From 80e64048052fcc50c8aaa31a3b04ba75b33f6796 Mon Sep 17 00:00:00 2001 From: armnov Date: Fri, 18 Nov 2022 02:53:51 +0300 Subject: [PATCH 07/19] feat: PYBIND11_PYCONFIG_SUPPORT_PY_VERSION set to 3.8 --- include/pybind11/embed.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 7a8f970575..7075e45f73 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -55,7 +55,7 @@ PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \ & variable) // NOLINT(bugprone-macro-parentheses) -#define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION (0x030B0000) +#define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION (0x03080000) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) From 5eb081a7937455f44559b3f7ee906367d9957349 Mon Sep 17 00:00:00 2001 From: armnov Date: Fri, 18 Nov 2022 02:56:03 +0300 Subject: [PATCH 08/19] tests: Custom PyConfig --- tests/test_embed/test_interpreter.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 44dcd1fdb0..3bafde3af3 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -168,6 +168,18 @@ TEST_CASE("There can be only one interpreter") { py::initialize_interpreter(); } +TEST_CASE("Custom PyConfig") { + py::finalize_interpreter(); + pybind11::scoped_config conf{}; + PyConfig_InitPythonConfig(&conf.config); + REQUIRE_NOTHROW(py::scoped_interpreter{conf.config}); + { + py::scoped_interpreter p{conf.config}; + REQUIRE(py::module_::import("widget_module").attr("add")(1, 41).cast() == 42); + } + py::initialize_interpreter(); +} + bool has_pybind11_internals_builtin() { auto builtins = py::handle(PyEval_GetBuiltins()); return builtins.contains(PYBIND11_INTERNALS_ID); From 5ab1132a3f0405347e8be9584eea82b1ce16343b Mon Sep 17 00:00:00 2001 From: armnov Date: Sat, 19 Nov 2022 01:23:55 +0300 Subject: [PATCH 09/19] ci: python 3.6 -> 3.8 --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 360760ac8d..391cf1071c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,7 +9,7 @@ platform: - x86 environment: matrix: - - PYTHON: 36 + - PYTHON: 38 CONFIG: Debug install: - ps: | From 4444ea16caca260368efb73c715b74196c0200e4 Mon Sep 17 00:00:00 2001 From: armnov Date: Tue, 22 Nov 2022 03:00:30 +0300 Subject: [PATCH 10/19] ci: reverted py 38 back to 36; refact: initialize_interpreter overloads --- .appveyor.yml | 2 +- include/pybind11/embed.h | 133 +++++++++++++------------- tests/test_embed/test_interpreter.cpp | 10 +- 3 files changed, 73 insertions(+), 72 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 391cf1071c..360760ac8d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,7 +9,7 @@ platform: - x86 environment: matrix: - - PYTHON: 38 + - PYTHON: 36 CONFIG: Debug install: - ps: | diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 7075e45f73..7af0890911 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -55,7 +55,7 @@ PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \ & variable) // NOLINT(bugprone-macro-parentheses) -#define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION (0x03080000) +#define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX (0x03080000) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) @@ -93,10 +93,23 @@ inline void precheck_interpreter() { } } -#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION -inline void initialize_interpreter(const PyConfig &config, bool add_program_dir_to_path) { - PyStatus status = Py_InitializeFromConfig(&config); +#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX +inline void initialize_interpreter(PyConfig *config, + int argc = 0, + const char *const *argv = nullptr, + bool add_program_dir_to_path = true) { + PyStatus status + = PyConfig_SetBytesArgv(config, argc, const_cast(argv)); + if (PyStatus_Exception(status)) { + // A failure here indicates a character-encoding failure or the python + // interpreter out of memory. Give up. + PyConfig_Clear(config); + throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg + : "Failed to prepare CPython"); + } + status = Py_InitializeFromConfig(config); if (PyStatus_Exception(status)) { + PyConfig_Clear(config); throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg : "Failed to init CPython"); } @@ -106,50 +119,13 @@ inline void initialize_interpreter(const PyConfig &config, bool add_program_dir_ "os.path.abspath(os.path.dirname(sys.argv[0])) " "if sys.argv and os.path.exists(sys.argv[0]) else '')"); } + PyConfig_Clear(config); } -#endif - -PYBIND11_NAMESPACE_END(detail) - -#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION -struct scoped_config { - scoped_config() = default; - ~scoped_config() { PyConfig_Clear(&config); }; - - scoped_config(const scoped_config &) = delete; - scoped_config &operator=(const scoped_config &) = delete; - - PyConfig config; -}; -#endif - -/** \rst - Initialize the Python interpreter. No other pybind11 or CPython API functions can be - called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The - optional `init_signal_handlers` parameter can be used to skip the registration of - signal handlers (see the `Python documentation`_ for details). Calling this function - again after the interpreter has already been initialized is a fatal error. - - If initializing the Python interpreter fails, then the program is terminated. (This - is controlled by the CPython runtime and is an exception to pybind11's normal behavior - of throwing exceptions on errors.) - - The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are - used to populate ``sys.argv`` and ``sys.path``. - See the |PySys_SetArgvEx documentation|_ for details. - - .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx - .. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation - .. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx - \endrst */ -inline void initialize_interpreter(bool init_signal_handlers = true, - int argc = 0, - const char *const *argv = nullptr, - bool add_program_dir_to_path = true) { - detail::precheck_interpreter(); - -#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION - +#else +inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers, + int argc, + const char *const *argv, + bool add_program_dir_to_path) { Py_InitializeEx(init_signal_handlers ? 1 : 0); # if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000 PyEval_InitThreads(); @@ -183,22 +159,44 @@ inline void initialize_interpreter(bool init_signal_handlers = true, auto *pysys_argv = widened_argv.get(); PySys_SetArgvEx(argc, pysys_argv, static_cast(add_program_dir_to_path)); -#else - scoped_config scoped_cfg; - PyConfig_InitIsolatedConfig(&scoped_cfg.config); - scoped_cfg.config.isolated = 0; - scoped_cfg.config.use_environment = 1; - scoped_cfg.config.install_signal_handlers = init_signal_handlers ? 1 : 0; +} +#endif - PyStatus status - = PyConfig_SetBytesArgv(&scoped_cfg.config, argc, const_cast(argv)); - if (PyStatus_Exception(status)) { - // A failure here indicates a character-encoding failure or the python - // interpreter out of memory. Give up. - throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg - : "Failed to prepare CPython"); - } - detail::initialize_interpreter(scoped_cfg.config, add_program_dir_to_path); +PYBIND11_NAMESPACE_END(detail) + +/** \rst + Initialize the Python interpreter. No other pybind11 or CPython API functions can be + called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The + optional `init_signal_handlers` parameter can be used to skip the registration of + signal handlers (see the `Python documentation`_ for details). Calling this function + again after the interpreter has already been initialized is a fatal error. + + If initializing the Python interpreter fails, then the program is terminated. (This + is controlled by the CPython runtime and is an exception to pybind11's normal behavior + of throwing exceptions on errors.) + + The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are + used to populate ``sys.argv`` and ``sys.path``. + See the |PySys_SetArgvEx documentation|_ for details. + + .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx + .. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation + .. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx + \endrst */ +inline void initialize_interpreter(bool init_signal_handlers = true, + int argc = 0, + const char *const *argv = nullptr, + bool add_program_dir_to_path = true) { + detail::precheck_interpreter(); +#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX + detail::initialize_interpreter_pre_pyconfig(init_signal_handlers, argc, argv, add_program_dir_to_path); +#else + PyConfig config; + PyConfig_InitIsolatedConfig(&config); + config.isolated = 0; + config.use_environment = 1; + config.install_signal_handlers = init_signal_handlers ? 1 : 0; + detail::initialize_interpreter(&config, argc, argv, add_program_dir_to_path); #endif } @@ -286,10 +284,13 @@ class scoped_interpreter { initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path); } -#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION - explicit scoped_interpreter(const PyConfig &config, bool add_program_dir_to_path = true) { +#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX + explicit scoped_interpreter(PyConfig *config, + int argc = 0, + const char *const *argv = nullptr, + bool add_program_dir_to_path = true) { detail::precheck_interpreter(); - detail::initialize_interpreter(config, add_program_dir_to_path); + detail::initialize_interpreter(config, argc, argv, add_program_dir_to_path); } #endif @@ -308,6 +309,4 @@ class scoped_interpreter { bool is_valid = true; }; -#undef PYBIND11_PYCONFIG_SUPPORT_PY_VERSION - PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 3bafde3af3..659acceaa1 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -168,17 +168,19 @@ TEST_CASE("There can be only one interpreter") { py::initialize_interpreter(); } +#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX TEST_CASE("Custom PyConfig") { py::finalize_interpreter(); - pybind11::scoped_config conf{}; - PyConfig_InitPythonConfig(&conf.config); - REQUIRE_NOTHROW(py::scoped_interpreter{conf.config}); + PyConfig config; + PyConfig_InitPythonConfig(&config); + REQUIRE_NOTHROW(py::scoped_interpreter{&config}); { - py::scoped_interpreter p{conf.config}; + py::scoped_interpreter p{&config}; REQUIRE(py::module_::import("widget_module").attr("add")(1, 41).cast() == 42); } py::initialize_interpreter(); } +#endif bool has_pybind11_internals_builtin() { auto builtins = py::handle(PyEval_GetBuiltins()); From f1f1ecd3d9343a81bd2d4985b085d90d0bb0405d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 00:02:45 +0000 Subject: [PATCH 11/19] style: pre-commit fixes --- include/pybind11/embed.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 7af0890911..bc3945979d 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -98,8 +98,7 @@ inline void initialize_interpreter(PyConfig *config, int argc = 0, const char *const *argv = nullptr, bool add_program_dir_to_path = true) { - PyStatus status - = PyConfig_SetBytesArgv(config, argc, const_cast(argv)); + PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast(argv)); if (PyStatus_Exception(status)) { // A failure here indicates a character-encoding failure or the python // interpreter out of memory. Give up. @@ -123,9 +122,9 @@ inline void initialize_interpreter(PyConfig *config, } #else inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers, - int argc, - const char *const *argv, - bool add_program_dir_to_path) { + int argc, + const char *const *argv, + bool add_program_dir_to_path) { Py_InitializeEx(init_signal_handlers ? 1 : 0); # if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000 PyEval_InitThreads(); @@ -189,7 +188,8 @@ inline void initialize_interpreter(bool init_signal_handlers = true, bool add_program_dir_to_path = true) { detail::precheck_interpreter(); #if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX - detail::initialize_interpreter_pre_pyconfig(init_signal_handlers, argc, argv, add_program_dir_to_path); + detail::initialize_interpreter_pre_pyconfig( + init_signal_handlers, argc, argv, add_program_dir_to_path); #else PyConfig config; PyConfig_InitIsolatedConfig(&config); From e1a486eab3122f571b1eb3890e9049c1442f63ef Mon Sep 17 00:00:00 2001 From: armnov Date: Tue, 22 Nov 2022 03:44:25 +0300 Subject: [PATCH 12/19] fix: readability-implicit-bool-conversion --- include/pybind11/embed.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index bc3945979d..e561c821a1 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -99,18 +99,18 @@ inline void initialize_interpreter(PyConfig *config, const char *const *argv = nullptr, bool add_program_dir_to_path = true) { PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast(argv)); - if (PyStatus_Exception(status)) { + if (PyStatus_Exception(status) != 0) { // A failure here indicates a character-encoding failure or the python // interpreter out of memory. Give up. PyConfig_Clear(config); - throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg - : "Failed to prepare CPython"); + throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg + : "Failed to prepare CPython"); } status = Py_InitializeFromConfig(config); - if (PyStatus_Exception(status)) { + if (PyStatus_Exception(status) != 0) { PyConfig_Clear(config); - throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg - : "Failed to init CPython"); + throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg + : "Failed to init CPython"); } if (add_program_dir_to_path) { PyRun_SimpleString("import sys, os.path; " From e25e97b7d5fa1165d97d93909466d593aaaffd86 Mon Sep 17 00:00:00 2001 From: armnov Date: Tue, 22 Nov 2022 22:11:46 +0300 Subject: [PATCH 13/19] refact: each initialize_interpreter overloads in pybind11 ns --- include/pybind11/embed.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index e561c821a1..10d1116f4e 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -93,11 +93,14 @@ inline void precheck_interpreter() { } } +PYBIND11_NAMESPACE_END(detail) + #if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX inline void initialize_interpreter(PyConfig *config, int argc = 0, const char *const *argv = nullptr, bool add_program_dir_to_path = true) { + detail::precheck_interpreter(); PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast(argv)); if (PyStatus_Exception(status) != 0) { // A failure here indicates a character-encoding failure or the python @@ -125,6 +128,7 @@ inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers, int argc, const char *const *argv, bool add_program_dir_to_path) { + detail::precheck_interpreter(); Py_InitializeEx(init_signal_handlers ? 1 : 0); # if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000 PyEval_InitThreads(); @@ -161,8 +165,6 @@ inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers, } #endif -PYBIND11_NAMESPACE_END(detail) - /** \rst Initialize the Python interpreter. No other pybind11 or CPython API functions can be called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The @@ -186,17 +188,15 @@ inline void initialize_interpreter(bool init_signal_handlers = true, int argc = 0, const char *const *argv = nullptr, bool add_program_dir_to_path = true) { - detail::precheck_interpreter(); #if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX - detail::initialize_interpreter_pre_pyconfig( - init_signal_handlers, argc, argv, add_program_dir_to_path); + initialize_interpreter_pre_pyconfig(init_signal_handlers, argc, argv, add_program_dir_to_path); #else PyConfig config; PyConfig_InitIsolatedConfig(&config); config.isolated = 0; config.use_environment = 1; config.install_signal_handlers = init_signal_handlers ? 1 : 0; - detail::initialize_interpreter(&config, argc, argv, add_program_dir_to_path); + initialize_interpreter(&config, argc, argv, add_program_dir_to_path); #endif } @@ -289,8 +289,7 @@ class scoped_interpreter { int argc = 0, const char *const *argv = nullptr, bool add_program_dir_to_path = true) { - detail::precheck_interpreter(); - detail::initialize_interpreter(config, argc, argv, add_program_dir_to_path); + initialize_interpreter(config, argc, argv, add_program_dir_to_path); } #endif From e2e0246443397fdb9300ddf7d622109e2ca9205f Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 23 Nov 2022 08:25:03 -0800 Subject: [PATCH 14/19] Move `initialize_interpreter_pre_pyconfig()` into the `detail` namespace. Move the `PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX` define down to where it is used for the first time, and check if it is defined already, so that it is possible to customize from the compilation command line, just in case there is some unforeseen issue for Python 3.8, 3.9, 3.10. --- include/pybind11/embed.h | 70 ++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 10d1116f4e..749c75beb6 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -55,7 +55,6 @@ PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \ & variable) // NOLINT(bugprone-macro-parentheses) -#define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX (0x03080000) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) @@ -93,37 +92,11 @@ inline void precheck_interpreter() { } } -PYBIND11_NAMESPACE_END(detail) +#if !defined(PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX) +# define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX (0x03080000) +#endif -#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX -inline void initialize_interpreter(PyConfig *config, - int argc = 0, - const char *const *argv = nullptr, - bool add_program_dir_to_path = true) { - detail::precheck_interpreter(); - PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast(argv)); - if (PyStatus_Exception(status) != 0) { - // A failure here indicates a character-encoding failure or the python - // interpreter out of memory. Give up. - PyConfig_Clear(config); - throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg - : "Failed to prepare CPython"); - } - status = Py_InitializeFromConfig(config); - if (PyStatus_Exception(status) != 0) { - PyConfig_Clear(config); - throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg - : "Failed to init CPython"); - } - if (add_program_dir_to_path) { - PyRun_SimpleString("import sys, os.path; " - "sys.path.insert(0, " - "os.path.abspath(os.path.dirname(sys.argv[0])) " - "if sys.argv and os.path.exists(sys.argv[0]) else '')"); - } - PyConfig_Clear(config); -} -#else +#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers, int argc, const char *const *argv, @@ -165,6 +138,38 @@ inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers, } #endif +PYBIND11_NAMESPACE_END(detail) + +#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX +inline void initialize_interpreter(PyConfig *config, + int argc = 0, + const char *const *argv = nullptr, + bool add_program_dir_to_path = true) { + detail::precheck_interpreter(); + PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast(argv)); + if (PyStatus_Exception(status) != 0) { + // A failure here indicates a character-encoding failure or the python + // interpreter out of memory. Give up. + PyConfig_Clear(config); + throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg + : "Failed to prepare CPython"); + } + status = Py_InitializeFromConfig(config); + if (PyStatus_Exception(status) != 0) { + PyConfig_Clear(config); + throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg + : "Failed to init CPython"); + } + if (add_program_dir_to_path) { + PyRun_SimpleString("import sys, os.path; " + "sys.path.insert(0, " + "os.path.abspath(os.path.dirname(sys.argv[0])) " + "if sys.argv and os.path.exists(sys.argv[0]) else '')"); + } + PyConfig_Clear(config); +} +#endif + /** \rst Initialize the Python interpreter. No other pybind11 or CPython API functions can be called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The @@ -189,7 +194,8 @@ inline void initialize_interpreter(bool init_signal_handlers = true, const char *const *argv = nullptr, bool add_program_dir_to_path = true) { #if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX - initialize_interpreter_pre_pyconfig(init_signal_handlers, argc, argv, add_program_dir_to_path); + detail::initialize_interpreter_pre_pyconfig( + init_signal_handlers, argc, argv, add_program_dir_to_path); #else PyConfig config; PyConfig_InitIsolatedConfig(&config); From 7f220345e4ac453464de2b333122fb985b388370 Mon Sep 17 00:00:00 2001 From: armnov Date: Fri, 25 Nov 2022 01:30:47 +0300 Subject: [PATCH 15/19] tests: Add program dir to path, Custom PyConfig with argv --- tests/test_embed/test_interpreter.cpp | 40 ++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 659acceaa1..5808ae26d2 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -180,6 +180,44 @@ TEST_CASE("Custom PyConfig") { } py::initialize_interpreter(); } + +TEST_CASE("Custom PyConfig with argv") { + py::finalize_interpreter(); + { + PyConfig config; + PyConfig_InitPythonConfig(&config); + config.install_signal_handlers = 1; + char *argv[] = {strdup("a.out")}; + py::scoped_interpreter argv_scope{&config, 1, argv}; + std::free(argv[0]); + auto module = py::module::import("test_interpreter"); + auto py_widget = module.attr("DerivedWidget")("The question"); + const auto &cpp_widget = py_widget.cast(); + REQUIRE(cpp_widget.argv0() == ""); + } + py::initialize_interpreter(); + +} + +TEST_CASE("Add program dir to path") { + static auto get_sys_path_size = []() -> size_t { + auto sys_path = py::module::import("sys").attr("path"); + return py::len(sys_path); + }; + py::finalize_interpreter(); + PyConfig config; + PyConfig_InitPythonConfig(&config); + size_t sys_path_default_size; + { + py::scoped_interpreter scoped_interp{&config, 0, nullptr, false}; + sys_path_default_size = get_sys_path_size(); + } + { + py::scoped_interpreter scoped_interp{&config}; // expected to append 1 elem to sys.path + REQUIRE(get_sys_path_size() == sys_path_default_size + 1); + } + py::initialize_interpreter(); +} #endif bool has_pybind11_internals_builtin() { @@ -412,4 +450,4 @@ TEST_CASE("make_iterator can be called before then after finalizing an interpret }()); py::initialize_interpreter(); -} +} \ No newline at end of file From e5e8c2894210b8f102418aab27a4f51b13ee93dc Mon Sep 17 00:00:00 2001 From: armnov Date: Fri, 25 Nov 2022 01:32:38 +0300 Subject: [PATCH 16/19] refact: clang-formatted --- tests/test_embed/test_interpreter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 5808ae26d2..071b94a0e2 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -196,7 +196,6 @@ TEST_CASE("Custom PyConfig with argv") { REQUIRE(cpp_widget.argv0() == ""); } py::initialize_interpreter(); - } TEST_CASE("Add program dir to path") { @@ -450,4 +449,4 @@ TEST_CASE("make_iterator can be called before then after finalizing an interpret }()); py::initialize_interpreter(); -} \ No newline at end of file +} From ed662deee0acd8d162c3628b1a3d1f122da9310d Mon Sep 17 00:00:00 2001 From: armnov Date: Sun, 27 Nov 2022 14:26:23 +0300 Subject: [PATCH 17/19] tests: Add-program-dir-to-path covers both scoped_interpreter overloads --- tests/test_embed/test_interpreter.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 071b94a0e2..b7b6db0a3c 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -185,18 +185,18 @@ TEST_CASE("Custom PyConfig with argv") { py::finalize_interpreter(); { PyConfig config; - PyConfig_InitPythonConfig(&config); - config.install_signal_handlers = 1; + PyConfig_InitIsolatedConfig(&config); char *argv[] = {strdup("a.out")}; py::scoped_interpreter argv_scope{&config, 1, argv}; std::free(argv[0]); auto module = py::module::import("test_interpreter"); auto py_widget = module.attr("DerivedWidget")("The question"); const auto &cpp_widget = py_widget.cast(); - REQUIRE(cpp_widget.argv0() == ""); + REQUIRE(cpp_widget.argv0() == "a.out"); } py::initialize_interpreter(); } +#endif TEST_CASE("Add program dir to path") { static auto get_sys_path_size = []() -> size_t { @@ -204,20 +204,25 @@ TEST_CASE("Add program dir to path") { return py::len(sys_path); }; py::finalize_interpreter(); - PyConfig config; - PyConfig_InitPythonConfig(&config); - size_t sys_path_default_size; + size_t sys_path_default_size = 0; { - py::scoped_interpreter scoped_interp{&config, 0, nullptr, false}; + py::scoped_interpreter scoped_interp{true, 0, nullptr, false}; sys_path_default_size = get_sys_path_size(); } { + py::scoped_interpreter scoped_interp{}; // expected to append 1 elem to sys.path + REQUIRE(get_sys_path_size() == sys_path_default_size + 1); + } +#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX + { + PyConfig config; + PyConfig_InitPythonConfig(&config); py::scoped_interpreter scoped_interp{&config}; // expected to append 1 elem to sys.path REQUIRE(get_sys_path_size() == sys_path_default_size + 1); } +#endif py::initialize_interpreter(); } -#endif bool has_pybind11_internals_builtin() { auto builtins = py::handle(PyEval_GetBuiltins()); From f11fcb9ac2f4827c1cad072fcdf36abfa0bd658f Mon Sep 17 00:00:00 2001 From: armnov Date: Sun, 27 Nov 2022 22:00:55 +0300 Subject: [PATCH 18/19] tests: Add-program-dir-to-path fixed --- tests/test_embed/test_interpreter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index b7b6db0a3c..c8f4607977 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -210,15 +210,15 @@ TEST_CASE("Add program dir to path") { sys_path_default_size = get_sys_path_size(); } { - py::scoped_interpreter scoped_interp{}; // expected to append 1 elem to sys.path - REQUIRE(get_sys_path_size() == sys_path_default_size + 1); + py::scoped_interpreter scoped_interp{}; // expected to append some to sys.path + REQUIRE(get_sys_path_size() > sys_path_default_size); } #if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX { PyConfig config; PyConfig_InitPythonConfig(&config); - py::scoped_interpreter scoped_interp{&config}; // expected to append 1 elem to sys.path - REQUIRE(get_sys_path_size() == sys_path_default_size + 1); + py::scoped_interpreter scoped_interp{&config}; // expected to append some to sys.path + REQUIRE(get_sys_path_size() > sys_path_default_size); } #endif py::initialize_interpreter(); From 6907f5acc095466a60079223b49a92d658912cf1 Mon Sep 17 00:00:00 2001 From: armnov Date: Mon, 28 Nov 2022 02:11:18 +0300 Subject: [PATCH 19/19] tests: Add-program-dir-to-path py_version dependant validation --- tests/test_embed/test_interpreter.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index c8f4607977..98fb3bae3b 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -203,7 +203,17 @@ TEST_CASE("Add program dir to path") { auto sys_path = py::module::import("sys").attr("path"); return py::len(sys_path); }; + static auto validate_path_len = [](size_t default_len) { +#if PY_VERSION_HEX < 0x030A0000 + // It seems a value remains in sys.path + // left by the previous call of scoped_interpreter ctor. + REQUIRE(get_sys_path_size() > default_len); +#else + REQUIRE(get_sys_path_size() == default_len + 1); +#endif + }; py::finalize_interpreter(); + size_t sys_path_default_size = 0; { py::scoped_interpreter scoped_interp{true, 0, nullptr, false}; @@ -211,14 +221,14 @@ TEST_CASE("Add program dir to path") { } { py::scoped_interpreter scoped_interp{}; // expected to append some to sys.path - REQUIRE(get_sys_path_size() > sys_path_default_size); + validate_path_len(sys_path_default_size); } #if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX { PyConfig config; PyConfig_InitPythonConfig(&config); py::scoped_interpreter scoped_interp{&config}; // expected to append some to sys.path - REQUIRE(get_sys_path_size() > sys_path_default_size); + validate_path_len(sys_path_default_size); } #endif py::initialize_interpreter();