Prepare IPyKernel communication

This commit is contained in:
Florian Märkl 2018-02-22 22:08:06 +01:00 committed by xarkes
parent 05be34ae6b
commit 926a9ffd1e
8 changed files with 159 additions and 34 deletions

View File

@ -102,7 +102,8 @@ SOURCES += \
CutterApplication.cpp \ CutterApplication.cpp \
utils/JupyterConnection.cpp \ utils/JupyterConnection.cpp \
widgets/JupyterWidget.cpp \ widgets/JupyterWidget.cpp \
utils/PythonAPI.cpp utils/PythonAPI.cpp \
utils/NestedIPyKernel.cpp
HEADERS += \ HEADERS += \
cutter.h \ cutter.h \
@ -166,7 +167,8 @@ HEADERS += \
widgets/VTablesWidget.h \ widgets/VTablesWidget.h \
utils/JupyterConnection.h \ utils/JupyterConnection.h \
widgets/JupyterWidget.h \ widgets/JupyterWidget.h \
utils/PythonAPI.h utils/PythonAPI.h \
utils/NestedIPyKernel.h
FORMS += \ FORMS += \
dialogs/AboutDialog.ui \ dialogs/AboutDialog.ui \

View File

@ -7,6 +7,9 @@ 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):
print("No!! Not into the pit! It burns!!!")
def poll(self): def poll(self):
return None return None

View File

@ -5,6 +5,12 @@ from notebook.notebookapp import *
# TODO: this must behave like a Popen instance and pipe to IPyKernelInterfaceKernel! # TODO: this must behave like a Popen instance and pipe to IPyKernelInterfaceKernel!
class IPyKernelInterfaceJupyter: class IPyKernelInterfaceJupyter:
def __init__(self, id):
self._id = id
def kill(self):
print("DIE!! " + str(self._id))
def poll(self): def poll(self):
return None return None
@ -45,8 +51,8 @@ class CutterInternalIPyKernelManager(IOLoopKernelManager):
# 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 import cutter_internal
cutter_internal.launch_ipykernel(kernel_cmd, env=env, **kw) id = cutter_internal.launch_ipykernel(kernel_cmd, env=env, **kw)
self.kernel = IPyKernelInterfaceJupyter() self.kernel = IPyKernelInterfaceJupyter(id)
# self._launch_kernel(kernel_cmd, env=env, # self._launch_kernel(kernel_cmd, env=env,
# **kw) # **kw)

View File

@ -9,6 +9,7 @@
#include <QFile> #include <QFile>
#include "JupyterConnection.h" #include "JupyterConnection.h"
#include "NestedIPyKernel.h"
#include "PythonAPI.h" #include "PythonAPI.h"
Q_GLOBAL_STATIC(JupyterConnection, uniqueInstance) Q_GLOBAL_STATIC(JupyterConnection, uniqueInstance)
@ -123,4 +124,25 @@ QString JupyterConnection::getUrl()
pyThreadState = PyEval_SaveThread(); pyThreadState = PyEval_SaveThread();
return urlWithTokenString; return urlWithTokenString;
} }
long JupyterConnection::startNestedIPyKernel(const QStringList &argv)
{
NestedIPyKernel *kernel = NestedIPyKernel::start(argv);
if (!kernel)
{
qWarning() << "Could not start nested IPyKernel.";
return 0;
}
long id = nextKernelId++;
kernels.insert(id, kernel);
return id;
}
NestedIPyKernel *JupyterConnection::getNestedIPyKernel(long id)
{
return kernels[id];
}

View File

@ -3,6 +3,8 @@
#include <QProcess> #include <QProcess>
class NestedIPyKernel;
struct _object; struct _object;
typedef _object PyObject; typedef _object PyObject;
@ -22,6 +24,9 @@ public:
void start(); void start();
QString getUrl(); QString getUrl();
long startNestedIPyKernel(const QStringList &argv);
NestedIPyKernel *getNestedIPyKernel(long id);
signals: signals:
void urlReceived(const QString &url); void urlReceived(const QString &url);
void creationFailed(); void creationFailed();
@ -32,6 +37,9 @@ private:
PyThreadState *pyThreadState = nullptr; PyThreadState *pyThreadState = nullptr;
QMap<long, NestedIPyKernel *> kernels;
long nextKernelId = 1;
void initPython(); void initPython();
void createCutterJupyterModule(); void createCutterJupyterModule();
}; };

View File

