diff --git a/.github/workflows/deploy-github-page.yml b/.github/workflows/deploy-github-page.yml index 4f60b9f0..43737206 100644 --- a/.github/workflows/deploy-github-page.yml +++ b/.github/workflows/deploy-github-page.yml @@ -33,7 +33,6 @@ jobs: environment-file: environment-wasm-build.yml init-shell: bash environment-name: xeus-python-wasm-build - - name: Set ncpus run: echo "ncpus=$(nproc --all)" >> $GITHUB_ENV @@ -47,6 +46,11 @@ jobs: set -eux + + URL=https://github.com/DerThorsten/xeus-python-shell/archive/refs/heads/libuv.zip + curl -L $URL -o xeus-python-shell-libuv.zip + unzip xeus-python-shell-libuv.zip + export PREFIX=$MAMBA_ROOT_PREFIX/envs/xeus-python-wasm-host echo "PREFIX=$PREFIX" >> $GITHUB_ENV @@ -63,10 +67,13 @@ jobs: - name: Jupyter Lite integration shell: bash -l {0} run: | + THIS_DIR=$(pwd) jupyter lite build \ --XeusAddon.prefix=${{ env.PREFIX }} \ --contents notebooks/ \ - --output-dir dist + --output-dir dist \ + --XeusAddon.mounts=$THIS_DIR/xeus-python-shell-libuv/xeus_python_shell/:/lib/python3.13/site-packages/xeus_python_shell/ + - name: Upload artifact uses: actions/upload-pages-artifact@v4 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9870f650..24d58632 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04, ubuntu-24.04, macos-13, macos-14] + os: [ubuntu-22.04, ubuntu-24.04, macos-14] build_type: [static_build, shared_build] steps: @@ -32,6 +32,10 @@ jobs: uses: mamba-org/setup-micromamba@v2 with: environment-file: environment-dev.yml + + - name: Install xeus-python-shell from GitHub + run: pip install git+https://github.com/DerThorsten/xeus-python-shell.git@libuv + - name: Make build directory run: mkdir build @@ -47,7 +51,8 @@ jobs: run: | CMAKE_EXTRA_ARGS="$CMAKE_EXTRA_ARGS -DXPYT_BUILD_STATIC=OFF" echo "CMAKE_EXTRA_ARGS=$CMAKE_EXTRA_ARGS" >> $GITHUB_ENV - + + - name: Configure cmake run: | cmake .. \ @@ -61,8 +66,12 @@ jobs: ${{ env.CMAKE_EXTRA_ARGS }} working-directory: build + - name: Build all targets + run: cmake --build . --verbose -j ${{ runner.os == 'macOS' && 3 || 4 }} + working-directory: build + - name: Install - run: make -j ${{ runner.os == 'macOS' && 3 || 4 }} install + run: cmake --install . working-directory: build - name: Print version @@ -97,6 +106,10 @@ jobs: - name: Make build directory run: mkdir build + + - name: Install xeus-python-shell from GitHub + shell: cmd /C call {0} + run: pip install git+https://github.com/DerThorsten/xeus-python-shell.git@libuv - name: Static build option if: matrix.build_type == 'static_build' @@ -134,13 +147,12 @@ jobs: - name: Test xeus-python Python shell: cmd /C call {0} - run: pytest . -vvv + run: pytest . -s -vvv + - name: Test xeus-python C++ shell: cmd /C call {0} run: | - micromamba activate xeus-python test_xeus_python timeout-minutes: 4 - working-directory: build\test - + working-directory: build\test \ No newline at end of file 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 92429df1..0be0d458 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ set(pybind11_REQUIRED_VERSION 2.6.1) set(pybind11_json_REQUIRED_VERSION 0.2.8) set(pyjs_REQUIRED_VERSION 2.0.0) -find_package(Python COMPONENTS Interpreter REQUIRED) +# find_package(Python COMPONENTS Interpreter REQUIRED) if (NOT TARGET pybind11::headers) # Defaults to ON for cmake >= 3.18 @@ -105,8 +105,11 @@ else () if (NOT TARGET xeus-zmq AND NOT TARGET xeus-zmq-static) find_package(xeus-zmq ${xeus-zmq_REQUIRED_VERSION} REQUIRED) endif () + endif() + + # Configuration # ============= @@ -178,6 +181,10 @@ set(XEUS_PYTHON_SRC src/xstream.hpp src/xtraceback.cpp src/xutils.cpp + src/xasync_runner.cpp + src/xaserver.cpp + src/xacontrol_default_runner.cpp + ) set(XEUS_PYTHON_HEADERS @@ -188,6 +195,8 @@ set(XEUS_PYTHON_HEADERS include/xeus-python/xinterpreter_raw.hpp include/xeus-python/xtraceback.hpp include/xeus-python/xutils.hpp + include/xeus-python/xaserver.hpp + include/xeus-python/xacontrol_default_runner.hpp ) set(XPYTHON_SRC @@ -317,7 +326,10 @@ macro(xpyt_create_target target_name src headers linkage output_name) set(XPYT_XEUS_TARGET xeus-zmq-static) endif () - target_link_libraries(${target_name} PUBLIC ${XPYT_XEUS_TARGET} PRIVATE pybind11::pybind11 pybind11_json) + target_link_libraries(${target_name} + PUBLIC ${XPYT_XEUS_TARGET} ${UVW_LIBRARY} + PRIVATE pybind11::pybind11 pybind11_json + ) if (WIN32 OR CYGWIN) target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) elseif (APPLE) @@ -371,11 +383,18 @@ endif () if (XPYT_BUILD_XPYTHON_EXECUTABLE) add_executable(xpython ${XPYTHON_SRC}) - target_link_libraries(xpython PRIVATE pybind11::embed) + target_link_libraries(xpython PRIVATE pybind11::embed ) xpyt_set_common_options(xpython) xpyt_set_kernel_options(xpython) xpyt_target_link_libraries(xpython) + + get_target_property(_libs xpython LINK_LIBRARIES) + message(STATUS "xpython LINK_LIBRARIES = ${_libs}") + + message(STATUS "xeus-python file = $") + + endif() # xpython_extension diff --git a/environment-dev.yml b/environment-dev.yml index be0e6932..1c0df0c6 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -15,7 +15,8 @@ dependencies: # The debugger is not available with python 3.13 because # of a spurious bug in debugpy - python <3.13 - - xeus-python-shell>=0.6.3,<0.7 + - pip + #- xeus-python-shell>=0.6.3,<0.7 # we need an unreleased version - debugpy>=1.6.5 - ipython # Test dependencies @@ -25,3 +26,7 @@ dependencies: - jupyter_kernel_test<0.8 - doctest - pluggy=1.3 + - libboost + - libboost-devel + - libboost-headers + diff --git a/environment-wasm-build.yml b/environment-wasm-build.yml index 617bf918..59cec0e7 100644 --- a/environment-wasm-build.yml +++ b/environment-wasm-build.yml @@ -13,3 +13,5 @@ dependencies: - jupyterlite-core >0.6 - jupyter_server - jupyterlite-xeus + - # widgets + - ipywidgets \ No newline at end of file diff --git a/environment-wasm-host.yml b/environment-wasm-host.yml index 72e89111..9004a4a5 100644 --- a/environment-wasm-host.yml +++ b/environment-wasm-host.yml @@ -12,9 +12,11 @@ dependencies: - numpy - xeus-lite - xeus - - xeus-python-shell>=0.6.3 - pyjs >=2,<3 - libpython - zstd - openssl - xz + - ipywidgets + # - xeus-python-shell>=0.6.3 + diff --git a/include/xeus-python/xacontrol_default_runner.hpp b/include/xeus-python/xacontrol_default_runner.hpp new file mode 100644 index 00000000..faafca23 --- /dev/null +++ b/include/xeus-python/xacontrol_default_runner.hpp @@ -0,0 +1,35 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay, Martin Renou * +* Copyright (c) 2016, QuantStack * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XEUS_PYTHON_ACONTROL_DEFAULT_RUNNER_HPP +#define XEUS_PYTHON_ACONTROL_DEFAULT_RUNNER_HPP + +#include "xeus_python_config.hpp" +#include "xeus-zmq/xcontrol_runner.hpp" + +namespace xpyt +{ + class XEUS_PYTHON_API xacontrol_default_runner final : public xeus::xcontrol_runner + { + public: + + xacontrol_default_runner() = default; + ~xacontrol_default_runner() override = default; + + private: + + void run_impl() override; + void stop_impl() override; + + bool m_request_stop; + }; +} + +#endif + diff --git a/include/xeus-python/xaserver.hpp b/include/xeus-python/xaserver.hpp new file mode 100644 index 00000000..0382b39c --- /dev/null +++ b/include/xeus-python/xaserver.hpp @@ -0,0 +1,22 @@ +/*************************************************************************** +* Copyright (c) 2024, Isabel Paredes * +* Copyright (c) 2024, QuantStack * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#include "xeus_python_config.hpp" +#include "xeus/xkernel.hpp" +#include "pybind11/pybind11.h" + +namespace py = pybind11; +namespace nl = nlohmann; + +namespace xpyt +{ + + XEUS_PYTHON_API XPYT_FORCE_PYBIND11_EXPORT xeus::xkernel::server_builder make_xaserver_factory(py::dict globals); + +} // namespace xeus diff --git a/include/xeus-python/xdebugger.hpp b/include/xeus-python/xdebugger.hpp index a7268818..055c1358 100644 --- a/include/xeus-python/xdebugger.hpp +++ b/include/xeus-python/xdebugger.hpp @@ -40,7 +40,9 @@ namespace xpyt using base_type = xeus::xdebugger_base; - debugger(xeus::xcontext& context, + debugger( + py::dict globals, + xeus::xcontext& context, const xeus::xconfiguration& config, const std::string& user_name, const std::string& session_id, @@ -65,6 +67,9 @@ namespace xpyt xeus::xdebugger_info get_debugger_info() const override; std::string get_cell_temporary_file(const std::string& code) const override; + + py::dict m_global_dict; + std::unique_ptr p_debugpy_client; std::string m_debugpy_host; std::string m_debugpy_port; @@ -74,8 +79,9 @@ namespace xpyt bool m_copy_to_globals_available; }; - XEUS_PYTHON_API - std::unique_ptr make_python_debugger(xeus::xcontext& context, + XEUS_PYTHON_API XPYT_FORCE_PYBIND11_EXPORT + std::unique_ptr make_python_debugger(py::dict globals, + xeus::xcontext& context, const xeus::xconfiguration& config, const std::string& user_name, const std::string& session_id, diff --git a/include/xeus-python/xinterpreter.hpp b/include/xeus-python/xinterpreter.hpp index 86727408..c8e2a9d6 100644 --- a/include/xeus-python/xinterpreter.hpp +++ b/include/xeus-python/xinterpreter.hpp @@ -32,7 +32,7 @@ namespace nl = nlohmann; namespace xpyt { - class XEUS_PYTHON_API interpreter : public xeus::xinterpreter + class XEUS_PYTHON_API XPYT_FORCE_PYBIND11_EXPORT interpreter : public xeus::xinterpreter { public: @@ -44,7 +44,9 @@ namespace xpyt // If redirect_display_enabled is true (default) then this interpreter will // overwrite sys.displayhook and send execution results using publish_execution_result. // Disable this if your interpreter uses custom display hook. - interpreter(bool redirect_output_enabled=true, bool redirect_display_enabled = true); + interpreter( + py::dict globals, + bool redirect_output_enabled=true, bool redirect_display_enabled = true); virtual ~interpreter(); protected: @@ -76,6 +78,7 @@ namespace xpyt void redirect_output(); + py::dict m_global_dict; py::object m_ipython_shell_app; py::object m_ipython_shell; py::object m_displayhook; @@ -92,14 +95,13 @@ namespace xpyt // If an application has already released the GIL by the time the interpreter // is started, m_release_gil_at_startup has to be set to false to prevent // releasing it again in configure_impl(). - // - bool m_release_gil_at_startup = true; + bool m_release_gil_at_startup = false; gil_scoped_release_ptr m_release_gil = nullptr; bool m_redirect_output_enabled; bool m_redirect_display_enabled; - private: + private: virtual void instanciate_ipython_shell(); virtual bool use_jedi_for_completion() const; diff --git a/include/xeus-python/xinterpreter_raw.hpp b/include/xeus-python/xinterpreter_raw.hpp index 3305e072..38cc9840 100644 --- a/include/xeus-python/xinterpreter_raw.hpp +++ b/include/xeus-python/xinterpreter_raw.hpp @@ -30,7 +30,7 @@ namespace nl = nlohmann; namespace xpyt { - class XEUS_PYTHON_API raw_interpreter : public xeus::xinterpreter + class XEUS_PYTHON_API XPYT_FORCE_PYBIND11_EXPORT raw_interpreter : public xeus::xinterpreter { public: @@ -42,7 +42,9 @@ namespace xpyt // If redirect_display_enabled is true (default) then this interpreter will // overwrite sys.displayhook and send execution results using publish_execution_result. // Disable this if your interpreter uses custom display hook. - raw_interpreter(bool redirect_output_enabled=true, bool redirect_display_enabled = true); + raw_interpreter( + py::dict globals, + bool redirect_output_enabled=true, bool redirect_display_enabled = true); virtual ~raw_interpreter(); protected: @@ -88,6 +90,10 @@ namespace xpyt bool m_release_gil_at_startup = true; gil_scoped_release_ptr m_release_gil = nullptr; bool m_redirect_display_enabled; + + std::string _i,_ii,_iii; + + py::dict m_global_dict; }; } diff --git a/notebooks/xeus-python.ipynb b/notebooks/xeus-python.ipynb index 12a0b75b..026d828c 100644 --- a/notebooks/xeus-python.ipynb +++ b/notebooks/xeus-python.ipynb @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -28,18 +28,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "a" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "7921" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "b = 89\n", "\n", @@ -51,9 +73,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "print" ] @@ -653,15 +686,18 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.9 (XPython)", + "display_name": "Python 3.13 (XPython)", "language": "python", "name": "xpython" }, "language_info": { + "codemirror_mode": "{\"name\": \"ipython\", \"version\": 3}", "file_extension": ".py", "mimetype": "text/x-python", "name": "python", - "version": "3.9.1" + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.11" } }, "nbformat": 4, diff --git a/src/main.cpp b/src/main.cpp index 8862a319..55615502 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,14 +21,17 @@ #include #endif + #include "xeus/xkernel.hpp" #include "xeus/xkernel_configuration.hpp" #include "xeus/xinterpreter.hpp" #include "xeus/xhelper.hpp" -#include "xeus-zmq/xserver_zmq_split.hpp" +#include "xeus-zmq/xserver_zmq.hpp" #include "xeus-zmq/xzmq_context.hpp" + + #include "pybind11/embed.h" #include "pybind11/pybind11.h" @@ -39,8 +42,10 @@ #include "xeus-python/xeus_python_config.hpp" #include "xeus-python/xutils.hpp" -namespace py = pybind11; +#include "xeus-python/xaserver.hpp" + +namespace py = pybind11; int main(int argc, char* argv[]) { @@ -88,11 +93,11 @@ int main(int argc, char* argv[]) config.home = const_cast(wstr.c_str()); xpyt::print_pythonhome(); - // Implicitly pre-initialize Python - status = PyConfig_SetBytesArgv(&config, argc, argv); - if (PyStatus_Exception(status)) { - std::cerr << "Error:" << status.err_msg << std::endl; - } + // Instantiating the Python interpreter + py::scoped_interpreter guard{}; + py::gil_scoped_acquire acquire; + + // Setting argv wchar_t** argw = new wchar_t*[size_t(argc)]; @@ -113,22 +118,24 @@ int main(int argc, char* argv[]) } delete[] argw; - // Instantiating the Python interpreter - py::scoped_interpreter guard; + std::unique_ptr context = xeus::make_zmq_context(); + // we want to use **the same global dict everywhere** + py::dict globals = py::globals(); + // Instantiating the xeus xinterpreter bool raw_mode = xpyt::extract_option("-r", "--raw", argc, argv); using interpreter_ptr = std::unique_ptr; interpreter_ptr interpreter; if (raw_mode) { - interpreter = interpreter_ptr(new xpyt::raw_interpreter()); + interpreter = interpreter_ptr(new xpyt::raw_interpreter(globals)); } else { - interpreter = interpreter_ptr(new xpyt::interpreter()); + interpreter = interpreter_ptr(new xpyt::interpreter(globals)); } using history_manager_ptr = std::unique_ptr; @@ -146,6 +153,24 @@ int main(int argc, char* argv[]) nl::json debugger_config; debugger_config["python"] = executable; + + // Factory to create the debugger with the global dict + auto make_the_debugger = [globals]( + xeus::xcontext& context, + const xeus::xconfiguration& config, + const std::string& user_name, + const std::string& session_id, + const nl::json& debugger_config) -> std::unique_ptr + { + return xpyt::make_python_debugger( + globals, + context, + config, + user_name, + session_id, + debugger_config); + }; + if (!connection_filename.empty()) { xeus::xconfiguration config = xeus::load_configuration(connection_filename); @@ -154,11 +179,11 @@ int main(int argc, char* argv[]) xeus::get_user_name(), std::move(context), std::move(interpreter), - xeus::make_xserver_shell_main, + xpyt::make_xaserver_factory(globals), std::move(hist), xeus::make_console_logger(xeus::xlogger::msg_type, xeus::make_file_logger(xeus::xlogger::content, "xeus.log")), - xpyt::make_python_debugger, + make_the_debugger, debugger_config); std::clog << @@ -166,8 +191,10 @@ int main(int argc, char* argv[]) "If you want to connect to this kernel from an other client, you can use" " the " + connection_filename + " file." << std::endl; - + + std::cout << "Starting kernel..." << std::endl; kernel.start(); + std::cout << "Kernel stopped." << std::endl; } else { @@ -175,10 +202,10 @@ int main(int argc, char* argv[]) xeus::xkernel kernel(xeus::get_user_name(), std::move(context), std::move(interpreter), - xeus::make_xserver_shell_main, + xpyt::make_xaserver_factory(globals), std::move(hist), nullptr, - xpyt::make_python_debugger, + make_the_debugger, debugger_config); std::cout << "Getting config" << std::endl; @@ -201,7 +228,9 @@ int main(int argc, char* argv[]) "}\n```" << std::endl; + std::cout << "Starting kernel..." << std::endl; kernel.start(); + std::cout << "Kernel stopped." << std::endl; } return 0; diff --git a/src/xacontrol_default_runner.cpp b/src/xacontrol_default_runner.cpp new file mode 100644 index 00000000..d1f7c169 --- /dev/null +++ b/src/xacontrol_default_runner.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay, Martin Renou * +* Copyright (c) 2016, QuantStack * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#include "xeus-python/xacontrol_default_runner.hpp" + +#include + +namespace xpyt +{ + void xacontrol_default_runner::run_impl() + { + std::clog << "Starting control default runner..." << std::endl; + m_request_stop = false; + + while (!m_request_stop) + { + auto msg = read_control(); + if (msg.has_value()) + { + std::cout << "Control default runner received a message." << msg.value().header().dump(4)<< std::endl; + notify_control_listener(std::move(msg.value())); + std::cout << "Notified control listener." << std::endl; + } + } + std::clog << "Exiting control default runner loop." << std::endl; + + stop_channels(); + } + + void xacontrol_default_runner::stop_impl() + { + std::clog << "Stopping control default runner..." << std::endl; + m_request_stop = true; + } +} + diff --git a/src/xaserver.cpp b/src/xaserver.cpp new file mode 100644 index 00000000..d1007e31 --- /dev/null +++ b/src/xaserver.cpp @@ -0,0 +1,41 @@ + +#include "xeus-python/xaserver.hpp" +#include "xasync_runner.hpp" +#include "xeus-python/xacontrol_default_runner.hpp" +#include "xeus-zmq/xserver_zmq_split.hpp" + +#include "xeus/xkernel.hpp" +#include "xeus/xserver.hpp" +#include "xeus/xeus_context.hpp" +#include "xeus/xkernel_configuration.hpp" + +#include "nlohmann/json.hpp" + + +namespace nl = nlohmann; + +namespace xpyt +{ + + + + xeus::xkernel::server_builder make_xaserver_factory(py::dict globals) + { + return [globals]( + xeus::xcontext& context, + const xeus::xconfiguration& config, + nl::json::error_handler_t eh) + { + return xeus::make_xserver_shell + ( + context, + config, + eh, + std::make_unique(), + std::make_unique(globals) + ); + }; + } + + +} // namespace xeus diff --git a/src/xasync_runner.cpp b/src/xasync_runner.cpp new file mode 100644 index 00000000..887313a5 --- /dev/null +++ b/src/xasync_runner.cpp @@ -0,0 +1,205 @@ +/*************************************************************************** +* Copyright (c) 2024, Isabel Paredes * +* Copyright (c) 2024, QuantStack * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#include +#include + +#include "xasync_runner.hpp" +#include "pybind11/embed.h" +#include "pybind11/pybind11.h" + +namespace py = pybind11; + +namespace xpyt +{ + + + xasync_runner::xasync_runner(py::dict globals) + : xeus::xshell_runner(), + m_global_dict{globals}, + m_use_busy_loop{true} + { + std::cout<< "xasync_runner created" << std::endl; + } + + void xasync_runner::run_impl() + { + std::cout << "get descriptors "<< std::endl; + int fd_shell_int = static_cast(this->get_shell_fd()); + int fd_controller_int = static_cast(this-> get_shell_controller_fd()); + + + std::cout << "Got descriptors: " << fd_shell_int << ", " << fd_controller_int << std::endl; + + // wrap this->on_message_doorbell_shell and this->on_message_doorbell_controller + // into a py::cpp_function + py::cpp_function shell_callback = py::cpp_function([this]() { + this->on_message_doorbell_shell(); + }); + py::cpp_function controller_callback = py::cpp_function([this]() { + this->on_message_doorbell_controller(); + }); + + + // ensure gil + py::gil_scoped_acquire acquire; + + + // the usual print function is not working for debugging since + // its replaced by the kernel's stdout handling, so we define + // a custom function that writes to sys.stdout directly + auto raw_print = py::cpp_function([]( + std::string msg + ) { + std::cout << "Received message in Python: " << msg << std::endl; + }); + + exec(R"( + import sys + import asyncio + import traceback + + is_win = sys.platform.startswith("win") or sys.platform.startswith("cygwin") or sys.platform.startswith("msys") + + if is_win: + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + + class ZMQSockReader: + def __init__(self, fd): + self._fd = fd + + def fileno(self): + # This allows asyncio's select to see the handle + return self._fd + + def make_fd(fd): + if is_win: + return ZMQSockReader(fd) + else: + return fd + + + async def busy_loop(fd, callback, name, raw_print): + try: + raw_print(f"Starting busy loop {name} ") + while True: + await asyncio.sleep(0) + callback() + except Exception as e: + raw_print(f"Exception in busy_loop {name} : {e}") + + def run_main_busy_loop(fd_shell, fd_controller, shell_callback, controller_callback, raw_print): + raw_print("Creating event loop busy loop") + loop = asyncio.get_event_loop() + + task_shell = loop.create_task(busy_loop(fd_shell, shell_callback, "shell", raw_print)) + task_controller = loop.create_task(busy_loop(fd_controller, controller_callback, "controller", raw_print)) + + raw_print("Running event loop forever") + loop.run_forever() + + + def run_main_non_busy_loop(fd_shell, fd_controller, shell_callback, controller_callback, raw_print): + raw_print("Creating event loop non-busy loop") + # here we create / ensure we have an event loop + loop = asyncio.get_event_loop() + raw_print("Adding readers to event loop") + loop.add_reader(fd_shell, shell_callback) + loop.add_reader(fd_controller, controller_callback) + raw_print("Running event loop forever") + loop.run_forever() + + def run_main(fd_shell, fd_controller, shell_callback, controller_callback, use_busy_loop, raw_print): + try: + fd_controller = make_fd(fd_controller) + fd_shell = make_fd(fd_shell) + + main = run_main_busy_loop if use_busy_loop else run_main_non_busy_loop + main(fd_shell, fd_controller, shell_callback, controller_callback, raw_print) + + except Exception as e: + # get full traceback + + traceback_str = traceback.format_exc() + raw_print(f"Exception in run_main: {traceback_str}") + + )", m_global_dict); + + py::object run_func = m_global_dict["run_main"]; + std::cout << "Starting async loop "<< std::endl; + run_func(fd_shell_int, fd_controller_int, shell_callback, controller_callback, m_use_busy_loop, raw_print); + + + } + + void xasync_runner::on_message_doorbell_shell() + { + int ZMQ_DONTWAIT{ 1 }; // from zmq.h + while (auto msg = read_shell(ZMQ_DONTWAIT)) + { + std::cout << "got message on shell "<(this->get_shell_fd()); + int fd_controller_int = static_cast(this-> get_shell_controller_fd()); + + + py::gil_scoped_acquire acquire; + + // Or create via exec if you need a more complex function: + py::exec(R"( + import asyncio + import sys + + def stop_loop(fd_shell, fd_controller, use_busy_loop): + + + # here we create / ensure we have an event loop + loop = asyncio.get_event_loop() + if not use_busy_loop: + loop.remove_reader(make_fd(fd_shell)) + loop.remove_reader(make_fd(fd_controller)) + loop.stop() + + )", m_global_dict); + + py::object stop_func = m_global_dict["stop_loop"]; + stop_func(fd_shell_int, fd_controller_int, m_use_busy_loop); + break; + + } + else + { + std::string rep = notify_internal_listener(std::move(val)); + send_controller(std::move(rep)); + } + } + } + + + +} // namespace xpyt diff --git a/src/xasync_runner.hpp b/src/xasync_runner.hpp new file mode 100644 index 00000000..f76ebd54 --- /dev/null +++ b/src/xasync_runner.hpp @@ -0,0 +1,57 @@ +/*************************************************************************** +* Copyright (c) 2024, Isabel Paredes * +* Copyright (c) 2024, QuantStack * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XEUS_PYTHON_ASYNC_RUNNER_HPP +#define XEUS_PYTHON_ASYNC_RUNNER_HPP + +#include + +#include "xeus-python/xeus_python_config.hpp" +#include "xeus-zmq/xshell_runner.hpp" +#include "pybind11/pybind11.h" + + +namespace py = pybind11; + +namespace xpyt +{ + + class xasync_runner final : public xeus::xshell_runner + { + public: + + xasync_runner(py::dict globals); + + xasync_runner(const xasync_runner&) = delete; + xasync_runner& operator=(const xasync_runner&) = delete; + xasync_runner(xasync_runner&&) = delete; + xasync_runner& operator=(xasync_runner&&) = delete; + + ~xasync_runner() override = default; + + private: + void on_message_doorbell_shell(); + void on_message_doorbell_controller(); + + void run_impl() override; + + py::dict m_global_dict; + + bool m_use_busy_loop; + + }; + + + + + + +} // namespace xeus + +#endif // XEUS_PYTHON_ASYNC_RUNNER_HPP diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 700e4702..e2cb5f1f 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -24,8 +24,10 @@ #include "pybind11/pybind11.h" #include "pybind11/stl.h" +#include "pybind11/embed.h" #include "xeus/xinterpreter.hpp" +#include "xeus/xeus_context.hpp" #include "xeus/xsystem.hpp" #include "xeus-zmq/xmiddleware.hpp" @@ -42,12 +44,14 @@ using namespace std::placeholders; namespace xpyt { - debugger::debugger(xeus::xcontext& context, + debugger::debugger(py::dict globals, + xeus::xcontext& context, const xeus::xconfiguration& config, const std::string& user_name, const std::string& session_id, const nl::json& debugger_config) : xdebugger_base(context) + , m_global_dict{globals} , p_debugpy_client(new xdebugpy_client(context, config, xeus::get_socket_linger(), @@ -145,7 +149,7 @@ namespace xpyt } py::gil_scoped_acquire acquire; - py::object variables = py::globals(); + py::object variables = m_global_dict; py::object repr_data = variables[py::str(var_repr_data)]; py::object repr_metadata = variables[py::str(var_repr_metadata)]; nl::json body = { @@ -267,7 +271,9 @@ namespace xpyt code += "debugpy.listen((\'" + m_debugpy_host + "\'," + m_debugpy_port + "))"; nl::json json_code; json_code["code"] = code; + std::cout<<"sending code to import and start debugpy: "<(); if(status != "ok") { @@ -289,7 +295,7 @@ namespace xpyt // Get debugpy version std::string expression = "debugpy.__version__"; - std::string version = (eval(py::str(expression))).cast(); + std::string version = eval(py::str(expression), m_global_dict).cast(); // Format the version to match [0-9]+(\s[0-9]+)* size_t pos = version.find_first_of("abrc"); @@ -362,13 +368,15 @@ namespace xpyt return get_cell_tmp_file(code); } - std::unique_ptr make_python_debugger(xeus::xcontext& context, + std::unique_ptr make_python_debugger( + py::dict globals, + xeus::xcontext& context, const xeus::xconfiguration& config, const std::string& user_name, const std::string& session_id, const nl::json& debugger_config) { - return std::unique_ptr(new debugger(context, + return std::unique_ptr(new debugger(globals,context, config, user_name, session_id, debugger_config)); } diff --git a/src/xdebugpy_client.cpp b/src/xdebugpy_client.cpp index f6825530..6924de19 100644 --- a/src/xdebugpy_client.cpp +++ b/src/xdebugpy_client.cpp @@ -10,6 +10,7 @@ #include "nlohmann/json.hpp" #include "xeus/xmessage.hpp" +#include "xeus/xeus_context.hpp" #include "xdebugpy_client.hpp" #include diff --git a/src/xdebugpy_client.hpp b/src/xdebugpy_client.hpp index 0f83e5e0..aec9e4e5 100644 --- a/src/xdebugpy_client.hpp +++ b/src/xdebugpy_client.hpp @@ -12,6 +12,7 @@ #define XPYT_DEBUGPY_CLIENT_HPP #include "xeus-zmq/xdap_tcp_client.hpp" +#include "xeus/xeus_context.hpp" namespace xpyt { diff --git a/src/xinspect.cpp b/src/xinspect.cpp index c820ea9b..1d94635f 100644 --- a/src/xinspect.cpp +++ b/src/xinspect.cpp @@ -22,30 +22,30 @@ using namespace pybind11::literals; namespace xpyt { - py::object static_inspect(const std::string& code) + py::object static_inspect(const std::string& code, py::dict globals) { py::module jedi = py::module::import("jedi"); - return jedi.attr("Interpreter")(code, py::make_tuple(py::globals())); + return jedi.attr("Interpreter")(code, py::make_tuple(globals)); } - py::object static_inspect(const std::string& code, int cursor_pos) + + py::object static_inspect(const std::string& code, int cursor_pos, py::dict globals) { std::string sub_code = code.substr(0, cursor_pos); - return static_inspect(sub_code); + return static_inspect(sub_code, globals); } - py::list get_completions(const std::string& code, int cursor_pos) + + py::list get_completions(const std::string& code, int cursor_pos, py::dict globals) { - return static_inspect(code, cursor_pos).attr("complete")(); + return static_inspect(code, cursor_pos, globals).attr("complete")(); } - py::list get_completions(const std::string& code) + py::list get_completions(const std::string& code, py::dict globals) { - return static_inspect(code).attr("complete")(); + return static_inspect(code, globals).attr("complete")(); } - py::list get_completions(const std::string& code); - std::string formatted_docstring_impl(py::object inter) { py::object definition = py::none(); @@ -121,15 +121,15 @@ namespace xpyt return result; } - std::string formatted_docstring(const std::string& code, int cursor_pos) + std::string formatted_docstring(const std::string& code, int cursor_pos, py::dict globals) { - py::object inter = static_inspect(code, cursor_pos); + py::object inter = static_inspect(code, cursor_pos, globals); return formatted_docstring_impl(inter); } - std::string formatted_docstring(const std::string& code) + std::string formatted_docstring(const std::string& code, py::dict globals) { - py::object inter = static_inspect(code); + py::object inter = static_inspect(code, globals); return formatted_docstring_impl(inter); } } diff --git a/src/xinspect.hpp b/src/xinspect.hpp index edd235da..b3fa6a77 100644 --- a/src/xinspect.hpp +++ b/src/xinspect.hpp @@ -19,11 +19,11 @@ namespace py = pybind11; namespace xpyt { - py::list get_completions(const std::string& code, int cursor_pos); - py::list get_completions(const std::string& code); + py::list get_completions(const std::string& code, int cursor_pos, py::dict globals); + py::list get_completions(const std::string& code, py::dict globals); - std::string formatted_docstring(const std::string& code, int cursor_pos); - std::string formatted_docstring(const std::string& code); + std::string formatted_docstring(const std::string& code, int cursor_pos, py::dict globals); + std::string formatted_docstring(const std::string& code, py::dict globals); } #endif diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index ff210b2d..820810b6 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -22,6 +22,7 @@ #include "xeus/xhelper.hpp" #include "pybind11/functional.h" +#include "pybind11/embed.h" #include "pybind11_json/pybind11_json.hpp" @@ -44,8 +45,11 @@ using namespace pybind11::literals; namespace xpyt { - interpreter::interpreter(bool redirect_output_enabled /*=true*/, bool redirect_display_enabled /*=true*/) - : m_redirect_output_enabled{redirect_output_enabled}, m_redirect_display_enabled{redirect_display_enabled} + interpreter::interpreter( + py::dict globals, + bool redirect_output_enabled /*=true*/, bool redirect_display_enabled /*=true*/) + : m_global_dict{globals}, + m_redirect_output_enabled{redirect_output_enabled}, m_redirect_display_enabled{redirect_display_enabled} { xeus::register_interpreter(this); } @@ -58,6 +62,7 @@ namespace xpyt { if (m_release_gil_at_startup) { + std::cout << "Releasing GIL at startup." << std::endl; // The GIL is not held by default by the interpreter, so every time we need to execute Python code we // will need to acquire the GIL m_release_gil = gil_scoped_release_ptr(new py::gil_scoped_release()); @@ -114,12 +119,17 @@ namespace xpyt py::module context_module = get_request_context_module(); } + + + + void interpreter::execute_request_impl(send_reply_callback cb, int /*execution_count*/, const std::string& code, xeus::execute_request_config config, nl::json user_expressions) { + std::cout<<"interpreter::execute_request_impl()"< traceback; + + auto when_done_callback_lambda = [this, cb, config, user_expressions]() { + std::cout<<"when_done_callback invoked"<m_ipython_shell.attr("payload_manager").attr("read_payload")(); + this->m_ipython_shell.attr("payload_manager").attr("clear_payload")(); + + // if(exception_occurred) + // { + // cb(xeus::create_error_reply(ename, evalue, traceback)); + // return; + // } + + if (this->m_ipython_shell.attr("last_error").is_none()) + { + nl::json user_exprs = this->m_ipython_shell.attr("user_expressions")(user_expressions); + cb(xeus::create_successful_reply(payload, user_exprs)); + } + else + { + py::list pyerror = this->m_ipython_shell.attr("last_error"); + + xerror error = extract_error(pyerror); + + if (!config.silent) + { + publish_execution_error(error.m_ename, error.m_evalue, error.m_traceback); + } + + cb(xeus::create_error_reply(error.m_ename, error.m_evalue, error.m_traceback)); + } + }; + std::function when_done_callback = when_done_callback_lambda; + + + try { - m_ipython_shell.attr("run_cell")(code, "store_history"_a=config.store_history, "silent"_a=config.silent); + + m_ipython_shell.attr("run_cell_async")(code, when_done_callback, "store_history"_a=config.store_history, "silent"_a=config.silent); } catch(std::runtime_error& e) { @@ -171,34 +224,12 @@ namespace xpyt exception_occurred = true; } - // Get payload - nl::json payload = m_ipython_shell.attr("payload_manager").attr("read_payload")(); - m_ipython_shell.attr("payload_manager").attr("clear_payload")(); - if(exception_occurred) - { - cb(xeus::create_error_reply(ename, evalue, traceback)); - return; - } - if (m_ipython_shell.attr("last_error").is_none()) - { - nl::json user_exprs = m_ipython_shell.attr("user_expressions")(user_expressions); - cb(xeus::create_successful_reply(payload, user_exprs)); - } - else - { - py::list pyerror = m_ipython_shell.attr("last_error"); - xerror error = extract_error(pyerror); - if (!config.silent) - { - publish_execution_error(error.m_ename, error.m_evalue, error.m_traceback); - } - cb(xeus::create_error_reply(error.m_ename, error.m_evalue, error.m_traceback)); - } + } nl::json interpreter::complete_request_impl( @@ -266,8 +297,7 @@ namespace xpyt } nl::json interpreter::kernel_info_request_impl() - { - + { /* The jupyter-console banner for xeus-python is the following: __ _____ _ _ ___ \ \/ / _ \ | | / __| @@ -300,25 +330,44 @@ namespace xpyt {"url", "https://xeus-python.readthedocs.io"} }); - return xeus::create_info_reply( - "5.3", // protocol_version - "xeus-python", // implementation - XPYT_VERSION, // implementation_version - "python", // language_name - PY_VERSION, // language_version - "text/x-python", // language_mimetype - ".py", // language_file_extension - "ipython" + std::to_string(PY_MAJOR_VERSION), // pygments_lexer - R"({"name": "ipython", "version": )" + std::to_string(PY_MAJOR_VERSION) + "}", // language_codemirror_mode - "python", // language_nbconvert_exporter - banner, // banner - (PY_MAJOR_VERSION != 3) || (PY_MINOR_VERSION != 13), // debugger - help_links // help_links - ); + // return xeus::create_info_reply( + // "5.3", // protocol_version + // "xeus-python", // implementation + // XPYT_VERSION, // implementation_version + // "python", // language_name + // PY_VERSION, // language_version + // "text/x-python", // language_mimetype + // ".py", // language_file_extension + // "ipython" + std::to_string(PY_MAJOR_VERSION), // pygments_lexer + // R"({"name": "ipython", "version": )" + std::to_string(PY_MAJOR_VERSION) + "}", // language_codemirror_mode + // "python", // language_nbconvert_exporter + // banner, // banner + // (PY_MAJOR_VERSION != 3) || (PY_MINOR_VERSION != 13), // debugger + // help_links // help_links + // ); + + nl::json kernel_res; + kernel_res["status"] = "ok"; + kernel_res["protocol_version"] = "5.3"; + kernel_res["implementation"] = "xeus-python"; + kernel_res["implementation_version"] = XPYT_VERSION; + kernel_res["language_info"]["name"] = "python"; + kernel_res["language_info"]["version"] = PY_VERSION; + kernel_res["language_info"]["mimetype"] = "text/x-python"; + kernel_res["language_info"]["file_extension"] = ".py"; + kernel_res["language_info"]["pygments_lexer"] = "ipython" + std::to_string(PY_MAJOR_VERSION); + //kernel_res["language_info"]["codemirror_mode"] = R"({"name": "ipython", "version": )" + std::to_string(PY_MAJOR_VERSION) + "}"; + kernel_res["language_info"]["nbconvert_exporter"] = "python"; + kernel_res["banner"] = banner; + kernel_res["debugger"] = false; + kernel_res["help_links"] = help_links; + return kernel_res; + } void interpreter::shutdown_request_impl() { + std::cout<<"Shutting down interpreter..."<()<() << ": " << py::str(item.second).cast() << std::endl; + } + return xeus::create_successful_reply(); } catch (py::error_already_set& e) @@ -343,6 +404,8 @@ namespace xpyt xerror error = extract_error(pyerror); + std::cout<<"Exception occurred during internal_request_impl: "< matches; int cursor_start = cursor_pos; - py::list completions = get_completions(code, cursor_pos); + py::list completions = get_completions(code, cursor_pos, m_global_dict); if (py::len(completions) != 0) { @@ -258,7 +271,7 @@ namespace xpyt nl::json kernel_res; nl::json pub_data; - std::string docstring = formatted_docstring(code, cursor_pos); + std::string docstring = formatted_docstring(code, cursor_pos, m_global_dict); bool found = false; if (!docstring.empty()) @@ -277,7 +290,7 @@ namespace xpyt nl::json raw_interpreter::kernel_info_request_impl() { - + std::cout<<"raw_interpreter::kernel_info_request_impl()"<()); + // When the debugger is started, it send some python code to execute, that triggers + // a call to publish_stream, and ultimately to this function. However: + // - we are out of the handling of an execute_request, therefore set_request_context + // has not been called + // - we cannot set it from another thread (the context of the context variable would + // be different) + // Therefore, we have to catch the exception thrown when the context variable is empty. + try + { + py::object res = context_module.attr("get_request_context")(); + return *(res.cast()); + } + catch (py::error_already_set& e) + { + return empty_request_context; + } } - void raw_interpreter::redirect_output() { py::module sys = py::module::import("sys"); diff --git a/src/xinterpreter_wasm.cpp b/src/xinterpreter_wasm.cpp index 38c3e205..333a56e1 100644 --- a/src/xinterpreter_wasm.cpp +++ b/src/xinterpreter_wasm.cpp @@ -22,7 +22,7 @@ namespace xpyt { wasm_interpreter::wasm_interpreter() - : interpreter(true, true) + : interpreter(py::globals(), true, true) { m_release_gil_at_startup = false; } diff --git a/src/xpython_extension.cpp b/src/xpython_extension.cpp index c27618b0..ec842f5e 100644 --- a/src/xpython_extension.cpp +++ b/src/xpython_extension.cpp @@ -35,6 +35,8 @@ #include "xeus-python/xdebugger.hpp" #include "xeus-python/xutils.hpp" +#include "xeus-python/xaserver.hpp" + namespace py = pybind11; @@ -70,18 +72,41 @@ void launch(const py::list args_list) std::unique_ptr context = xeus::make_zmq_context(); + + py::dict globals = py::globals(); + // Instantiating the xeus xinterpreter using interpreter_ptr = std::unique_ptr; interpreter_ptr interpreter; if (raw_mode) { - interpreter = interpreter_ptr(new xpyt::raw_interpreter()); + interpreter = interpreter_ptr(new xpyt::raw_interpreter(globals)); } else { - interpreter = interpreter_ptr(new xpyt::interpreter()); + interpreter = interpreter_ptr(new xpyt::interpreter(globals)); } + + auto make_the_debugger = [&globals]( + xeus::xcontext& context, + const xeus::xconfiguration& config, + const std::string& user_name, + const std::string& session_id, + const nl::json& debugger_config) -> std::unique_ptr + { + return xpyt::make_python_debugger( + globals, + context, + config, + user_name, + session_id, + debugger_config); + }; + + + + using history_manager_ptr = std::unique_ptr; history_manager_ptr hist = xeus::make_in_memory_history_manager(); @@ -100,11 +125,11 @@ void launch(const py::list args_list) xeus::get_user_name(), std::move(context), std::move(interpreter), - xeus::make_xserver_shell_main, + xpyt::make_xaserver_factory(globals), std::move(hist), xeus::make_console_logger(xeus::xlogger::msg_type, xeus::make_file_logger(xeus::xlogger::content, "xeus.log")), - xpyt::make_python_debugger); + make_the_debugger); std::clog << "Starting xeus-python kernel...\n\n" @@ -119,10 +144,10 @@ void launch(const py::list args_list) xeus::xkernel kernel(xeus::get_user_name(), std::move(context), std::move(interpreter), - xeus::make_xserver_shell_main, + xpyt::make_xaserver_factory(globals), std::move(hist), nullptr, - xpyt::make_python_debugger); + make_the_debugger); const auto& config = kernel.get_config(); std::clog << diff --git a/src/xutils.cpp b/src/xutils.cpp index 811b8beb..ad4282af 100644 --- a/src/xutils.cpp +++ b/src/xutils.cpp @@ -59,6 +59,7 @@ namespace xpyt void exec(const py::object& code, const py::object& scope) { py::exec("exec(_code_, _scope_, _scope_)", py::globals(), py::dict(py::arg("_code_") = code, py::arg("_scope_") = scope)); + // py::exec(code, scope, scope); } py::object eval(const py::object& code, const py::object& scope) @@ -87,6 +88,7 @@ namespace xpyt void sigsegv_handler(int sig) { + std::cout << "Segmentation fault (signal " << sig << "). Exiting..." << std::endl; #if defined(__GNUC__) && !defined(XPYT_EMSCRIPTEN_WASM_BUILD) void* array[10]; @@ -102,6 +104,7 @@ namespace xpyt void sigkill_handler(int /*sig*/) { + std::cout << "Received termination signal. Exiting..." << std::endl; exit(0); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d5fafe2c..d73955d0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,7 +12,7 @@ # ========== cmake_minimum_required(VERSION 3.20) - +enable_testing() if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) project(xeus-python-test) @@ -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 @@ -57,6 +60,7 @@ set(XEUS_PYTHON_TESTS add_executable(test_xeus_python ${XEUS_PYTHON_TESTS}) + target_compile_features(test_xeus_python PRIVATE cxx_std_17) if (APPLE) @@ -70,7 +74,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 208c07af..9fd1bbd1 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 * ***********************************/ @@ -995,7 +1000,7 @@ nl::json debugger_client::attach() if (!rep["content"]["success"].get()) { shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); throw std::runtime_error("Could not initialize debugger, exiting"); } m_client.send_on_control("debug_request", make_attach_request(3)); @@ -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(); { @@ -1168,7 +1179,7 @@ TEST_SUITE("debugger") bool res = deb.test_init(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -1176,7 +1187,7 @@ TEST_SUITE("debugger") TEST_CASE("disconnect") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1184,7 +1195,7 @@ TEST_SUITE("debugger") deb.start(); bool res = deb.test_disconnect(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -1192,7 +1203,7 @@ TEST_SUITE("debugger") TEST_CASE("attach") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1201,7 +1212,7 @@ TEST_SUITE("debugger") bool res = deb.test_attach(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -1209,17 +1220,17 @@ TEST_SUITE("debugger") TEST_CASE("multisession") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { debugger_client deb(*context_ptr, KERNEL_JSON, "debugger_multi_session.log"); deb.start(); bool res1 = deb.test_disconnect(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); bool res2 = deb.test_disconnect(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res1); CHECK(res2); t.notify_done(); @@ -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(); { @@ -1237,7 +1248,7 @@ TEST_SUITE("debugger") bool res = deb.test_external_set_breakpoints(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1256,7 +1267,7 @@ TEST_SUITE("debugger") bool res = deb.test_external_next_continue(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1273,7 +1284,7 @@ TEST_SUITE("debugger") bool res = deb.test_set_breakpoints(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1307,7 +1318,7 @@ TEST_SUITE("debugger") bool res = deb.test_source(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1326,7 +1337,7 @@ TEST_SUITE("debugger") bool res = deb.test_next_continue(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -1337,7 +1348,7 @@ TEST_SUITE("debugger") /* TEST_CASE("stepin") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1346,7 +1357,7 @@ TEST_SUITE("debugger") bool res = deb.test_step_in(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1364,7 +1375,7 @@ TEST_SUITE("debugger") bool res = deb.test_stack_trace(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1381,7 +1392,7 @@ TEST_SUITE("debugger") bool res = deb.test_debug_info(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1398,7 +1409,7 @@ TEST_SUITE("debugger") bool res = deb.test_inspect_variables(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1416,7 +1427,7 @@ TEST_SUITE("debugger") deb.start(); bool res = deb.test_rich_inspect_variables(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -1425,7 +1436,7 @@ TEST_SUITE("debugger") TEST_CASE("variables") { - start_kernel(); + KernelProcess xpython_process; timer t; auto context_ptr = xeus::make_zmq_context(); { @@ -1434,7 +1445,7 @@ TEST_SUITE("debugger") bool res = deb.test_variables(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); } @@ -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(); { @@ -1451,7 +1462,7 @@ TEST_SUITE("debugger") bool res = deb.test_copy_to_globals(); deb.disconnect_debugger(); deb.shutdown(); - std::this_thread::sleep_for(2s); + std::this_thread::sleep_for(4s); CHECK(res); t.notify_done(); }