diff options
author | myrtle <gatecat@ds0.me> | 2022-09-15 09:06:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-15 09:06:35 +0200 |
commit | 3983d4fe53e2c609a5c76510aff8e998a4c22285 (patch) | |
tree | 1c4a543f661dd1b281aecf4660388491702fa8d8 /3rdparty/pybind11/tests/test_embed/test_interpreter.cpp | |
parent | f1349e114f3a16ccd002e8513339e18f5be4d31b (diff) | |
parent | a72f898ff4c4237424c468044a6db9d6953b541e (diff) | |
download | nextpnr-3983d4fe53e2c609a5c76510aff8e998a4c22285.tar.gz nextpnr-3983d4fe53e2c609a5c76510aff8e998a4c22285.tar.bz2 nextpnr-3983d4fe53e2c609a5c76510aff8e998a4c22285.zip |
Merge pull request #1024 from YosysHQ/gatecat/pybind11-bump
3rdparty: Bump vendored pybind11 version for py3.11 support
Diffstat (limited to '3rdparty/pybind11/tests/test_embed/test_interpreter.cpp')
-rw-r--r-- | 3rdparty/pybind11/tests/test_embed/test_interpreter.cpp | 180 |
1 files changed, 149 insertions, 31 deletions
diff --git a/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp b/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp index 944334ce..6299293b 100644 --- a/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp +++ b/3rdparty/pybind11/tests/test_embed/test_interpreter.cpp @@ -1,27 +1,29 @@ #include <pybind11/embed.h> #ifdef _MSC_VER -// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch -// 2.0.1; this should be fixed in the next catch release after 2.0.1). -# pragma warning(disable: 4996) +// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to +// catch 2.0.1; this should be fixed in the next catch release after 2.0.1). +# pragma warning(disable : 4996) #endif #include <catch.hpp> - -#include <thread> +#include <cstdlib> #include <fstream> #include <functional> +#include <thread> +#include <utility> namespace py = pybind11; using namespace py::literals; class Widget { public: - Widget(std::string message) : message(message) { } + explicit Widget(std::string message) : message(std::move(message)) {} virtual ~Widget() = default; std::string the_message() const { return message; } virtual int the_answer() const = 0; + virtual std::string argv0() const = 0; private: std::string message; @@ -31,6 +33,23 @@ class PyWidget final : public Widget { using Widget::Widget; int the_answer() const override { PYBIND11_OVERRIDE_PURE(int, Widget, the_answer); } + std::string argv0() const override { PYBIND11_OVERRIDE_PURE(std::string, Widget, argv0); } +}; + +class test_override_cache_helper { + +public: + virtual int func() { return 0; } + + test_override_cache_helper() = default; + virtual ~test_override_cache_helper() = default; + // Non-copyable + test_override_cache_helper &operator=(test_override_cache_helper const &Right) = delete; + test_override_cache_helper(test_override_cache_helper const &Copy) = delete; +}; + +class test_override_cache_helper_trampoline : public test_override_cache_helper { + int func() override { PYBIND11_OVERRIDE(int, test_override_cache_helper, func); } }; PYBIND11_EMBEDDED_MODULE(widget_module, m) { @@ -41,24 +60,39 @@ PYBIND11_EMBEDDED_MODULE(widget_module, m) { m.def("add", [](int i, int j) { return i + j; }); } -PYBIND11_EMBEDDED_MODULE(throw_exception, ) { - throw std::runtime_error("C++ Error"); +PYBIND11_EMBEDDED_MODULE(trampoline_module, m) { + py::class_<test_override_cache_helper, + test_override_cache_helper_trampoline, + std::shared_ptr<test_override_cache_helper>>(m, "test_override_cache_helper") + .def(py::init_alias<>()) + .def("func", &test_override_cache_helper::func); } +PYBIND11_EMBEDDED_MODULE(throw_exception, ) { throw std::runtime_error("C++ Error"); } + PYBIND11_EMBEDDED_MODULE(throw_error_already_set, ) { auto d = py::dict(); d["missing"].cast<py::object>(); } +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<std::string>(); + 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")); - auto locals = py::dict("hello"_a="Hello, World!", "x"_a=5, **module_.attr("__dict__")); + auto locals = py::dict("hello"_a = "Hello, World!", "x"_a = 5, **module_.attr("__dict__")); py::exec(R"( widget = DerivedWidget("{} - {}".format(hello, x)) message = widget.the_message - )", py::globals(), locals); + )", + py::globals(), + locals); REQUIRE(locals["message"].cast<std::string>() == "Hello, World! - 5"); auto py_widget = module_.attr("DerivedWidget")("The question"); @@ -69,12 +103,51 @@ TEST_CASE("Pass classes and data between modules defined in C++ and Python") { REQUIRE(cpp_widget.the_answer() == 42); } +TEST_CASE("Override cache") { + auto module_ = py::module_::import("test_trampoline"); + REQUIRE(py::hasattr(module_, "func")); + REQUIRE(py::hasattr(module_, "func2")); + + auto locals = py::dict(**module_.attr("__dict__")); + + int i = 0; + for (; i < 1500; ++i) { + std::shared_ptr<test_override_cache_helper> p_obj; + std::shared_ptr<test_override_cache_helper> p_obj2; + + py::object loc_inst = locals["func"](); + p_obj = py::cast<std::shared_ptr<test_override_cache_helper>>(loc_inst); + + int ret = p_obj->func(); + + REQUIRE(ret == 42); + + loc_inst = locals["func2"](); + + p_obj2 = py::cast<std::shared_ptr<test_override_cache_helper>>(loc_inst); + + p_obj2->func(); + } +} + TEST_CASE("Import error handling") { REQUIRE_NOTHROW(py::module_::import("widget_module")); - REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), - "ImportError: C++ Error"); + REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), "ImportError: C++ Error"); REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"), - Catch::Contains("ImportError: KeyError")); + Catch::Contains("ImportError: initialization failed")); + + auto locals = py::dict("is_keyerror"_a = false, "message"_a = "not set"); + py::exec(R"( + try: + import throw_error_already_set + except ImportError as e: + is_keyerror = type(e.__cause__) == KeyError + message = str(e.__cause__) + )", + py::globals(), + locals); + REQUIRE(locals["is_keyerror"].cast<bool>() == true); + REQUIRE(locals["message"].cast<std::string>() == "'missing'"); } TEST_CASE("There can be only one interpreter") { @@ -102,7 +175,7 @@ bool has_pybind11_internals_builtin() { bool has_pybind11_internals_static() { auto **&ipp = py::detail::get_internals_pp(); - return ipp && *ipp; + return (ipp != nullptr) && (*ipp != nullptr); } TEST_CASE("Restart the interpreter") { @@ -110,11 +183,12 @@ TEST_CASE("Restart the interpreter") { REQUIRE(py::module_::import("widget_module").attr("add")(1, 2).cast<int>() == 3); REQUIRE(has_pybind11_internals_builtin()); REQUIRE(has_pybind11_internals_static()); - REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast<int>() == 123); + REQUIRE(py::module_::import("external_module").attr("A")(123).attr("value").cast<int>() + == 123); // local and foreign module internals should point to the same internals: - REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) == - py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>()); + REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) + == py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>()); // Restart the interpreter. py::finalize_interpreter(); @@ -129,16 +203,19 @@ TEST_CASE("Restart the interpreter") { pybind11::detail::get_internals(); REQUIRE(has_pybind11_internals_builtin()); REQUIRE(has_pybind11_internals_static()); - REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) == - py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>()); + REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) + == py::module_::import("external_module").attr("internals_at")().cast<uintptr_t>()); // Make sure that an interpreter with no get_internals() created until finalize still gets the // internals destroyed py::finalize_interpreter(); py::initialize_interpreter(); bool ran = false; - py::module_::import("__main__").attr("internals_destroy_test") = - py::capsule(&ran, [](void *ran) { py::detail::get_internals(); *static_cast<bool *>(ran) = true; }); + py::module_::import("__main__").attr("internals_destroy_test") + = py::capsule(&ran, [](void *ran) { + py::detail::get_internals(); + *static_cast<bool *>(ran) = true; + }); REQUIRE_FALSE(has_pybind11_internals_builtin()); REQUIRE_FALSE(has_pybind11_internals_static()); REQUIRE_FALSE(ran); @@ -171,8 +248,8 @@ TEST_CASE("Subinterpreter") { REQUIRE(has_pybind11_internals_static()); /// Create and switch to a subinterpreter. - auto main_tstate = PyThreadState_Get(); - auto sub_tstate = Py_NewInterpreter(); + auto *main_tstate = PyThreadState_Get(); + auto *sub_tstate = Py_NewInterpreter(); // 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 @@ -212,7 +289,7 @@ TEST_CASE("Threads") { REQUIRE_FALSE(has_pybind11_internals_static()); constexpr auto num_threads = 10; - auto locals = py::dict("count"_a=0); + auto locals = py::dict("count"_a = 0); { py::gil_scoped_release gil_release{}; @@ -238,7 +315,11 @@ TEST_CASE("Threads") { struct scope_exit { std::function<void()> f_; explicit scope_exit(std::function<void()> f) noexcept : f_(std::move(f)) {} - ~scope_exit() { if (f_) f_(); } + ~scope_exit() { + if (f_) { + f_(); + } + } }; TEST_CASE("Reload module from file") { @@ -249,9 +330,8 @@ TEST_CASE("Reload module from file") { bool dont_write_bytecode = sys.attr("dont_write_bytecode").cast<bool>(); sys.attr("dont_write_bytecode") = true; // Reset the value at scope exit - scope_exit reset_dont_write_bytecode([&]() { - sys.attr("dont_write_bytecode") = dont_write_bytecode; - }); + scope_exit reset_dont_write_bytecode( + [&]() { sys.attr("dont_write_bytecode") = dont_write_bytecode; }); std::string module_name = "test_module_reload"; std::string module_file = module_name + ".py"; @@ -262,9 +342,7 @@ TEST_CASE("Reload module from file") { test_module << " return 1\n"; test_module.close(); // Delete the file at scope exit - scope_exit delete_module_file([&]() { - std::remove(module_file.c_str()); - }); + scope_exit delete_module_file([&]() { std::remove(module_file.c_str()); }); // Import the module from file auto module_ = py::module_::import(module_name.c_str()); @@ -282,3 +360,43 @@ TEST_CASE("Reload module from file") { result = module_.attr("test")().cast<int>(); REQUIRE(result == 2); } + +TEST_CASE("sys.argv gets initialized properly") { + py::finalize_interpreter(); + { + py::scoped_interpreter default_scope; + auto module = py::module::import("test_interpreter"); + auto py_widget = module.attr("DerivedWidget")("The question"); + const auto &cpp_widget = py_widget.cast<const Widget &>(); + REQUIRE(cpp_widget.argv0().empty()); + } + + { + char *argv[] = {strdup("a.out")}; + py::scoped_interpreter argv_scope(true, 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<const Widget &>(); + REQUIRE(cpp_widget.argv0() == "a.out"); + } + py::initialize_interpreter(); +} + +TEST_CASE("make_iterator can be called before then after finalizing an interpreter") { + // Reproduction of issue #2101 (https://github.com/pybind/pybind11/issues/2101) + py::finalize_interpreter(); + + std::vector<int> container; + { + pybind11::scoped_interpreter g; + auto iter = pybind11::make_iterator(container.begin(), container.end()); + } + + REQUIRE_NOTHROW([&]() { + pybind11::scoped_interpreter g; + auto iter = pybind11::make_iterator(container.begin(), container.end()); + }()); + + py::initialize_interpreter(); +} |