From 81fab52c7c9ab079f7da15ff74a32e8f550ccb0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 22 Jan 2026 08:30:27 +0100 Subject: [PATCH 01/23] applied #685 to async pr --- test/CMakeLists.txt | 5 +++- test/test_debugger.cpp | 57 +++++++++++++++++++++++++----------------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d5fafe2c..5aceaf62 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -45,6 +45,9 @@ endif() find_package(Threads) find_package(doctest) +cmake_policy(SET CMP0167 OLD) # use find boost from old cmake +find_package(Boost REQUIRED COMPONENTS process filesystem) + include_directories(${GTEST_INCLUDE_DIRS} SYSTEM) set(XEUS_PYTHON_TESTS @@ -70,7 +73,7 @@ set_target_properties(test_xeus_python PROPERTIES ) include_directories(${PYTHON_INCLUDE_DIRS}) -target_link_libraries(test_xeus_python ${PYTHON_LIBRARIES} xeus-zmq doctest::doctest ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(test_xeus_python ${PYTHON_LIBRARIES} xeus-zmq doctest::doctest Boost::headers Boost::filesystem Boost::process ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(test_xeus_python PRIVATE ${XEUS_PYTHON_INCLUDE_DIR}) add_custom_target(xtest COMMAND test_xeus_python DEPENDS test_xeus_python) diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index 23e515f3..7593009e 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -35,6 +35,11 @@ #include #endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + /*********************************** * Should be moved in a utils file * ***********************************/ @@ -1147,19 +1152,25 @@ void dump_connection_file() } } -void start_kernel() +struct KernelProcess { - dump_connection_file(); - std::string cmd = "xpython -f " + KERNEL_JSON + "&"; - int ret2 = std::system(cmd.c_str()); - std::this_thread::sleep_for(2s); -} + KernelProcess() + { + std::this_thread::sleep_for(2s); + } + +private: + + bool _ = [] { dump_connection_file(); return true; }(); + boost::asio::io_context ctx; + boost::process::process process{ ctx, boost::process::environment::find_executable("xpython"), { "-f" , KERNEL_JSON } }; +}; TEST_SUITE("debugger") { TEST_CASE("init") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1176,7 +1187,7 @@ TEST_SUITE("debugger") TEST_CASE("disconnect") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1192,7 +1203,7 @@ TEST_SUITE("debugger") TEST_CASE("attach") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1209,7 +1220,7 @@ TEST_SUITE("debugger") TEST_CASE("multisession") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1228,7 +1239,7 @@ TEST_SUITE("debugger") TEST_CASE("set_external_breakpoints") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1247,7 +1258,7 @@ TEST_SUITE("debugger") /* TEST_CASE("external_next_continue") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1264,7 +1275,7 @@ TEST_SUITE("debugger") */ TEST_CASE("set_breakpoints") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1281,7 +1292,7 @@ TEST_SUITE("debugger") TEST_CASE("set_exception_breakpoints") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1298,7 +1309,7 @@ TEST_SUITE("debugger") TEST_CASE("source") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1317,7 +1328,7 @@ TEST_SUITE("debugger") /* TEST_CASE("next_continue") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1337,7 +1348,7 @@ TEST_SUITE("debugger") /* TEST_CASE("stepin") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1355,7 +1366,7 @@ TEST_SUITE("debugger") TEST_CASE("stack_trace") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1372,7 +1383,7 @@ TEST_SUITE("debugger") TEST_CASE("debug_info") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1389,7 +1400,7 @@ TEST_SUITE("debugger") TEST_CASE("inspect_variables") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1408,7 +1419,7 @@ TEST_SUITE("debugger") /* TEST_CASE("rich_inspect_variables") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1425,7 +1436,7 @@ TEST_SUITE("debugger") TEST_CASE("variables") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1442,7 +1453,7 @@ TEST_SUITE("debugger") TEST_CASE("copy_to_globals") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { From 3f20bb3e8f8196da8bd41f47e0fa638cf7bdd53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 22 Jan 2026 09:45:10 +0100 Subject: [PATCH 02/23] added missing boost dependency --- environment-dev.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment-dev.yml b/environment-dev.yml index 7c8335ec..0e9fee44 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -25,3 +25,4 @@ dependencies: - jupyter_kernel_test<0.8 - doctest - pluggy=1.3 + - libboost From 51f6d5da23b8fe27704e23bc8cf0fbf23dd2250c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 22 Jan 2026 05:19:02 +0100 Subject: [PATCH 03/23] ignore root build dirs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 041239f7..97eb65ac 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ docs/build/ # Build directory build/ +/build-*/ # generated kernel specs /share/jupyter/kernels/xpython/kernel.json From 4620d877a3590e7c8877815a7ea013e150b0e2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:04:11 +0100 Subject: [PATCH 04/23] added missing boost packages dependencies --- environment-dev.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/environment-dev.yml b/environment-dev.yml index 0e9fee44..e1483547 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -26,3 +26,6 @@ dependencies: - doctest - pluggy=1.3 - libboost + - libboost-devel + - libboost-headers + From 0323cb4a296ca68c7662bbd1c66807a6b8a56665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:18:02 +0100 Subject: [PATCH 05/23] debug duct tape and more info --- include/xeus-python/xdebugger.hpp | 13 ++++++ src/main.cpp | 4 ++ src/xdebugger.cpp | 3 ++ src/xinterpreter.cpp | 30 ++++++++++++++ src/xpaths.cpp | 13 ++++++ test/test_debugger.cpp | 69 +++++++++++++++++++++++++++---- 6 files changed, 125 insertions(+), 7 deletions(-) diff --git a/include/xeus-python/xdebugger.hpp b/include/xeus-python/xdebugger.hpp index 6c7743ae..725ce0cb 100644 --- a/include/xeus-python/xdebugger.hpp +++ b/include/xeus-python/xdebugger.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "nlohmann/json.hpp" #include "pybind11/pybind11.h" @@ -69,7 +70,19 @@ namespace xpyt std::string m_debugpy_host; std::string m_debugpy_port; nl::json m_debugger_config; + + struct after { ~after(){ std::cout << "\n### " << std::this_thread::get_id() << " DESTROYING PYDEBUGGER - DONE" << std::endl; } } after_pydebugger; py::object m_pydebugger; + struct before { + py::object& ref_pydebugger; + __declspec(noinline) ~before(){ + std::cout << "\n### " << std::this_thread::get_id() << " DESTROYING PYDEBUGGER ..." << std::endl; + py::gil_scoped_acquire acquire; + auto pydebugger = std::move(ref_pydebugger); + std::cout << "\n### " << std::this_thread::get_id() << " DESTROYING PYDEBUGGER - local destroy ..." << std::endl; + } + } before_pydebugger{ m_pydebugger }; + xeus::xthread m_client_runner; bool m_copy_to_globals_available; }; diff --git a/src/main.cpp b/src/main.cpp index 8862a319..6cc41ff4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -80,6 +80,10 @@ int main(int argc, char* argv[]) // Setting Program Name static const std::string executable(xpyt::get_python_path()); static const std::wstring wexecutable(executable.cbegin(), executable.cend()); + if (!std::filesystem::exists(wexecutable)) + { + throw std::runtime_error(std::string("cannot find python executable, tried ") + executable); + } config.program_name = const_cast(wexecutable.c_str()); // Setting Python Home diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 58d534da..a2cca1c5 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -255,6 +255,7 @@ namespace xpyt if (std::getenv("XEUS_LOG") != nullptr) { std::ofstream out("xeus.log", std::ios_base::app); + //auto& out = std::cout; out << "===== DEBUGGER CONFIG =====" << std::endl; out << m_debugger_config.dump() << std::endl; } @@ -283,9 +284,11 @@ namespace xpyt } else { + std::cout << "\n### " << std::this_thread::get_id() << " CREATING PYDEBUGGER ..." << std::endl; py::gil_scoped_acquire acquire; py::module xeus_python_shell = py::module::import("xeus_python_shell.debugger"); m_pydebugger = xeus_python_shell.attr("XDebugger")(); + std::cout << "\n### " << std::this_thread::get_id() << " CREATING PYDEBUGGER - DONE" << std::endl; // Get debugpy version std::string expression = "debugpy.__version__"; diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 548935e1..0da26021 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -363,6 +363,36 @@ namespace xpyt return xeus::create_error_reply(error.m_ename, error.m_evalue, error.m_traceback); } + + std::cout << "\n--> processing error ... " << std::endl; + py::list pyerror = [&]() -> py::list { + try { + auto last_error = m_ipython_shell.attr("last_error"); + std::cout << "\n--> converting last_error to list ... " << std::endl; + return py::list(last_error); + } + catch (py::error_already_set& e) + { + std::cout << "\n--> processing error: failed acquiring `last_error` " << std::endl; + return py::list{}; + } + }(); + + if (pyerror.empty()) + { + return xeus::create_error_reply("SNAFU", "python SNAFU"); + } + + std::cout << "\n--> A " << std::endl; + xerror error = extract_error(pyerror); + std::cout << "\n--> B " << std::endl; + publish_execution_error(error.m_ename, error.m_evalue, error.m_traceback); + std::cout << "\n--> C " << std::endl; + error.m_traceback.resize(1); + error.m_traceback[0] = code; + std::cout << "\n--> D " << std::endl; + return xeus::create_error_reply(error.m_ename, error.m_evalue, error.m_traceback); + } void interpreter::set_request_context(xeus::xrequest_context context) diff --git a/src/xpaths.cpp b/src/xpaths.cpp index 9ccc468d..3ca84226 100644 --- a/src/xpaths.cpp +++ b/src/xpaths.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "pybind11/pybind11.h" @@ -44,7 +45,12 @@ namespace xpyt #elif defined(XEUS_PYTHONHOME_ABSPATH) static const std::string pythonhome = XPYT_STRINGIFY(XEUS_PYTHONHOME_ABSPATH); #else +# ifdef _WIN32 + using namespace std::filesystem; + static const std::string pythonhome = canonical(xeus::prefix_path()).parent_path().string(); // wont work with unicode paths +# else static const std::string pythonhome = xeus::prefix_path(); +# endif #endif return pythonhome; } @@ -52,6 +58,13 @@ namespace xpyt std::string get_python_path() { + const char* python_exe_environment = std::getenv("PYTHON_EXECUTABLE"); + if (python_exe_environment != nullptr && std::strlen(python_exe_environment) != 0) + { + static const std::string python_exe_path = python_exe_environment; + return python_exe_path; + } + std::string python_prefix = get_python_prefix(); #ifdef _WIN32 if (python_prefix.back() != '\\') diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index 7593009e..9075e0b3 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -1070,6 +1070,8 @@ class timer { public: + struct timeout : std::runtime_error { using std::runtime_error::runtime_error; }; + timer(); ~timer(); @@ -1113,8 +1115,10 @@ void timer::run_timer() std::unique_lock lk(m_mcv); if (!m_cv.wait_for(lk, std::chrono::seconds(20), [this]() { return m_done; })) { - std::clog << "Unit test time out !!" << std::endl; - std::terminate(); + constexpr auto message = "Unit test time out !!"; + std::clog << message << std::endl; + //std::terminate(); + throw timeout{ message }; // same as calling terminate if unhandled, but some impl will also display the error in the console } } @@ -1154,18 +1158,69 @@ void dump_connection_file() struct KernelProcess { + KernelProcess() { - std::this_thread::sleep_for(2s); + running.emplace_back(m_impl); + std::cout << "=> xpython launched" << std::endl; + std::this_thread::sleep_for(4s); } -private: + ~KernelProcess() + { + std::cout << "=> xpython - destructor end" << std::endl; + } - bool _ = [] { dump_connection_file(); return true; }(); - boost::asio::io_context ctx; - boost::process::process process{ ctx, boost::process::environment::find_executable("xpython"), { "-f" , KERNEL_JSON } }; + private: + struct Impl + { + bool _ = [] { dump_connection_file(); return true; }(); + boost::asio::io_context ctx; + boost::filesystem::path xpython_path = boost::process::environment::find_executable("xpython"); + boost::process::process process{ ctx, xpython_path, { "-f" , KERNEL_JSON } }; + + struct on_destruction { + ~on_destruction() + { + std::cout << "=> xpython - destructor begin" << std::endl; + } + }; + }; + + std::shared_ptr m_impl = std::make_shared(); + + struct on_program_exit + { + ~on_program_exit() + { + std::cout << "=> test program exiting (after `main()`): explicitly terminate remaining sub-processes ..." << std::endl; + for (auto& maybe_process : running) + { + if (auto process_impl = maybe_process.lock()) + { + std::cout << "=> request exit politely : " << process_impl->xpython_path << "(" << process_impl->process.id() << ") ..." << std::endl; + process_impl->process.request_exit(); + //process_impl->process.wait(); + std::this_thread::sleep_for(std::chrono::seconds(2)); + if (process_impl->process.running()) + { + std::cout << "=> still running, force terminate : " << process_impl->xpython_path << "(" << process_impl->process.id() << ") ..." << std::endl; + process_impl->process.terminate(); + process_impl->process.wait(); + } + std::cout << "=> terminate " << process_impl->xpython_path << "(" << process_impl->process.id() << ") - DONE" << std::endl; + } + } + std::cout << "=> test program exiting : explicitly terminate remaining sub-processes - DONE" << std::endl; + } + }; + static std::vector> running; + static on_program_exit run_on_program_exit; }; +std::vector> KernelProcess::running; +KernelProcess::on_program_exit KernelProcess::run_on_program_exit; + TEST_SUITE("debugger") { TEST_CASE("init") From c93706f11579510a9987cc45d9d7d6c7be35ffe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Mon, 2 Mar 2026 15:14:49 +0100 Subject: [PATCH 06/23] removed msvc-specific instruction --- include/xeus-python/xdebugger.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/xeus-python/xdebugger.hpp b/include/xeus-python/xdebugger.hpp index 725ce0cb..f4275047 100644 --- a/include/xeus-python/xdebugger.hpp +++ b/include/xeus-python/xdebugger.hpp @@ -75,7 +75,7 @@ namespace xpyt py::object m_pydebugger; struct before { py::object& ref_pydebugger; - __declspec(noinline) ~before(){ + ~before(){ std::cout << "\n### " << std::this_thread::get_id() << " DESTROYING PYDEBUGGER ..." << std::endl; py::gil_scoped_acquire acquire; auto pydebugger = std::move(ref_pydebugger); From d69bab824dbaa40fe2e2e55ff095bacb2f983d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:37:33 +0100 Subject: [PATCH 07/23] tests wait for xpython's ready message instead of sleep --- test/test_debugger.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index 9075e0b3..9a5b3a33 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -38,6 +38,7 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#include #include /*********************************** @@ -1163,7 +1164,14 @@ struct KernelProcess { running.emplace_back(m_impl); std::cout << "=> xpython launched" << std::endl; - std::this_thread::sleep_for(4s); + + // wait for xpython to be ready before continuing + constexpr std::string_view ready_message = "Run with XEUS"; + std::string err_output; + boost::asio::read_until(m_impl->err_pipe, boost::asio::dynamic_buffer(err_output), ready_message); + + std::cout << "=> xpython is ready" << std::endl; + } ~KernelProcess() @@ -1176,8 +1184,9 @@ struct KernelProcess { bool _ = [] { dump_connection_file(); return true; }(); boost::asio::io_context ctx; + boost::asio::readable_pipe err_pipe{ ctx }; boost::filesystem::path xpython_path = boost::process::environment::find_executable("xpython"); - boost::process::process process{ ctx, xpython_path, { "-f" , KERNEL_JSON } }; + boost::process::process process{ ctx, xpython_path, { "-f" , KERNEL_JSON }, boost::process::process_stdio{{}, {}, err_pipe} }; struct on_destruction { ~on_destruction() From 813043a4f5ec8978ef9e79406a9d09b38807ae88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:18:02 +0100 Subject: [PATCH 08/23] debug duct tape and more info --- include/xeus-python/xdebugger.hpp | 1 + src/xinterpreter.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/include/xeus-python/xdebugger.hpp b/include/xeus-python/xdebugger.hpp index f4275047..6251217b 100644 --- a/include/xeus-python/xdebugger.hpp +++ b/include/xeus-python/xdebugger.hpp @@ -72,6 +72,7 @@ namespace xpyt nl::json m_debugger_config; struct after { ~after(){ std::cout << "\n### " << std::this_thread::get_id() << " DESTROYING PYDEBUGGER - DONE" << std::endl; } } after_pydebugger; + py::object m_pydebugger; struct before { py::object& ref_pydebugger; diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 0da26021..78be16a4 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -393,6 +393,36 @@ namespace xpyt std::cout << "\n--> D " << std::endl; return xeus::create_error_reply(error.m_ename, error.m_evalue, error.m_traceback); + + std::cout << "\n--> processing error ... " << std::endl; + py::list pyerror = [&]() -> py::list { + try { + auto last_error = m_ipython_shell.attr("last_error"); + std::cout << "\n--> converting last_error to list ... " << std::endl; + return py::list(last_error); + } + catch (py::error_already_set& e) + { + std::cout << "\n--> processing error: failed acquiring `last_error` " << std::endl; + return py::list{}; + } + }(); + + if (pyerror.empty()) + { + return xeus::create_error_reply("SNAFU", "python SNAFU"); + } + + std::cout << "\n--> A " << std::endl; + xerror error = extract_error(pyerror); + std::cout << "\n--> B " << std::endl; + publish_execution_error(error.m_ename, error.m_evalue, error.m_traceback); + std::cout << "\n--> C " << std::endl; + error.m_traceback.resize(1); + error.m_traceback[0] = code; + std::cout << "\n--> D " << std::endl; + return xeus::create_error_reply(error.m_ename, error.m_evalue, error.m_traceback); + } void interpreter::set_request_context(xeus::xrequest_context context) From b9fb70df08412eaf4a6be614b6804810f1929e2b Mon Sep 17 00:00:00 2001 From: DerThorsten Date: Wed, 18 Mar 2026 10:09:25 +0100 Subject: [PATCH 09/23] fi --- src/xinterpreter.cpp | 75 +++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 78be16a4..a1994f51 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -337,62 +337,70 @@ namespace xpyt nl::json interpreter::internal_request_impl(const nl::json& content) { + std::cout << "Received internal request with content: " << content.dump(4) << std::endl; py::gil_scoped_acquire acquire; std::string code = content.value("code", ""); + std::cout<<"reset traceback"< processing error ... " << std::endl; - py::list pyerror = [&]() -> py::list { - try { - auto last_error = m_ipython_shell.attr("last_error"); - std::cout << "\n--> converting last_error to list ... " << std::endl; - return py::list(last_error); + std::cout<<"creating error reply"< processing error: failed acquiring `last_error` " << std::endl; - return py::list{}; + std::cout<<"an error occurred during error handling: "<()); } - }(); - - if (pyerror.empty()) + catch(std::exception& e) + { + std::cout<<"a standard exception occurred during error handling: "<()); + } + catch (...) + { + std::cout<<"an unknown error occurred during error handling"<()); + } + } + catch(std::exception& e) { - return xeus::create_error_reply("SNAFU", "python SNAFU"); + std::cout<<"a standard exception occurred during code execution: "<()); + } + catch (...) + { + std::cout<<"an unknown error occurred during code execution"<()); } - - std::cout << "\n--> A " << std::endl; - xerror error = extract_error(pyerror); - std::cout << "\n--> B " << std::endl; - publish_execution_error(error.m_ename, error.m_evalue, error.m_traceback); - std::cout << "\n--> C " << std::endl; - error.m_traceback.resize(1); - error.m_traceback[0] = code; - std::cout << "\n--> D " << std::endl; - return xeus::create_error_reply(error.m_ename, error.m_evalue, error.m_traceback); - std::cout << "\n--> processing error ... " << std::endl; py::list pyerror = [&]() -> py::list { @@ -425,6 +433,7 @@ namespace xpyt } + void interpreter::set_request_context(xeus::xrequest_context context) { py::gil_scoped_acquire acquire; From 2ffd3aeb44c9eb256cd73fb8162e2925751b6b1b Mon Sep 17 00:00:00 2001 From: DerThorsten Date: Wed, 18 Mar 2026 10:15:22 +0100 Subject: [PATCH 10/23] fix --- test/test_debugger.cpp | 73 ++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index 9a5b3a33..bc11d703 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -8,8 +8,10 @@ * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ + #include "doctest/doctest.h" + #include #include #include @@ -19,6 +21,7 @@ #include #include + #include "xeus/xsystem.hpp" #include "xeus_client.hpp" @@ -367,7 +370,7 @@ nl::json make_exception_breakpoint_request(int seq) })}, {"breakMode", "always"} }; - nl::json options = nl::json::array({except_option, except_option}); + nl::json options = nl::json::array({ except_option, except_option }); nl::json req = { {"type", "request"}, {"seq", seq}, @@ -389,8 +392,8 @@ class debugger_client public: debugger_client(xeus::xcontext& context, - const std::string& connection_file, - const std::string& log_file); + const std::string& connection_file, + const std::string& log_file); bool test_init(); bool test_disconnect(); @@ -436,10 +439,10 @@ class debugger_client }; debugger_client::debugger_client(xeus::xcontext& context, - const std::string& connection_file, - const std::string& log_file) + const std::string& connection_file, + const std::string& log_file) : m_client(context, "debugger_client", - std::get(xeus::load_configuration(connection_file)), log_file) + xeus::load_configuration(connection_file), log_file) { } @@ -477,7 +480,7 @@ bool debugger_client::print_code_variable(const std::string& expected, int& seq) ++seq; nl::json json1 = m_client.receive_on_control(); - if(json1["content"]["body"]["stackFrames"].empty()) + if (json1["content"]["body"]["stackFrames"].empty()) { m_client.send_on_control("debug_request", make_stacktrace_request(seq, 1)); ++seq; @@ -497,11 +500,11 @@ bool debugger_client::print_code_variable(const std::string& expected, int& seq) const auto& ar = json3["content"]["body"]["variables"]; bool var_found = false; std::string name, value; - for(auto it = ar.begin(); it != ar.end() && !var_found; ++it) + for (auto it = ar.begin(); it != ar.end() && !var_found; ++it) { auto d = *it; name = d["name"]; - if(name == "i") + if (name == "i") { var_found = true; value = d["value"]; @@ -751,7 +754,7 @@ bool debugger_client::test_inspect_variables() auto check_var = [&vars](const std::string& name, const std::string& value) { auto x = std::find_if(vars.begin(), vars.end(), [&name](const nl::json& var) { return var.is_object() && var.value("name", "") == name; - }); + }); if (x == vars.end()) { std::cout << "missing " << name << std::endl; @@ -759,7 +762,7 @@ bool debugger_client::test_inspect_variables() } nl::json var = *x; return var["value"] == value && var["variablesReference"] == 0; - }; + }; bool res = check_var("i", "4") && check_var("j", "8") && check_var("k", "5"); return res; @@ -864,7 +867,7 @@ bool debugger_client::test_variables() ++seq; nl::json json1 = m_client.receive_on_control(); - if(json1["content"]["body"]["stackFrames"].empty()) + if (json1["content"]["body"]["stackFrames"].empty()) { m_client.send_on_control("debug_request", make_stacktrace_request(seq, 1)); ++seq; @@ -915,7 +918,7 @@ bool debugger_client::test_copy_to_globals() m_client.send_on_control("debug_request", make_stacktrace_request(seq, 1)); ++seq; nl::json json1 = m_client.receive_on_control(); - if(json1["content"]["body"]["stackFrames"].empty()) + if (json1["content"]["body"]["stackFrames"].empty()) { m_client.send_on_control("debug_request", make_stacktrace_request(seq, 1)); ++seq; @@ -941,7 +944,7 @@ bool debugger_client::test_copy_to_globals() ++seq; nl::json json3 = m_client.receive_on_control(); nl::json local_var = {}; - for (auto &var: json3["content"]["body"]["variables"]){ + for (auto& var : json3["content"]["body"]["variables"]) { if (var["evaluateName"] == local_var_name) { local_var = var; } @@ -958,7 +961,7 @@ bool debugger_client::test_copy_to_globals() ++seq; nl::json json4 = m_client.receive_on_control(); nl::json global_var = {}; - for (auto &var: json4["content"]["body"]["variables"]){ + for (auto& var : json4["content"]["body"]["variables"]) { if (var["evaluateName"] == global_var_name) { global_var = var; } @@ -1040,7 +1043,7 @@ std::string debugger_client::get_external_path() void debugger_client::dump_external_file() { static bool already_dumped = false; - if(!already_dumped) + if (!already_dumped) { std::ofstream out(get_external_path()); out << make_external_code() << std::endl; @@ -1149,7 +1152,7 @@ void dump_connection_file() "kernel_name": "xcpp" } )"; - if(!dumped) + if (!dumped) { std::ofstream out(KERNEL_JSON); out << connection_file; @@ -1169,7 +1172,7 @@ struct KernelProcess constexpr std::string_view ready_message = "Run with XEUS"; std::string err_output; boost::asio::read_until(m_impl->err_pipe, boost::asio::dynamic_buffer(err_output), ready_message); - + std::cout << "=> xpython is ready" << std::endl; } @@ -1479,24 +1482,24 @@ TEST_SUITE("debugger") } } -// TODO: Get test_rich_inspect_variables to work -/* - TEST_CASE("rich_inspect_variables") - { - KernelProcess xpython_process; - timer t; - auto context_ptr = xeus::make_zmq_context(); + // TODO: Get test_rich_inspect_variables to work + /* + TEST_CASE("rich_inspect_variables") { - debugger_client deb(*context_ptr, KERNEL_JSON, "debugger_rich_inspect_variables.log"); - deb.start(); - bool res = deb.test_rich_inspect_variables(); - deb.shutdown(); - std::this_thread::sleep_for(2s); - CHECK(res); - t.notify_done(); + KernelProcess xpython_process; + timer t; + auto context_ptr = xeus::make_zmq_context(); + { + debugger_client deb(*context_ptr, KERNEL_JSON, "debugger_rich_inspect_variables.log"); + deb.start(); + bool res = deb.test_rich_inspect_variables(); + deb.shutdown(); + std::this_thread::sleep_for(2s); + CHECK(res); + t.notify_done(); + } } - } -*/ + */ TEST_CASE("variables") { @@ -1531,4 +1534,4 @@ TEST_SUITE("debugger") t.notify_done(); } } -} +} \ No newline at end of file From 6b3f143a3a3427c5a5b2dad79d1668d62a6fa102 Mon Sep 17 00:00:00 2001 From: DerThorsten Date: Wed, 18 Mar 2026 10:40:14 +0100 Subject: [PATCH 11/23] x6 --- test/test_debugger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index bc11d703..a6545713 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -442,7 +442,7 @@ debugger_client::debugger_client(xeus::xcontext& context, const std::string& connection_file, const std::string& log_file) : m_client(context, "debugger_client", - xeus::load_configuration(connection_file), log_file) + std::get(xeus::load_configuration(connection_file)), log_file) { } From f60aa9214058ec5e1d43b7bb963fcba92156aac1 Mon Sep 17 00:00:00 2001 From: DerThorsten Date: Wed, 18 Mar 2026 11:04:16 +0100 Subject: [PATCH 12/23] one minute --- test/test_debugger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index a6545713..ee10902b 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -1117,7 +1117,7 @@ void timer::notify_done() void timer::run_timer() { std::unique_lock lk(m_mcv); - if (!m_cv.wait_for(lk, std::chrono::seconds(20), [this]() { return m_done; })) + if (!m_cv.wait_for(lk, std::chrono::seconds(60), [this]() { return m_done; })) { constexpr auto message = "Unit test time out !!"; std::clog << message << std::endl; From 699c28e928c1e5e0710316ed39fcef5ccdd946bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Mon, 23 Mar 2026 17:05:22 +0100 Subject: [PATCH 13/23] cleaner fix: lock python's GIL when destroying the debugger --- include/xeus-python/xdebugger.hpp | 13 ------------- src/xdebugger.cpp | 3 +++ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/include/xeus-python/xdebugger.hpp b/include/xeus-python/xdebugger.hpp index 6251217b..d5464932 100644 --- a/include/xeus-python/xdebugger.hpp +++ b/include/xeus-python/xdebugger.hpp @@ -70,20 +70,7 @@ namespace xpyt std::string m_debugpy_host; std::string m_debugpy_port; nl::json m_debugger_config; - - struct after { ~after(){ std::cout << "\n### " << std::this_thread::get_id() << " DESTROYING PYDEBUGGER - DONE" << std::endl; } } after_pydebugger; - py::object m_pydebugger; - struct before { - py::object& ref_pydebugger; - ~before(){ - std::cout << "\n### " << std::this_thread::get_id() << " DESTROYING PYDEBUGGER ..." << std::endl; - py::gil_scoped_acquire acquire; - auto pydebugger = std::move(ref_pydebugger); - std::cout << "\n### " << std::this_thread::get_id() << " DESTROYING PYDEBUGGER - local destroy ..." << std::endl; - } - } before_pydebugger{ m_pydebugger }; - xeus::xthread m_client_runner; bool m_copy_to_globals_available; }; diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index a2cca1c5..bfe2e811 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -72,6 +72,9 @@ namespace xpyt debugger::~debugger() { + // release/destroy the debugger python object while GIL is acquired + pybind11::gil_scoped_acquire gil_lock; + auto local_debug = std::move(m_pydebugger); } nl::json debugger::inspect_variables_request(const nl::json& message) From 4d0b596b14083aff9fb832f935a37cdbf2946d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Tue, 24 Mar 2026 17:55:43 +0100 Subject: [PATCH 14/23] cleanup python path deduction unicode-friendly (on windows) and absolute paths --- src/main.cpp | 8 ++++++-- src/xpaths.cpp | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6cc41ff4..9a907fbe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -88,8 +88,12 @@ int main(int argc, char* argv[]) // Setting Python Home static const std::string pythonhome{ xpyt::get_python_prefix() }; - static const std::wstring wstr(pythonhome.cbegin(), pythonhome.cend());; - config.home = const_cast(wstr.c_str()); + static const std::wstring wpythonhome(pythonhome.cbegin(), pythonhome.cend()); + if (!std::filesystem::exists(wpythonhome)) + { + throw std::runtime_error(std::string("cannot find python home directory, tried ") + pythonhome); + } + config.home = const_cast(wpythonhome.c_str()); xpyt::print_pythonhome(); // Implicitly pre-initialize Python diff --git a/src/xpaths.cpp b/src/xpaths.cpp index 3ca84226..79f1db34 100644 --- a/src/xpaths.cpp +++ b/src/xpaths.cpp @@ -45,11 +45,20 @@ namespace xpyt #elif defined(XEUS_PYTHONHOME_ABSPATH) static const std::string pythonhome = XPYT_STRINGIFY(XEUS_PYTHONHOME_ABSPATH); #else -# ifdef _WIN32 using namespace std::filesystem; - static const std::string pythonhome = canonical(xeus::prefix_path()).parent_path().string(); // wont work with unicode paths +# ifdef _WIN32 + // python is located in the root dir of the env on Windows, not the prefix path which is in env-root/Library/ + static const std::string pythonhome = [] { + // makes sure std::filesystem knows the string we pass is indeed UTF-8 (by convention) + // and not some other encoding (as often expected on Windows) + const path prefix_path_u8(xeus::prefix_path(), std::locale("en_US.UTF-8")); + // we need the parent path of the prefix path + const auto python_dir_u8 = canonical(prefix_path_u8).parent_path().u8string(); + // TOOD: in C++20, `python_dir_u8` will have changed type to `std::u8string`, a conversion would be needed here. + return python_dir_u8; + }(); # else - static const std::string pythonhome = xeus::prefix_path(); + static const std::string pythonhome = canonical(xeus::prefix_path()).u8string(); # endif #endif return pythonhome; From 8cc679468f4119ed26c3b8c214572344ed76f315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 26 Mar 2026 12:30:57 +0100 Subject: [PATCH 15/23] removed debugging logs & cleaned error logs --- src/xdebugger.cpp | 4 +--- src/xinterpreter.cpp | 55 ++++++-------------------------------------- 2 files changed, 8 insertions(+), 51 deletions(-) diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index bfe2e811..7624c50f 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -287,12 +287,10 @@ namespace xpyt } else { - std::cout << "\n### " << std::this_thread::get_id() << " CREATING PYDEBUGGER ..." << std::endl; py::gil_scoped_acquire acquire; py::module xeus_python_shell = py::module::import("xeus_python_shell.debugger"); m_pydebugger = xeus_python_shell.attr("XDebugger")(); - std::cout << "\n### " << std::this_thread::get_id() << " CREATING PYDEBUGGER - DONE" << std::endl; - + // Get debugpy version std::string expression = "debugpy.__version__"; std::string version = (eval(py::str(expression))).cast(); diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index a1994f51..82c170ae 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -337,100 +337,59 @@ namespace xpyt nl::json interpreter::internal_request_impl(const nl::json& content) { - std::cout << "Received internal request with content: " << content.dump(4) << std::endl; + std::cerr << "Received internal request with content: " << content.dump(4) << std::endl; py::gil_scoped_acquire acquire; std::string code = content.value("code", ""); - std::cout<<"reset traceback"<()); } catch(std::exception& e) { - std::cout<<"a standard exception occurred during error handling: "<()); } catch (...) { - std::cout<<"an unknown error occurred during error handling"<()); } } catch(std::exception& e) { - std::cout<<"a standard exception occurred during code execution: "<()); } catch (...) { - std::cout<<"an unknown error occurred during code execution"<()); } - - std::cout << "\n--> processing error ... " << std::endl; - py::list pyerror = [&]() -> py::list { - try { - auto last_error = m_ipython_shell.attr("last_error"); - std::cout << "\n--> converting last_error to list ... " << std::endl; - return py::list(last_error); - } - catch (py::error_already_set& e) - { - std::cout << "\n--> processing error: failed acquiring `last_error` " << std::endl; - return py::list{}; - } - }(); - - if (pyerror.empty()) - { - return xeus::create_error_reply("SNAFU", "python SNAFU"); - } - - std::cout << "\n--> A " << std::endl; - xerror error = extract_error(pyerror); - std::cout << "\n--> B " << std::endl; - publish_execution_error(error.m_ename, error.m_evalue, error.m_traceback); - std::cout << "\n--> C " << std::endl; - error.m_traceback.resize(1); - error.m_traceback[0] = code; - std::cout << "\n--> D " << std::endl; - return xeus::create_error_reply(error.m_ename, error.m_evalue, error.m_traceback); - } From 90c1275c44b0455c5b1c9f430e65d4360ce30dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 26 Mar 2026 12:47:08 +0100 Subject: [PATCH 16/23] imrpoved sub-process tracking output in tests --- test/test_debugger.cpp | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index ee10902b..9672b0be 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -21,7 +21,6 @@ #include #include - #include "xeus/xsystem.hpp" #include "xeus_client.hpp" @@ -1166,20 +1165,22 @@ struct KernelProcess KernelProcess() { running.emplace_back(m_impl); - std::cout << "=> xpython launched" << std::endl; - - // wait for xpython to be ready before continuing - constexpr std::string_view ready_message = "Run with XEUS"; + + std::cout << "-> xpython sub-process started, waiting for ready message ..." << std::endl; + constexpr std::string_view ready_message = "Run with XEUS"; // we expect this to appear in the error output of xpython once it's ready std::string err_output; boost::asio::read_until(m_impl->err_pipe, boost::asio::dynamic_buffer(err_output), ready_message); - - std::cout << "=> xpython is ready" << std::endl; - + std::cout << "-> xpython ready, proceeding with the test" << std::endl; } ~KernelProcess() { - std::cout << "=> xpython - destructor end" << std::endl; + if (m_impl) + { + std::cout << "-> xpython - stopping ..." << std::endl; + m_impl.reset(); + std::cout << "-> xpython - done" << std::endl; + } } private: @@ -1190,40 +1191,40 @@ struct KernelProcess boost::asio::readable_pipe err_pipe{ ctx }; boost::filesystem::path xpython_path = boost::process::environment::find_executable("xpython"); boost::process::process process{ ctx, xpython_path, { "-f" , KERNEL_JSON }, boost::process::process_stdio{{}, {}, err_pipe} }; - - struct on_destruction { - ~on_destruction() - { - std::cout << "=> xpython - destructor begin" << std::endl; - } - }; }; std::shared_ptr m_impl = std::make_shared(); struct on_program_exit { + // Makes sure sub-processes are all destroyed even if an abort occcurs. + // Will not be invoked on quick_exit however. ~on_program_exit() { - std::cout << "=> test program exiting (after `main()`): explicitly terminate remaining sub-processes ..." << std::endl; + const auto still_running_count = std::count_if(running.begin(), running.end(), [](const auto& maybe_impl) { return !maybe_impl.expired(); }); + if (still_running_count == 0) + { + return; + } + + std::cerr << "=> test program exiting (after `main()`): explicitly terminate remaining sub-processes (" << still_running_count << ") ..." << std::endl; for (auto& maybe_process : running) { if (auto process_impl = maybe_process.lock()) { - std::cout << "=> request exit politely : " << process_impl->xpython_path << "(" << process_impl->process.id() << ") ..." << std::endl; + std::cerr << "=> request exit politely : " << process_impl->xpython_path << "(" << process_impl->process.id() << ") ..." << std::endl; process_impl->process.request_exit(); - //process_impl->process.wait(); std::this_thread::sleep_for(std::chrono::seconds(2)); if (process_impl->process.running()) { - std::cout << "=> still running, force terminate : " << process_impl->xpython_path << "(" << process_impl->process.id() << ") ..." << std::endl; + std::cerr << "=> still running, force terminate : " << process_impl->xpython_path << "(" << process_impl->process.id() << ") ..." << std::endl; process_impl->process.terminate(); process_impl->process.wait(); } - std::cout << "=> terminate " << process_impl->xpython_path << "(" << process_impl->process.id() << ") - DONE" << std::endl; + std::cerr << "=> terminate " << process_impl->xpython_path << "(" << process_impl->process.id() << ") - DONE" << std::endl; } } - std::cout << "=> test program exiting : explicitly terminate remaining sub-processes - DONE" << std::endl; + std::cerr << "=> test program exiting : explicitly terminate remaining sub-processes - DONE" << std::endl; } }; static std::vector> running; From c0a705dd8b3757a2655254358b44ce52a6d0c946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:27:06 +0100 Subject: [PATCH 17/23] removed commented code --- src/xdebugger.cpp | 1 - test/test_debugger.cpp | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 7624c50f..0ce29a8d 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -258,7 +258,6 @@ namespace xpyt if (std::getenv("XEUS_LOG") != nullptr) { std::ofstream out("xeus.log", std::ios_base::app); - //auto& out = std::cout; out << "===== DEBUGGER CONFIG =====" << std::endl; out << m_debugger_config.dump() << std::endl; } diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index 9672b0be..d5a3e873 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -1120,7 +1120,6 @@ void timer::run_timer() { constexpr auto message = "Unit test time out !!"; std::clog << message << std::endl; - //std::terminate(); throw timeout{ message }; // same as calling terminate if unhandled, but some impl will also display the error in the console } } @@ -1165,7 +1164,7 @@ struct KernelProcess KernelProcess() { running.emplace_back(m_impl); - + std::cout << "-> xpython sub-process started, waiting for ready message ..." << std::endl; constexpr std::string_view ready_message = "Run with XEUS"; // we expect this to appear in the error output of xpython once it's ready std::string err_output; From d0b02404ff9a98dcab4ff2117783a228b9736469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:30:11 +0100 Subject: [PATCH 18/23] run 20 times on windows --- .github/workflows/main.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e8776221..d70f9c51 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -139,8 +139,9 @@ jobs: - name: Test xeus-python C++ shell: cmd /C call {0} run: | - micromamba activate xeus-python - test_xeus_python - timeout-minutes: 4 + for /L %%i in (1,1,20) do ( + test_xeus_python + ) + timeout-minutes: 40 working-directory: build\test From 39cff4ac3fdc96128c33fb195dba2743cc655176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:35:30 +0100 Subject: [PATCH 19/23] run tests 20 times on unix --- .github/workflows/main.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d70f9c51..9b23af05 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -69,8 +69,12 @@ jobs: run: xpython --version - name: Test xeus-python C++ - run: ./test_xeus_python - timeout-minutes: 4 + run: | + for i in $(seq 1 20); + do + ./test_xeus_python + done + timeout-minutes: 40 working-directory: build/test - name: Test xeus-python Python From c4f81b61ac7f60c7b87ba5a434f57e14b7f5484f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:43:19 +0100 Subject: [PATCH 20/23] show test run number in CI logs --- .github/workflows/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b23af05..2d11c990 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -70,8 +70,9 @@ jobs: - name: Test xeus-python C++ run: | - for i in $(seq 1 20); + for i in $(seq 1 10); do + echo "tests run $i" ./test_xeus_python done timeout-minutes: 40 @@ -144,6 +145,7 @@ jobs: shell: cmd /C call {0} run: | for /L %%i in (1,1,20) do ( + echo tests run %%i test_xeus_python ) timeout-minutes: 40 From fe16b17bebc4883b49f800888d0580ec258a53e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:43:53 +0100 Subject: [PATCH 21/23] removed changes to python path deduction, improved wrong path detection and report instead --- src/main.cpp | 13 +++++++++++-- src/xpaths.cpp | 17 +---------------- test/test_debugger.cpp | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9a907fbe..95af2803 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,12 +77,21 @@ int main(int argc, char* argv[]) PyConfig_InitPythonConfig(&config); // config.isolated = 1; + constexpr std::string_view python_path_help = "PYTHONHOME or PYTHON_EXECUTABLE environment variables can be used to specify the correct path"; + const auto fail_with_error_message = [](const auto&... message_parts) { + std::stringstream message_stream; + ((message_stream << message_parts), ...); + auto message = message_stream.str(); + std::cerr << message << std::endl; + throw std::runtime_error(std::move(message)); + }; + // Setting Program Name static const std::string executable(xpyt::get_python_path()); static const std::wstring wexecutable(executable.cbegin(), executable.cend()); if (!std::filesystem::exists(wexecutable)) { - throw std::runtime_error(std::string("cannot find python executable, tried ") + executable); + fail_with_error_message("cannot find python executable, tried '", executable, "' - ", python_path_help); } config.program_name = const_cast(wexecutable.c_str()); @@ -91,7 +100,7 @@ int main(int argc, char* argv[]) static const std::wstring wpythonhome(pythonhome.cbegin(), pythonhome.cend()); if (!std::filesystem::exists(wpythonhome)) { - throw std::runtime_error(std::string("cannot find python home directory, tried ") + pythonhome); + fail_with_error_message("cannot find python home directory, tried '", pythonhome, "' - ", python_path_help); } config.home = const_cast(wpythonhome.c_str()); xpyt::print_pythonhome(); diff --git a/src/xpaths.cpp b/src/xpaths.cpp index 79f1db34..3fbd84a2 100644 --- a/src/xpaths.cpp +++ b/src/xpaths.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include "pybind11/pybind11.h" @@ -45,21 +44,7 @@ namespace xpyt #elif defined(XEUS_PYTHONHOME_ABSPATH) static const std::string pythonhome = XPYT_STRINGIFY(XEUS_PYTHONHOME_ABSPATH); #else - using namespace std::filesystem; -# ifdef _WIN32 - // python is located in the root dir of the env on Windows, not the prefix path which is in env-root/Library/ - static const std::string pythonhome = [] { - // makes sure std::filesystem knows the string we pass is indeed UTF-8 (by convention) - // and not some other encoding (as often expected on Windows) - const path prefix_path_u8(xeus::prefix_path(), std::locale("en_US.UTF-8")); - // we need the parent path of the prefix path - const auto python_dir_u8 = canonical(prefix_path_u8).parent_path().u8string(); - // TOOD: in C++20, `python_dir_u8` will have changed type to `std::u8string`, a conversion would be needed here. - return python_dir_u8; - }(); -# else - static const std::string pythonhome = canonical(xeus::prefix_path()).u8string(); -# endif + static const std::string pythonhome = xeus::prefix_path(); #endif return pythonhome; } diff --git a/test/test_debugger.cpp b/test/test_debugger.cpp index d5a3e873..888a1b5b 100644 --- a/test/test_debugger.cpp +++ b/test/test_debugger.cpp @@ -1160,15 +1160,31 @@ void dump_connection_file() struct KernelProcess { + struct error : std::runtime_error { + using std::runtime_error::runtime_error; + }; KernelProcess() { running.emplace_back(m_impl); std::cout << "-> xpython sub-process started, waiting for ready message ..." << std::endl; + constexpr std::string_view ready_message = "Run with XEUS"; // we expect this to appear in the error output of xpython once it's ready std::string err_output; - boost::asio::read_until(m_impl->err_pipe, boost::asio::dynamic_buffer(err_output), ready_message); + try + { + boost::asio::read_until(m_impl->err_pipe, boost::asio::dynamic_buffer(err_output), ready_message); + } + catch (const std::exception& ex) + { + std::stringstream msgbuilder; + msgbuilder << "xpython startup failed: " << ex.what(); + msgbuilder << "\nxpython's standard error output : " << err_output; + auto message = msgbuilder.str(); + std::cerr << message << std::endl; + throw error(message); + } std::cout << "-> xpython ready, proceeding with the test" << std::endl; } From 25fc85efe7ee34f1f2f4a1a50a368bbc79374a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:05:56 +0100 Subject: [PATCH 22/23] debugger reference decreased instead of object destroyed --- src/xdebugger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 0ce29a8d..bc5f987f 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -74,7 +74,7 @@ namespace xpyt { // release/destroy the debugger python object while GIL is acquired pybind11::gil_scoped_acquire gil_lock; - auto local_debug = std::move(m_pydebugger); + m_pydebugger.dec_ref(); } nl::json debugger::inspect_variables_request(const nl::json& message) From a6191ec8f3dd283d126b6099fa4a30c0e32621ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaim=20=28Jo=C3=ABl=20Lamotte=29?= <142265+Klaim@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:12:04 +0100 Subject: [PATCH 23/23] removed some logs --- src/xinterpreter.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 82c170ae..4ec49c5d 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -337,7 +337,6 @@ namespace xpyt nl::json interpreter::internal_request_impl(const nl::json& content) { - std::cerr << "Received internal request with content: " << content.dump(4) << std::endl; py::gil_scoped_acquire acquire; std::string code = content.value("code", ""); @@ -351,8 +350,6 @@ namespace xpyt catch (py::error_already_set& e) { try{ - std::cerr << "an error occurred during code execution: " << e.what() <()); } catch (...) { - std::cerr << "an unknown error occurred during code execution"<()); } }