diff --git a/src/python/cutter_ipykernel.py b/src/python/cutter_ipykernel.py index bf9985ec..b78a1ac2 100644 --- a/src/python/cutter_ipykernel.py +++ b/src/python/cutter_ipykernel.py @@ -3,6 +3,7 @@ import logging import threading import signal import cutter_internal +import zmq from ipykernel.kernelapp import IPKernelApp from ipykernel.ipkernel import IPythonKernel @@ -28,6 +29,16 @@ class IPyKernelInterfaceKernel: else: return 0 + def cleanup(self): + self._app.heartbeat.context.destroy() + self._thread.join() + self._app.heartbeat.join() + self._app.iopub_thread.stop() + self._app.kernel.shell.history_manager.save_thread.stop() + zmq.Context.instance().destroy() + # successful if only the main thread remains + return len(threading.enumerate()) == 1 + class CutterIPythonKernel(IPythonKernel): def __init__(self, **kwargs): @@ -59,7 +70,7 @@ def launch_ipykernel(argv): def run_kernel(): app.kernel_class = CutterIPythonKernel - #app.log_level = logging.DEBUG + # app.log_level = logging.DEBUG app.initialize(argv[3:]) app.start() diff --git a/src/utils/JupyterConnection.cpp b/src/utils/JupyterConnection.cpp index a12fe045..151fe9fd 100644 --- a/src/utils/JupyterConnection.cpp +++ b/src/utils/JupyterConnection.cpp @@ -156,3 +156,29 @@ NestedIPyKernel *JupyterConnection::getNestedIPyKernel(long id) } return *it; } + +QVariant JupyterConnection::pollNestedIPyKernel(long id) +{ + auto it = kernels.find(id); + if(it == kernels.end()) + { + return QVariant(0); + } + + NestedIPyKernel *kernel = *it; + QVariant v = kernel->poll(); + + if(!v.isNull()) + { + // if poll of kernel returns anything but None, it has already quit and should be cleaned up + PyThreadState *subinterpreterState = kernel->getThreadState(); + delete kernel; + kernels.erase(it); + + PyThreadState *parentThreadState = PyThreadState_Swap(subinterpreterState); + Py_EndInterpreter(subinterpreterState); + PyThreadState_Swap(parentThreadState); + } + + return v; +} diff --git a/src/utils/JupyterConnection.h b/src/utils/JupyterConnection.h index c67727ce..a408c977 100644 --- a/src/utils/JupyterConnection.h +++ b/src/utils/JupyterConnection.h @@ -26,6 +26,7 @@ public: long startNestedIPyKernel(const QStringList &argv); NestedIPyKernel *getNestedIPyKernel(long id); + QVariant pollNestedIPyKernel(long id); signals: void urlReceived(const QString &url); diff --git a/src/utils/NestedIPyKernel.cpp b/src/utils/NestedIPyKernel.cpp index c066a8ad..b7e05eff 100644 --- a/src/utils/NestedIPyKernel.cpp +++ b/src/utils/NestedIPyKernel.cpp @@ -62,6 +62,13 @@ NestedIPyKernel::NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringL NestedIPyKernel::~NestedIPyKernel() { + auto parentThreadState = PyThreadState_Swap(threadState); + auto ret = PyObject_CallMethod(kernel, "cleanup", nullptr); + if (!ret) + { + PyErr_Print(); + } + PyThreadState_Swap(parentThreadState); } void NestedIPyKernel::sendSignal(long signum) diff --git a/src/utils/NestedIPyKernel.h b/src/utils/NestedIPyKernel.h index 12751f87..dce41ac2 100644 --- a/src/utils/NestedIPyKernel.h +++ b/src/utils/NestedIPyKernel.h @@ -20,6 +20,8 @@ public: void sendSignal(long signum); QVariant poll(); + PyThreadState *getThreadState() { return threadState; } + private: NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv); diff --git a/src/utils/PythonAPI.cpp b/src/utils/PythonAPI.cpp index e0ba13df..0b7cdfdc 100644 --- a/src/utils/PythonAPI.cpp +++ b/src/utils/PythonAPI.cpp @@ -110,13 +110,7 @@ PyObject *api_internal_kernel_interface_poll(PyObject *, PyObject *args) return nullptr; } - NestedIPyKernel *kernel = Jupyter()->getNestedIPyKernel(id); - if(!kernel) - { - return PyLong_FromLong(0); - } - - QVariant v = kernel->poll(); + QVariant v = Jupyter()->pollNestedIPyKernel(id); bool ok; auto ret = static_cast(v.toLongLong(&ok)); if(ok)