mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-20 20:06:12 +00:00
Wrap Python API
This commit is contained in:
parent
9aec148218
commit
8d92f92025
@ -218,7 +218,8 @@ SOURCES += \
|
||||
dialogs/EditFunctionDialog.cpp \
|
||||
widgets/CutterTreeView.cpp \
|
||||
widgets/ComboQuickFilterView.cpp \
|
||||
dialogs/HexdumpRangeDialog.cpp
|
||||
dialogs/HexdumpRangeDialog.cpp \
|
||||
common/QtResImporter.cpp
|
||||
|
||||
HEADERS += \
|
||||
Cutter.h \
|
||||
@ -320,7 +321,8 @@ HEADERS += \
|
||||
dialogs/EditFunctionDialog.h \
|
||||
widgets/CutterTreeView.h \
|
||||
widgets/ComboQuickFilterView.h \
|
||||
dialogs/HexdumpRangeDialog.h
|
||||
dialogs/HexdumpRangeDialog.h \
|
||||
common/QtResImporter.h
|
||||
|
||||
FORMS += \
|
||||
dialogs/AboutDialog.ui \
|
||||
|
@ -1,19 +1,18 @@
|
||||
#ifdef CUTTER_ENABLE_JUPYTER
|
||||
|
||||
#include <Python.h>
|
||||
#include <marshal.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
#include <QFile>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
|
||||
#include "JupyterConnection.h"
|
||||
#include "NestedIPyKernel.h"
|
||||
#include "QtResImporter.h"
|
||||
#include "PythonAPI.h"
|
||||
|
||||
Q_GLOBAL_STATIC(JupyterConnection, uniqueInstance)
|
||||
@ -79,8 +78,9 @@ void JupyterConnection::initPythonHome()
|
||||
|
||||
void JupyterConnection::initPython()
|
||||
{
|
||||
PyImport_AppendInittab("cutter", &PyInit_api);
|
||||
PyImport_AppendInittab("_cutter", &PyInit_api);
|
||||
PyImport_AppendInittab("cutter_internal", &PyInit_api_internal);
|
||||
PyImport_AppendInittab("_qtres", &PyInit_qtres);
|
||||
Py_Initialize();
|
||||
PyEval_InitThreads();
|
||||
|
||||
@ -93,31 +93,7 @@ void JupyterConnection::createCutterJupyterModule()
|
||||
PyEval_RestoreThread(pyThreadState);
|
||||
}
|
||||
|
||||
QFile moduleFile(":/python/cutter_jupyter.pyc");
|
||||
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);
|
||||
cutterJupyterModule = QtResImport("cutter_jupyter");
|
||||
if (!cutterJupyterModule) {
|
||||
PyErr_Print();
|
||||
qWarning() << "Could not import cutter_jupyter.";
|
||||
@ -125,7 +101,6 @@ void JupyterConnection::createCutterJupyterModule()
|
||||
pyThreadState = PyEval_SaveThread();
|
||||
return;
|
||||
}
|
||||
Py_DECREF(moduleCodeObject);
|
||||
|
||||
pyThreadState = PyEval_SaveThread();
|
||||
}
|
||||
|
@ -2,13 +2,11 @@
|
||||
#ifdef CUTTER_ENABLE_JUPYTER
|
||||
|
||||
#include <Python.h>
|
||||
#include <marshal.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <csignal>
|
||||
|
||||
#include "Cutter.h"
|
||||
#include "NestedIPyKernel.h"
|
||||
#include "QtResImporter.h"
|
||||
|
||||
NestedIPyKernel *NestedIPyKernel::start(const QStringList &argv)
|
||||
{
|
||||
@ -20,29 +18,8 @@ NestedIPyKernel *NestedIPyKernel::start(const QStringList &argv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QFile moduleFile(":/python/cutter_ipykernel.pyc");
|
||||
bool isBytecode = moduleFile.exists();
|
||||
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);
|
||||
RegQtResImporter();
|
||||
auto cutterIPykernelModule = QtResImport("cutter_ipykernel");
|
||||
if (!cutterIPykernelModule) {
|
||||
qWarning() << "Could not import cutter_ipykernel.";
|
||||
return nullptr;
|
||||
|
@ -32,28 +32,6 @@ PyObject *api_cmd(PyObject *self, PyObject *args)
|
||||
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)
|
||||
{
|
||||
Q_UNUSED(self);
|
||||
@ -87,10 +65,6 @@ PyMethodDef CutterMethods[] = {
|
||||
"cmd", api_cmd, METH_VARARGS,
|
||||
"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 Cutter widgets"
|
||||
@ -103,7 +77,7 @@ PyMethodDef CutterMethods[] = {
|
||||
};
|
||||
|
||||
PyModuleDef CutterModule = {
|
||||
PyModuleDef_HEAD_INIT, "cutter", NULL, -1, CutterMethods,
|
||||
PyModuleDef_HEAD_INIT, "_cutter", NULL, -1, CutterMethods,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
@ -112,7 +86,6 @@ PyObject *PyInit_api()
|
||||
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/delete.svg</file>
|
||||
<file>img/icons/previous.svg</file>
|
||||
<file>python/cutter.py</file>
|
||||
<file>python/reg_qtres_importer.py</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
Loading…
Reference in New Issue
Block a user