@ -0,0 +1,70 @@
#include <Python.h>
#include <QFile>
#include "cutter.h"
#include "NestedIPyKernel.h"
NestedIPyKernel *NestedIPyKernel::start(const QStringList &argv)
{
PyThreadState *parentThreadState = PyThreadState_Get();
PyThreadState *threadState = Py_NewInterpreter();
if (!threadState)
{
qWarning() << "Could not create subinterpreter.";
return nullptr;
}
QFile moduleFile(":/python/cutter_ipykernel.py");
moduleFile.open(QIODevice::ReadOnly);
QByteArray moduleCode = moduleFile.readAll();
moduleFile.close();
auto moduleCodeObject = Py_CompileString(moduleCode.constData(), "cutter_ipykernel.py", Py_file_input);
if (!moduleCodeObject)
{
qWarning() << "Could not compile cutter_ipykernel.";
return nullptr;
}
auto cutterIPykernelModule = PyImport_ExecCodeModule("cutter_ipykernel", moduleCodeObject);
Py_DECREF(moduleCodeObject);
if (!cutterIPykernelModule)
{
qWarning() << "Could not import cutter_ipykernel.";
return nullptr;
}
auto kernel = new NestedIPyKernel(cutterIPykernelModule, argv);
PyThreadState_Swap(parentThreadState);
return kernel;
}
NestedIPyKernel::NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv)
{
auto launchFunc = PyObject_GetAttrString(cutterIPykernelModule, "launch_ipykernel");
PyObject *argvListObject = PyList_New(argv.size());
for (int i = 0; i < argv.size(); i++)
{
QString s = argv[i];
PyList_SetItem(argvListObject, i, PyUnicode_DecodeUTF8(s.toUtf8().constData(), s.length(), nullptr));
}
kernel = PyObject_CallFunction(launchFunc, "O", argvListObject);
}
NestedIPyKernel::~NestedIPyKernel()
{
}
void NestedIPyKernel::kill()
{
auto parentThreadState = PyThreadState_Swap(threadState);
PyObject_CallMethod(kernel, "kill", nullptr);
PyThreadState_Swap(parentThreadState);
}

View File

@ -0,0 +1,29 @@
#ifndef NESTEDIPYKERNEL_H
#define NESTEDIPYKERNEL_H
#include <QStringList>
struct _object;
typedef _object PyObject;
struct _ts;
typedef _ts PyThreadState;
class NestedIPyKernel
{
public:
static NestedIPyKernel *start(const QStringList &argv);
~NestedIPyKernel();
void kill();
private:
NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv);
PyThreadState *threadState;
PyObject *kernel;
};
#endif //NESTEDIPYKERNEL_H

View File

@ -1,6 +1,8 @@
#include "PythonAPI.h" #include "PythonAPI.h"
#include "cutter.h" #include "cutter.h"
#include "JupyterConnection.h"
#include "NestedIPyKernel.h"
#include <QFile> #include <QFile>
@ -67,47 +69,30 @@ PyObject *api_internal_launch_ipykernel(PyObject *self, PyObject *args, PyObject
argv.append(s); argv.append(s);
} }
PyThreadState *parentThreadState = PyThreadState_Get(); long id = Jupyter()->startNestedIPyKernel(argv);
QFile moduleFile(":/python/cutter_ipykernel.py"); return PyLong_FromLong(id);
moduleFile.open(QIODevice::ReadOnly); }
QByteArray moduleCode = moduleFile.readAll();
moduleFile.close();
auto moduleCodeObject = Py_CompileString(moduleCode.constData(), "cutter_ipykernel.py", Py_file_input); PyObject *api_internal_kernel_interface_kill(PyObject *, PyObject *args)
if (!moduleCodeObject) {
long id;
if (!PyArg_ParseTuple(args, "l", &id))
{ {
qWarning() << "Could not compile cutter_ipykernel."; qWarning() << "Invalid args passed to api_internal_kernel_interface_kill().";
return nullptr;
}
auto cutterIPykernelModule = PyImport_ExecCodeModule("cutter_ipykernel", moduleCodeObject);
Py_DECREF(moduleCodeObject);
if (!cutterIPykernelModule)
{
qWarning() << "Could not import cutter_ipykernel.";
return nullptr; return nullptr;
} }
auto launchFunc = PyObject_GetAttrString(cutterIPykernelModule, "launch_ipykernel"); Jupyter()->getNestedIPyKernel(id)->kill();
argvListObject = PyList_New(argv.size()); Py_RETURN_NONE;
for (int i = 0; i < argv.size(); i++)
{
QString s = argv[i];
PyList_SetItem(argvListObject, i, PyUnicode_DecodeUTF8(s.toUtf8().constData(), s.length(), nullptr));
}
auto ipyKernel = PyObject_CallFunction(launchFunc, "O", argvListObject);
Q_UNUSED(ipyKernel);
PyThreadState_Swap(parentThreadState);
return PyLong_FromLong(42);
} }
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, ""},
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };