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!
class IPyKernelInterfaceKernel:
def kill(self):
print("No!! Not into the pit! It burns!!!")
def send_signal(self, signum):
pass
def poll(self):
return None

View File

@ -1,34 +1,32 @@
from jupyter_client.ioloop import IOLoopKernelManager
from notebook.notebookapp import *
import signal
import cutter_internal
# TODO: this must behave like a Popen instance and pipe to IPyKernelInterfaceKernel!
class IPyKernelInterfaceJupyter:
def __init__(self, id):
self._id = id
def send_signal(self, signum):
cutter_internal.kernel_interface_send_signal(self._id, signum)
def kill(self):
print("DIE!! " + str(self._id))
self.send_signal(signal.SIGKILL)
def terminate(self):
self.send_signal(signal.SIGTERM)
def poll(self):
return None
return cutter_internal.kernel_interface_poll(self._id)
def wait(self, timeout=None):
pass
class CutterInternalIPyKernelManager(IOLoopKernelManager):
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
self.write_connection_file()
@ -50,15 +48,16 @@ class CutterInternalIPyKernelManager(IOLoopKernelManager):
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!
import cutter_internal
id = cutter_internal.launch_ipykernel(kernel_cmd, env=env, **kw)
self.kernel = IPyKernelInterfaceJupyter(id)
# self._launch_kernel(kernel_cmd, env=env,
# **kw)
# self._launch_kernel(kernel_cmd, env=env, **kw)
self.start_restarter()
self._connect_control_socket()
def signal_kernel(self, signum):
self.kernel.send_signal(signum)
def kernel_manager_factory(kernel_name, **kwargs):
if kernel_name in {"python", "python2", "python3"}:

View File

@ -144,5 +144,10 @@ long JupyterConnection::startNestedIPyKernel(const QStringList &argv)
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)
{
threadState = PyThreadState_Get();
auto launchFunc = PyObject_GetAttrString(cutterIPykernelModule, "launch_ipykernel");
PyObject *argvListObject = PyList_New(argv.size());
@ -59,12 +61,24 @@ NestedIPyKernel::NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringL
NestedIPyKernel::~NestedIPyKernel()
{
}
void NestedIPyKernel::kill()
void NestedIPyKernel::sendSignal(long signum)
{
auto parentThreadState = PyThreadState_Swap(threadState);
PyObject_CallMethod(kernel, "kill", nullptr);
PyObject_CallMethod(kernel, "send_signal", "l", signum);
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);
~NestedIPyKernel();
void kill();
void sendSignal(long signum);
QVariant poll();
private:
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);
}
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;
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;
}
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[] = {
{"launch_ipykernel", (PyCFunction)api_internal_launch_ipykernel, METH_VARARGS | METH_KEYWORDS,
"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}
};