IPyKernel signal and poll

This commit is contained in:
Florian Märkl 2018-02-23 13:04:53 +01:00 committed by xarkes
parent 926a9ffd1e
commit 693fc1eb1f
6 changed files with 84 additions and 30 deletions

View File

@ -7,8 +7,8 @@ from ipykernel.ipkernel import IPythonKernel
# TODO: Make this behave like a Popen instance and pipe it to IPyKernelInterfaceJupyter! # TODO: Make this behave like a Popen instance and pipe it to IPyKernelInterfaceJupyter!
class IPyKernelInterfaceKernel: class IPyKernelInterfaceKernel:
def kill(self): def send_signal(self, signum):
print("No!! Not into the pit! It burns!!!") pass
def poll(self): def poll(self):
return None return None

View File

@ -1,34 +1,32 @@
from jupyter_client.ioloop import IOLoopKernelManager from jupyter_client.ioloop import IOLoopKernelManager
from notebook.notebookapp import * from notebook.notebookapp import *
import signal
import cutter_internal
# TODO: this must behave like a Popen instance and pipe to IPyKernelInterfaceKernel!
class IPyKernelInterfaceJupyter: class IPyKernelInterfaceJupyter:
def __init__(self, id): def __init__(self, id):
self._id = id self._id = id
def send_signal(self, signum):
cutter_internal.kernel_interface_send_signal(self._id, signum)
def kill(self): def kill(self):
print("DIE!! " + str(self._id)) self.send_signal(signal.SIGKILL)
def terminate(self):
self.send_signal(signal.SIGTERM)
def poll(self): def poll(self):
return None return cutter_internal.kernel_interface_poll(self._id)
def wait(self, timeout=None):
pass
class CutterInternalIPyKernelManager(IOLoopKernelManager): class CutterInternalIPyKernelManager(IOLoopKernelManager):
def start_kernel(self, **kw): def start_kernel(self, **kw):
"""Starts a kernel on this host in a separate process.
If random ports (port=0) are being used, this method must be called
before the channels are created.
Parameters
----------
`**kw` : optional
keyword arguments that are passed down to build the kernel_cmd
and launching the kernel (e.g. Popen kwargs).
"""
# write connection file / get default ports # write connection file / get default ports
self.write_connection_file() self.write_connection_file()
@ -50,15 +48,16 @@ class CutterInternalIPyKernelManager(IOLoopKernelManager):
self.log.debug("Starting kernel: %s", kernel_cmd) self.log.debug("Starting kernel: %s", kernel_cmd)
# TODO: kernel_cmd including python executable and so on is currently used for argv. Make a clean version! # TODO: kernel_cmd including python executable and so on is currently used for argv. Make a clean version!
import cutter_internal
id = cutter_internal.launch_ipykernel(kernel_cmd, env=env, **kw) id = cutter_internal.launch_ipykernel(kernel_cmd, env=env, **kw)
self.kernel = IPyKernelInterfaceJupyter(id) self.kernel = IPyKernelInterfaceJupyter(id)
# self._launch_kernel(kernel_cmd, env=env, # self._launch_kernel(kernel_cmd, env=env, **kw)
# **kw)
self.start_restarter() self.start_restarter()
self._connect_control_socket() self._connect_control_socket()
def signal_kernel(self, signum):
self.kernel.send_signal(signum)
def kernel_manager_factory(kernel_name, **kwargs): def kernel_manager_factory(kernel_name, **kwargs):
if kernel_name in {"python", "python2", "python3"}: if kernel_name in {"python", "python2", "python3"}:

View File

@ -144,5 +144,10 @@ long JupyterConnection::startNestedIPyKernel(const QStringList &argv)
NestedIPyKernel *JupyterConnection::getNestedIPyKernel(long id) NestedIPyKernel *JupyterConnection::getNestedIPyKernel(long id)
{ {
return kernels[id]; auto it = kernels.find(id);
if(it == kernels.end())
{
return nullptr;
}
return *it;
} }

View File

