2018-03-02 13:15:53 +00:00
|
|
|
#ifdef CUTTER_ENABLE_JUPYTER
|
2017-12-13 17:36:00 +00:00
|
|
|
|
2018-02-09 15:48:02 +00:00
|
|
|
#include <Python.h>
|
|
|
|
|
2017-12-13 17:36:00 +00:00
|
|
|
#include "JupyterConnection.h"
|
2018-02-22 21:08:06 +00:00
|
|
|
#include "NestedIPyKernel.h"
|
2018-06-23 16:59:23 +00:00
|
|
|
#include "PythonManager.h"
|
2019-02-03 11:31:59 +00:00
|
|
|
#include "QtResImporter.h"
|
2018-06-23 16:59:23 +00:00
|
|
|
|
|
|
|
#include <QVariant>
|
|
|
|
#include <QDebug>
|
2017-12-13 17:36:00 +00:00
|
|
|
|
2018-02-22 19:56:15 +00:00
|
|
|
Q_GLOBAL_STATIC(JupyterConnection, uniqueInstance)
|
|
|
|
|
|
|
|
JupyterConnection *JupyterConnection::getInstance()
|
|
|
|
{
|
|
|
|
return uniqueInstance;
|
|
|
|
}
|
|
|
|
|
2017-12-13 17:36:00 +00:00
|
|
|
JupyterConnection::JupyterConnection(QObject *parent) : QObject(parent)
|
|
|
|
{
|
2019-02-03 11:31:59 +00:00
|
|
|
connect(Python(), &PythonManager::willShutDown, this, &JupyterConnection::stop);
|
2017-12-13 17:36:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
JupyterConnection::~JupyterConnection()
|
|
|
|
{
|
2018-09-05 06:27:33 +00:00
|
|
|
}
|
|
|
|
|
2018-02-22 19:56:15 +00:00
|
|
|
|
|
|
|
void JupyterConnection::start()
|
|
|
|
{
|
2018-06-23 16:59:23 +00:00
|
|
|
if (notebookInstanceExists) {
|
2018-02-22 19:56:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-03 11:31:59 +00:00
|
|
|
notebookInstanceExists = startJupyterNotebook();
|
2018-02-22 19:56:15 +00:00
|
|
|
|
|
|
|
emit urlReceived(getUrl());
|
|
|
|
}
|
|
|
|
|
2019-02-03 11:31:59 +00:00
|
|
|
void JupyterConnection::stop()
|
|
|
|
{
|
|
|
|
if (cutterNotebookAppInstance) {
|
|
|
|
Python()->restoreThread();
|
|
|
|
auto stopFunc = PyObject_GetAttrString(cutterNotebookAppInstance, "stop");
|
|
|
|
PyObject_CallObject(stopFunc, nullptr);
|
|
|
|
Py_DECREF(cutterNotebookAppInstance);
|
|
|
|
Python()->saveThread();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-22 19:56:15 +00:00
|
|
|
QString JupyterConnection::getUrl()
|
|
|
|
{
|
2018-06-23 16:59:23 +00:00
|
|
|
if (!notebookInstanceExists) {
|
2018-02-22 19:56:15 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-02-03 11:31:59 +00:00
|
|
|
QString url = getJupyterUrl();
|
2018-06-23 16:59:23 +00:00
|
|
|
return url;
|
2018-02-22 21:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
long JupyterConnection::startNestedIPyKernel(const QStringList &argv)
|
|
|
|
{
|
|
|
|
NestedIPyKernel *kernel = NestedIPyKernel::start(argv);
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!kernel) {
|
2018-02-22 21:08:06 +00:00
|
|
|
qWarning() << "Could not start nested IPyKernel.";
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
long id = nextKernelId++;
|
|
|
|
kernels.insert(id, kernel);
|
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
NestedIPyKernel *JupyterConnection::getNestedIPyKernel(long id)
|
|
|
|
{
|
2018-02-23 12:04:53 +00:00
|
|
|
auto it = kernels.find(id);
|
2018-03-21 20:32:32 +00:00
|
|
|
if (it == kernels.end()) {
|
2018-02-23 12:04:53 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return *it;
|
2018-02-22 21:08:06 +00:00
|
|
|
}
|
2018-02-23 16:42:54 +00:00
|
|
|
|
|
|
|
QVariant JupyterConnection::pollNestedIPyKernel(long id)
|
|
|
|
{
|
|
|
|
auto it = kernels.find(id);
|
2018-03-21 20:32:32 +00:00
|
|
|
if (it == kernels.end()) {
|
2018-02-23 16:42:54 +00:00
|
|
|
return QVariant(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
NestedIPyKernel *kernel = *it;
|
|
|
|
QVariant v = kernel->poll();
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
if (!v.isNull()) {
|
2018-02-23 16:42:54 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2018-03-02 13:15:53 +00:00
|
|
|
|
2019-02-03 11:31:59 +00:00
|
|
|
bool JupyterConnection::startJupyterNotebook()
|
|
|
|
{
|
|
|
|
Python()->restoreThread();
|
|
|
|
|
|
|
|
if (!cutterJupyterModule) {
|
|
|
|
cutterJupyterModule = QtResImport("cutter_jupyter");
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* startFunc = PyObject_GetAttrString(cutterJupyterModule, "start_jupyter");
|
|
|
|
if (!startFunc) {
|
|
|
|
qWarning() << "Couldn't get attribute start_jupyter.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
cutterNotebookAppInstance = PyObject_CallObject(startFunc, nullptr);
|
|
|
|
Python()->saveThread();
|
|
|
|
|
|
|
|
return cutterNotebookAppInstance != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString JupyterConnection::getJupyterUrl()
|
|
|
|
{
|
|
|
|
Python()->restoreThread();
|
|
|
|
|
|
|
|
auto urlWithToken = PyObject_GetAttrString(cutterNotebookAppInstance, "url_with_token");
|
|
|
|
auto asciiBytes = PyUnicode_AsASCIIString(urlWithToken);
|
|
|
|
auto urlWithTokenString = QString::fromUtf8(PyBytes_AsString(asciiBytes));
|
|
|
|
Py_DECREF(asciiBytes);
|
|
|
|
Py_DECREF(urlWithToken);
|
|
|
|
|
|
|
|
Python()->saveThread();
|
|
|
|
|
|
|
|
return urlWithTokenString;
|
|
|
|
}
|
|
|
|
|
2018-03-02 13:15:53 +00:00
|
|
|
#endif
|