From 68e6fdaa90fc93979e6d5d1e9f788f464593e8f2 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 21 Aug 2022 09:44:01 -0700 Subject: [PATCH] embed.h Python 3.11 `config.use_environment=1` + `PYTHONPATH` test (#4119) * Add debug fprintf to test_interpreter.cpp * Update `sys.path` from `PYTHONPATH` in Python >= 3.11 branch of `initialize_interpreter()` * Use `config.isolated = 0; config.use_environment = 1;` As suggsted by @vstinner here: https://github.com/pybind/pybind11/pull/4119#issuecomment-1219442853 * Add `TEST_CASE("PYTHONPATH is used to update sys.path")` * Fix clang-tidy error. * Use `_putenv_s()` under Windows. * Fix clang-tidy error: argument name ... in comment does not match parameter name * Remove slash from PYTHONPATH addition, to work around Windows slash-vs-backslash issue. * Use `py::str(...)` instead of `.attr("__str__")` as suggested by @skylion007 Co-authored-by: Aaron Gokaslan Co-authored-by: Aaron Gokaslan --- include/pybind11/embed.h | 2 ++ tests/test_embed/catch.cpp | 18 ++++++++++++++++++ tests/test_embed/test_interpreter.cpp | 7 +++++++ 3 files changed, 27 insertions(+) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index d6999cd77..0ac609e0f 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -150,6 +150,8 @@ inline void initialize_interpreter(bool init_signal_handlers = true, #else PyConfig 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)); diff --git a/tests/test_embed/catch.cpp b/tests/test_embed/catch.cpp index 96d2e3f92..a03a8b37c 100644 --- a/tests/test_embed/catch.cpp +++ b/tests/test_embed/catch.cpp @@ -20,7 +20,25 @@ namespace py = pybind11; int main(int argc, char *argv[]) { + // Setup for TEST_CASE in test_interpreter.cpp, tagging on a large random number: + std::string updated_pythonpath("pybind11_test_embed_PYTHONPATH_2099743835476552"); + const char *preexisting_pythonpath = getenv("PYTHONPATH"); + if (preexisting_pythonpath != nullptr) { +#if defined(_WIN32) + updated_pythonpath += ';'; +#else + updated_pythonpath += ':'; +#endif + updated_pythonpath += preexisting_pythonpath; + } +#if defined(_WIN32) + _putenv_s("PYTHONPATH", updated_pythonpath.c_str()); +#else + setenv("PYTHONPATH", updated_pythonpath.c_str(), /*replace=*/1); +#endif + py::scoped_interpreter guard{}; + auto result = Catch::Session().run(argc, argv); return result < 0xff ? result : 0xff; diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 1c45457a0..6299293b9 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -75,6 +75,13 @@ PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) { d["missing"].cast(); } +TEST_CASE("PYTHONPATH is used to update sys.path") { + // The setup for this TEST_CASE is in catch.cpp! + auto sys_path = py::str(py::module_::import("sys").attr("path")).cast(); + REQUIRE_THAT(sys_path, + Catch::Matchers::Contains("pybind11_test_embed_PYTHONPATH_2099743835476552")); +} + TEST_CASE("Pass classes and data between modules defined in C++ and Python") { auto module_ = py::module_::import("test_interpreter"); REQUIRE(py::hasattr(module_, "DerivedWidget"));