mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-30 08:45:25 +00:00
Wrap Python API
This commit is contained in:
parent
9aec148218
commit
8d92f92025
@ -218,7 +218,8 @@ SOURCES += \
|
|||||||
dialogs/EditFunctionDialog.cpp \
|
dialogs/EditFunctionDialog.cpp \
|
||||||
widgets/CutterTreeView.cpp \
|
widgets/CutterTreeView.cpp \
|
||||||
widgets/ComboQuickFilterView.cpp \
|
widgets/ComboQuickFilterView.cpp \
|
||||||
dialogs/HexdumpRangeDialog.cpp
|
dialogs/HexdumpRangeDialog.cpp \
|
||||||
|
common/QtResImporter.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
Cutter.h \
|
Cutter.h \
|
||||||
@ -320,7 +321,8 @@ HEADERS += \
|
|||||||
dialogs/EditFunctionDialog.h \
|
dialogs/EditFunctionDialog.h \
|
||||||
widgets/CutterTreeView.h \
|
widgets/CutterTreeView.h \
|
||||||
widgets/ComboQuickFilterView.h \
|
widgets/ComboQuickFilterView.h \
|
||||||
dialogs/HexdumpRangeDialog.h
|
dialogs/HexdumpRangeDialog.h \
|
||||||
|
common/QtResImporter.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
dialogs/AboutDialog.ui \
|
dialogs/AboutDialog.ui \
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
#ifdef CUTTER_ENABLE_JUPYTER
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <marshal.h>
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QFile>
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
#include "JupyterConnection.h"
|
#include "JupyterConnection.h"
|
||||||
#include "NestedIPyKernel.h"
|
#include "NestedIPyKernel.h"
|
||||||
|
#include "QtResImporter.h"
|
||||||
#include "PythonAPI.h"
|
#include "PythonAPI.h"
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(JupyterConnection, uniqueInstance)
|
Q_GLOBAL_STATIC(JupyterConnection, uniqueInstance)
|
||||||
@ -79,8 +78,9 @@ void JupyterConnection::initPythonHome()
|
|||||||
|
|
||||||
void JupyterConnection::initPython()
|
void JupyterConnection::initPython()
|
||||||
{
|
{
|
||||||
PyImport_AppendInittab("cutter", &PyInit_api);
|
PyImport_AppendInittab("_cutter", &PyInit_api);
|
||||||
PyImport_AppendInittab("cutter_internal", &PyInit_api_internal);
|
PyImport_AppendInittab("cutter_internal", &PyInit_api_internal);
|
||||||
|
PyImport_AppendInittab("_qtres", &PyInit_qtres);
|
||||||
Py_Initialize();
|
Py_Initialize();
|
||||||
PyEval_InitThreads();
|
PyEval_InitThreads();
|
||||||
|
|
||||||
@ -93,31 +93,7 @@ void JupyterConnection::createCutterJupyterModule()
|
|||||||
PyEval_RestoreThread(pyThreadState);
|
PyEval_RestoreThread(pyThreadState);
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile moduleFile(":/python/cutter_jupyter.pyc");
|
cutterJupyterModule = QtResImport("cutter_jupyter");
|
||||||
bool isBytecode = moduleFile.exists();
|
|
||||||
if (!isBytecode) {
|
|
||||||
moduleFile.setFileName(":/python/cutter_jupyter.py");
|
|
||||||
}
|
|
||||||
moduleFile.open(QIODevice::ReadOnly);
|
|
||||||
QByteArray moduleCode = moduleFile.readAll();
|
|
||||||
moduleFile.close();
|
|
||||||
|
|
||||||
PyObject *moduleCodeObject;
|
|
||||||
if (isBytecode) {
|
|
||||||
moduleCodeObject = PyMarshal_ReadObjectFromString(moduleCode.constData() + 12,
|
|
||||||
moduleCode.size() - 12);
|
|
||||||
} else {
|
|
||||||
moduleCodeObject = Py_CompileString(moduleCode.constData(), "cutter_jupyter.py",
|
|
||||||
Py_file_input);
|
|
||||||
}
|
|
||||||
if (!moduleCodeObject) {
|
|
||||||
PyErr_Print();
|
|
||||||
qWarning() << "Could not compile cutter_jupyter.";
|
|
||||||
emit creationFailed();
|
|
||||||
pyThreadState = PyEval_SaveThread();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cutterJupyterModule = PyImport_ExecCodeModule("cutter_jupyter", moduleCodeObject);
|
|
||||||
if (!cutterJupyterModule) {
|
if (!cutterJupyterModule) {
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
qWarning() << "Could not import cutter_jupyter.";
|
qWarning() << "Could not import cutter_jupyter.";
|
||||||
@ -125,7 +101,6 @@ void JupyterConnection::createCutterJupyterModule()
|
|||||||
pyThreadState = PyEval_SaveThread();
|
pyThreadState = PyEval_SaveThread();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Py_DECREF(moduleCodeObject);
|
|
||||||
|
|
||||||
pyThreadState = PyEval_SaveThread();
|
pyThreadState = PyEval_SaveThread();
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,11 @@
|
|||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
#ifdef CUTTER_ENABLE_JUPYTER
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <marshal.h>
|
|
||||||
|
|
||||||
#include <QFile>
|
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
|
||||||
#include "Cutter.h"
|
#include "Cutter.h"
|
||||||
#include "NestedIPyKernel.h"
|
#include "NestedIPyKernel.h"
|
||||||
|
#include "QtResImporter.h"
|
||||||
|
|
||||||
NestedIPyKernel *NestedIPyKernel::start(const QStringList &argv)
|
NestedIPyKernel *NestedIPyKernel::start(const QStringList &argv)
|
||||||
{
|
{
|
||||||
@ -20,29 +18,8 @@ NestedIPyKernel *NestedIPyKernel::start(const QStringList &argv)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile moduleFile(":/python/cutter_ipykernel.pyc");
|
RegQtResImporter();
|
||||||
bool isBytecode = moduleFile.exists();
|
auto cutterIPykernelModule = QtResImport("cutter_ipykernel");
|
||||||
if (!isBytecode) {
|
|
||||||
moduleFile.setFileName(":/python/cutter_ipykernel.py");
|
|
||||||
}
|
|
||||||
moduleFile.open(QIODevice::ReadOnly);
|
|
||||||
QByteArray moduleCode = moduleFile.readAll();
|
|
||||||
moduleFile.close();
|
|
||||||
|
|
||||||
PyObject *moduleCodeObject;
|
|
||||||
if (isBytecode) {
|
|
||||||
moduleCodeObject = PyMarshal_ReadObjectFromString(moduleCode.constData() + 12,
|
|
||||||
moduleCode.size() - 12);
|
|
||||||
} else {
|
|
||||||
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) {
|
if (!cutterIPykernelModule) {
|
||||||
qWarning() << "Could not import cutter_ipykernel.";
|
qWarning() << "Could not import cutter_ipykernel.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -32,28 +32,6 @@ PyObject *api_cmd(PyObject *self, PyObject *args)
|
|||||||
return PyUnicode_FromString(result);
|
return PyUnicode_FromString(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *api_cmdj(PyObject *self, PyObject *args)
|
|
||||||
{
|
|
||||||
Q_UNUSED(self);
|
|
||||||
char *command;
|
|
||||||
char *result = (char *) "";
|
|
||||||
QString cmdRes;
|
|
||||||
QByteArray cmdBytes;
|
|
||||||
if (PyArg_ParseTuple(args, "s:command", &command)) {
|
|
||||||
cmdRes = Core()->cmd(command);
|
|
||||||
cmdBytes = cmdRes.toLocal8Bit();
|
|
||||||
result = cmdBytes.data();
|
|
||||||
PyObject *jsonModule = PyImport_ImportModule("json");
|
|
||||||
PyObject *loadsFunc = PyObject_GetAttrString(jsonModule, "loads");
|
|
||||||
if (!PyCallable_Check(loadsFunc)) {
|
|
||||||
PyErr_SetString(PyExc_SystemError, "Could not parse JSON.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return PyEval_CallFunction(loadsFunc, "(s)", result);
|
|
||||||
}
|
|
||||||
return Py_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject *api_refresh(PyObject *self, PyObject *args)
|
PyObject *api_refresh(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
Q_UNUSED(self);
|
Q_UNUSED(self);
|
||||||
@ -87,10 +65,6 @@ PyMethodDef CutterMethods[] = {
|
|||||||
"cmd", api_cmd, METH_VARARGS,
|
"cmd", api_cmd, METH_VARARGS,
|
||||||
"Execute a command inside Cutter"
|
"Execute a command inside Cutter"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"cmdj", api_cmdj, METH_VARARGS,
|
|
||||||
"Execute a JSON command and return the result as a dictionnary"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"refresh", api_refresh, METH_NOARGS,
|
"refresh", api_refresh, METH_NOARGS,
|
||||||
"Refresh Cutter widgets"
|
"Refresh Cutter widgets"
|
||||||
@ -103,7 +77,7 @@ PyMethodDef CutterMethods[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PyModuleDef CutterModule = {
|
PyModuleDef CutterModule = {
|
||||||
PyModuleDef_HEAD_INIT, "cutter", NULL, -1, CutterMethods,
|
PyModuleDef_HEAD_INIT, "_cutter", NULL, -1, CutterMethods,
|
||||||
NULL, NULL, NULL, NULL
|
NULL, NULL, NULL, NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,7 +86,6 @@ PyObject *PyInit_api()
|
|||||||
return PyModule_Create(&CutterModule);
|
return PyModule_Create(&CutterModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
|
|
||||||
|
|
||||||
|
99
src/common/QtResImporter.cpp
Normal file
99
src/common/QtResImporter.cpp
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#include <Python.h>
|
||||||
|
#include <marshal.h>
|
||||||
|
|
||||||
|
#include "QtResImporter.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
int QtResExists(const char *name, QFile &file)
|
||||||
|
{
|
||||||
|
QString fname = QString::asprintf(":/python/%s.py", name);
|
||||||
|
file.setFileName(fname);
|
||||||
|
if (file.exists())
|
||||||
|
return 1;
|
||||||
|
fname.append('c');
|
||||||
|
file.setFileName(fname);
|
||||||
|
if (file.exists())
|
||||||
|
return 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *QtResGetCode(const char *name)
|
||||||
|
{
|
||||||
|
QFile moduleFile;
|
||||||
|
bool isBytecode = false;
|
||||||
|
|
||||||
|
switch (QtResExists(name, moduleFile)) {
|
||||||
|
case 0:
|
||||||
|
return nullptr;
|
||||||
|
case 2:
|
||||||
|
isBytecode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleFile.open(QIODevice::ReadOnly);
|
||||||
|
QByteArray data = moduleFile.readAll();
|
||||||
|
moduleFile.close();
|
||||||
|
|
||||||
|
PyObject *codeObject;
|
||||||
|
if (isBytecode) {
|
||||||
|
codeObject = PyMarshal_ReadObjectFromString(data.constData() + 12,
|
||||||
|
data.size() - 12);
|
||||||
|
} else {
|
||||||
|
codeObject = Py_CompileString(data.constData(),
|
||||||
|
moduleFile.fileName().toLocal8Bit().constData(),
|
||||||
|
Py_file_input);
|
||||||
|
}
|
||||||
|
if (!codeObject) {
|
||||||
|
qWarning() << "Couldn't unmarshal/compile " << moduleFile.fileName();
|
||||||
|
}
|
||||||
|
return codeObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *QtResImport(const char *name)
|
||||||
|
{
|
||||||
|
PyObject *codeObject = QtResGetCode(name);
|
||||||
|
if (!codeObject)
|
||||||
|
return nullptr;
|
||||||
|
PyObject *module = PyImport_ExecCodeModule(name, codeObject);
|
||||||
|
Py_DECREF(codeObject);
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *qtres_exists(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
Q_UNUSED(self)
|
||||||
|
char *name;
|
||||||
|
QFile resFile;
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &name))
|
||||||
|
return NULL;
|
||||||
|
return PyBool_FromLong(QtResExists(name, resFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *qtres_get_code(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
Q_UNUSED(self)
|
||||||
|
char *name;
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &name))
|
||||||
|
return NULL;
|
||||||
|
PyObject *ret = QtResGetCode(name);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyMethodDef QtResMethods[] = {
|
||||||
|
{ "exists", qtres_exists, METH_VARARGS, NULL },
|
||||||
|
{ "get_code", qtres_get_code, METH_VARARGS, NULL },
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
PyModuleDef QtResModule = {
|
||||||
|
PyModuleDef_HEAD_INIT, "_qtres", NULL, -1, QtResMethods,
|
||||||
|
NULL, NULL, NULL, NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
PyObject *PyInit_qtres()
|
||||||
|
{
|
||||||
|
return PyModule_Create(&QtResModule);
|
||||||
|
}
|
10
src/common/QtResImporter.h
Normal file
10
src/common/QtResImporter.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef QTRESIMPORTER_H
|
||||||
|
#define QTRESIMPORTER_H
|
||||||
|
|
||||||
|
PyObject *PyInit_qtres();
|
||||||
|
|
||||||
|
PyObject *QtResImport(const char *name);
|
||||||
|
|
||||||
|
#define RegQtResImporter() Py_DecRef(QtResImport("reg_qtres_importer"))
|
||||||
|
|
||||||
|
#endif // QTRESIMPORTER_H
|
6
src/python/cutter.py
Normal file
6
src/python/cutter.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import json
|
||||||
|
from _cutter import *
|
||||||
|
|
||||||
|
def cmdj(command):
|
||||||
|
'''Execute a JSON command and return the result as a dictionnary'''
|
||||||
|
return json.loads(cmd(command))
|
31
src/python/reg_qtres_importer.py
Normal file
31
src/python/reg_qtres_importer.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import sys
|
||||||
|
import importlib
|
||||||
|
import _qtres
|
||||||
|
|
||||||
|
class QtResLoader:
|
||||||
|
@classmethod
|
||||||
|
def get_code(cls, name):
|
||||||
|
return _qtres.get_code(name)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_module(cls, spec):
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def exec_module(cls, module):
|
||||||
|
code = cls.get_code(module.__name__)
|
||||||
|
if code is None:
|
||||||
|
raise ImportError("get_code() failed")
|
||||||
|
return
|
||||||
|
exec(code, module.__dict__)
|
||||||
|
|
||||||
|
class QtResFinder:
|
||||||
|
@classmethod
|
||||||
|
def find_spec(cls, fullname, path=None, target=None):
|
||||||
|
if path or target:
|
||||||
|
return None
|
||||||
|
if not _qtres.exists(fullname):
|
||||||
|
return None
|
||||||
|
return importlib._bootstrap.ModuleSpec(fullname, QtResLoader)
|
||||||
|
|
||||||
|
sys.meta_path.append(QtResFinder)
|
@ -82,5 +82,7 @@
|
|||||||
<file>img/icons/copy.svg</file>
|
<file>img/icons/copy.svg</file>
|
||||||
<file>img/icons/delete.svg</file>
|
<file>img/icons/delete.svg</file>
|
||||||
<file>img/icons/previous.svg</file>
|
<file>img/icons/previous.svg</file>
|
||||||
|
<file>python/cutter.py</file>
|
||||||
|
<file>python/reg_qtres_importer.py</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
Loading…
Reference in New Issue
Block a user