mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-31 16:47:26 +00:00
Run IPyKernel in subinterpreter and thus expose Cutter bindings to it
This commit is contained in:
parent
abd9cca091
commit
b9b903052c
39
src/python/cutter_ipykernel.py
Normal file
39
src/python/cutter_ipykernel.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
from ipykernel.kernelapp import IPKernelApp
|
||||||
|
from ipykernel.ipkernel import IPythonKernel
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Make this behave like a Popen instance and pipe it to IPyKernelInterfaceJupyter!
|
||||||
|
class IPyKernelInterfaceKernel:
|
||||||
|
def poll(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class CutterIPythonKernel(IPythonKernel):
|
||||||
|
def pre_handler_hook(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def post_handler_hook(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CutterIPKernelApp(IPKernelApp):
|
||||||
|
def init_signal(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def launch_ipykernel(argv):
|
||||||
|
def run_kernel():
|
||||||
|
app = CutterIPKernelApp.instance()
|
||||||
|
app.kernel_class = CutterIPythonKernel
|
||||||
|
app.log_level = logging.DEBUG
|
||||||
|
app.initialize(argv[3:])
|
||||||
|
app.start()
|
||||||
|
|
||||||
|
thread = threading.Thread(target=run_kernel)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
return IPyKernelInterfaceKernel()
|
||||||
|
|
@ -1,8 +1,64 @@
|
|||||||
import sys
|
|
||||||
import threading
|
from jupyter_client.ioloop import IOLoopKernelManager
|
||||||
import time
|
|
||||||
from notebook.notebookapp import *
|
from notebook.notebookapp import *
|
||||||
import cutter
|
|
||||||
|
|
||||||
|
# TODO: this must behave like a Popen instance and pipe to IPyKernelInterfaceKernel!
|
||||||
|
class IPyKernelInterfaceJupyter:
|
||||||
|
def poll(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
# save kwargs for use in restart
|
||||||
|
self._launch_args = kw.copy()
|
||||||
|
# build the Popen cmd
|
||||||
|
extra_arguments = kw.pop('extra_arguments', [])
|
||||||
|
kernel_cmd = self.format_kernel_cmd(extra_arguments=extra_arguments)
|
||||||
|
env = kw.pop('env', os.environ).copy()
|
||||||
|
# Don't allow PYTHONEXECUTABLE to be passed to kernel process.
|
||||||
|
# If set, it can bork all the things.
|
||||||
|
env.pop('PYTHONEXECUTABLE', None)
|
||||||
|
if not self.kernel_cmd:
|
||||||
|
# If kernel_cmd has been set manually, don't refer to a kernel spec
|
||||||
|
# Environment variables from kernel spec are added to os.environ
|
||||||
|
env.update(self.kernel_spec.env or {})
|
||||||
|
|
||||||
|
# launch the kernel subprocess
|
||||||
|
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
|
||||||
|
cutter_internal.launch_ipykernel(kernel_cmd, env=env, **kw)
|
||||||
|
self.kernel = IPyKernelInterfaceJupyter()
|
||||||
|
# self._launch_kernel(kernel_cmd, env=env,
|
||||||
|
# **kw)
|
||||||
|
|
||||||
|
self.start_restarter()
|
||||||
|
self._connect_control_socket()
|
||||||
|
|
||||||
|
|
||||||
|
def kernel_manager_factory(kernel_name, **kwargs):
|
||||||
|
if kernel_name in {"python", "python2", "python3"}:
|
||||||
|
return CutterInternalIPyKernelManager(kernel_name=kernel_name, **kwargs)
|
||||||
|
else:
|
||||||
|
return IOLoopKernelManager(kernel_name=kernel_name, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CutterNotebookApp(NotebookApp):
|
class CutterNotebookApp(NotebookApp):
|
||||||
@ -13,6 +69,8 @@ class CutterNotebookApp(NotebookApp):
|
|||||||
def start(self):
|
def start(self):
|
||||||
""" see NotebookApp.start() """
|
""" see NotebookApp.start() """
|
||||||
|
|
||||||
|
self.kernel_manager.kernel_manager_factory = kernel_manager_factory
|
||||||
|
|
||||||
super(NotebookApp, self).start()
|
super(NotebookApp, self).start()
|
||||||
|
|
||||||
self.write_server_info_file()
|
self.write_server_info_file()
|
||||||
@ -48,7 +106,9 @@ def start_jupyter():
|
|||||||
app = CutterNotebookApp()
|
app = CutterNotebookApp()
|
||||||
app.initialize()
|
app.initialize()
|
||||||
app.start()
|
app.start()
|
||||||
print('TODO: Export cutter bindings to any kernel')
|
|
||||||
print(cutter.version())
|
|
||||||
print(cutter.cmd('?e That is executed from radare2'))
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = start_jupyter()
|
||||||
|
print("Started " + app.url_with_token)
|
||||||
|
@ -62,5 +62,6 @@
|
|||||||
<file>img/cutter.svg</file>
|
<file>img/cutter.svg</file>
|
||||||
|
|
||||||
<file>python/cutter_jupyter.py</file>
|
<file>python/cutter_jupyter.py</file>
|
||||||
|
<file>python/cutter_ipykernel.py</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -32,6 +32,7 @@ JupyterConnection::~JupyterConnection()
|
|||||||
void JupyterConnection::start()
|
void JupyterConnection::start()
|
||||||
{
|
{
|
||||||
PyImport_AppendInittab("cutter", &PyInit_api);
|
PyImport_AppendInittab("cutter", &PyInit_api);
|
||||||
|
PyImport_AppendInittab("cutter_internal", &PyInit_api_internal);
|
||||||
Py_Initialize();
|
Py_Initialize();
|
||||||
PyEval_InitThreads();
|
PyEval_InitThreads();
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
#include "PythonAPI.h"
|
#include "PythonAPI.h"
|
||||||
#include "cutter.h"
|
#include "cutter.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
/* Return the number of arguments of the application command line */
|
/* Return the number of arguments of the application command line */
|
||||||
PyObject *api_version(PyObject *self, PyObject *null)
|
PyObject *api_version(PyObject *self, PyObject *null)
|
||||||
{
|
{
|
||||||
@ -34,8 +37,92 @@ PyModuleDef CutterModule = {
|
|||||||
NULL, NULL, NULL, NULL
|
NULL, NULL, NULL, NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
PyObject *PyInit_api()
|
PyObject *PyInit_api()
|
||||||
{
|
{
|
||||||
return PyModule_Create(&CutterModule);
|
return PyModule_Create(&CutterModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *api_internal_launch_ipykernel(PyObject *self, PyObject *args, PyObject *kw)
|
||||||
|
{
|
||||||
|
QStringList argv;
|
||||||
|
|
||||||
|
{
|
||||||
|
PyObject *argvListObject;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &argvListObject)
|
||||||
|
|| !PyList_Check(argvListObject))
|
||||||
|
{
|
||||||
|
qWarning() << "Invalid args passed to api_internal_launch_ipykernel().";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<PyList_Size(argvListObject); i++)
|
||||||
|
{
|
||||||
|
PyObject *o = PyList_GetItem(argvListObject, i);
|
||||||
|
QString s = QString::fromUtf8(PyUnicode_AsUTF8(o));
|
||||||
|
argv.append(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyThreadState *parentThreadState = PyThreadState_Get();
|
||||||
|
|
||||||
|
PyThreadState *threadState = Py_NewInterpreter();
|
||||||
|
|
||||||
|
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 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto ipyKernel = PyObject_CallFunction(launchFunc, "O", argvListObject);
|
||||||
|
|
||||||
|
PyThreadState_Swap(parentThreadState);
|
||||||
|
|
||||||
|
return PyLong_FromLong(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyMethodDef CutterInternalMethods[] = {
|
||||||
|
{"launch_ipykernel", (PyCFunction)api_internal_launch_ipykernel, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
"Launch an IPython Kernel in a subinterpreter"},
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
PyModuleDef CutterInternalModule = {
|
||||||
|
PyModuleDef_HEAD_INIT, "cutter_internal", NULL, -1, CutterInternalMethods,
|
||||||
|
NULL, NULL, NULL, NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *PyInit_api_internal()
|
||||||
|
{
|
||||||
|
return PyModule_Create(&CutterInternalModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#define PYTHONAPI_H
|
#define PYTHONAPI_H
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
|
||||||
PyObject *PyInit_api();
|
PyObject *PyInit_api();
|
||||||
|
PyObject *PyInit_api_internal();
|
||||||
|
|
||||||
#endif // PYTHONAPI_H
|
#endif // PYTHONAPI_H
|
||||||
|
Loading…
Reference in New Issue
Block a user