@ -45,6 +45,8 @@ NestedIPyKernel *NestedIPyKernel::start(const QStringList &argv)
NestedIPyKernel::NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv) NestedIPyKernel::NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv)
{ {
threadState = PyThreadState_Get();
auto launchFunc = PyObject_GetAttrString(cutterIPykernelModule, "launch_ipykernel"); auto launchFunc = PyObject_GetAttrString(cutterIPykernelModule, "launch_ipykernel");
PyObject *argvListObject = PyList_New(argv.size()); PyObject *argvListObject = PyList_New(argv.size());
@ -59,12 +61,24 @@ NestedIPyKernel::NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringL
NestedIPyKernel::~NestedIPyKernel() NestedIPyKernel::~NestedIPyKernel()
{ {
} }
void NestedIPyKernel::kill() void NestedIPyKernel::sendSignal(long signum)
{ {
auto parentThreadState = PyThreadState_Swap(threadState); auto parentThreadState = PyThreadState_Swap(threadState);
PyObject_CallMethod(kernel, "kill", nullptr); PyObject_CallMethod(kernel, "send_signal", "l", signum);
PyThreadState_Swap(parentThreadState); PyThreadState_Swap(parentThreadState);
} }
QVariant NestedIPyKernel::poll()
{
QVariant ret;
auto parentThreadState = PyThreadState_Swap(threadState);
PyObject *pyRet = PyObject_CallMethod(kernel, "poll", nullptr);
if(PyLong_Check(pyRet))
{
ret = (qlonglong)PyLong_AsLong(pyRet);
}
PyThreadState_Swap(parentThreadState);
return ret;
}

View File

@ -17,7 +17,8 @@ public:
static NestedIPyKernel *start(const QStringList &argv); static NestedIPyKernel *start(const QStringList &argv);
~NestedIPyKernel(); ~NestedIPyKernel();
void kill(); void sendSignal(long signum);
QVariant poll();
private: private:
NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv); NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv);

View File

@ -74,25 +74,60 @@ PyObject *api_internal_launch_ipykernel(PyObject *self, PyObject *args, PyObject
return PyLong_FromLong(id); return PyLong_FromLong(id);
} }
PyObject *api_internal_kernel_interface_kill(PyObject *, PyObject *args) PyObject *api_internal_kernel_interface_send_signal(PyObject *, PyObject *args)
{
long id;
long signum;
if (!PyArg_ParseTuple(args, "ll", &id, &signum))
{
qWarning() << "Invalid args passed to api_internal_kernel_interface_send_signal().";
return nullptr;
}
NestedIPyKernel *kernel = Jupyter()->getNestedIPyKernel(id);
if(kernel)
{
kernel->sendSignal(signum);
}
Py_RETURN_NONE;
}
PyObject *api_internal_kernel_interface_poll(PyObject *, PyObject *args)
{ {
long id; long id;
if (!PyArg_ParseTuple(args, "l", &id)) if (!PyArg_ParseTuple(args, "l", &id))
{ {
qWarning() << "Invalid args passed to api_internal_kernel_interface_kill()."; qWarning() << "Invalid args passed to api_internal_kernel_interface_poll().";
return nullptr; return nullptr;
} }
Jupyter()->getNestedIPyKernel(id)->kill(); NestedIPyKernel *kernel = Jupyter()->getNestedIPyKernel(id);
if(!kernel)
{
return PyLong_FromLong(0);
}
Py_RETURN_NONE; QVariant v = kernel->poll();
bool ok;
auto ret = static_cast<long>(v.toLongLong(&ok));
if(ok)
{
return PyLong_FromLong(ret);
}
else
{
Py_RETURN_NONE;
}
} }
PyMethodDef CutterInternalMethods[] = { PyMethodDef CutterInternalMethods[] = {
{"launch_ipykernel", (PyCFunction)api_internal_launch_ipykernel, METH_VARARGS | METH_KEYWORDS, {"launch_ipykernel", (PyCFunction)api_internal_launch_ipykernel, METH_VARARGS | METH_KEYWORDS,
"Launch an IPython Kernel in a subinterpreter"}, "Launch an IPython Kernel in a subinterpreter"},
{"kernel_interface_kill", (PyCFunction)api_internal_kernel_interface_kill, METH_VARARGS, ""}, {"kernel_interface_send_signal", (PyCFunction)api_internal_kernel_interface_send_signal, METH_VARARGS, ""},
{"kernel_interface_poll", (PyCFunction)api_internal_kernel_interface_poll, METH_VARARGS, ""},
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };