Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
First implementation of find_module and load_module so python module …
…can be imported from pycode with import. That s works in progress fullpath aren t managed, and bytecode py[c|o] file can t be used. The path finder should be written and not hardcoded with ":/"+module_name+".py"
  • Loading branch information
Benoît HERVIER committed Oct 16, 2013
1 parent 87e8ab8 commit cc924d3
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 73 deletions.
10 changes: 10 additions & 0 deletions python.pri
@@ -1,4 +1,14 @@
# Link against Python 3.3
PYTHON_CONFIG = python3.3-config

unix: {
QMAKE_LIBS += $$system($$PYTHON_CONFIG --ldflags)
QMAKE_CXXFLAGS += $$system($$PYTHON_CONFIG --includes)
}

win32: {
INCLUDEPATH += C:\Python27\include
LIBS += -LC:\Python27\libs -lpython27
}

#RESOURCES +=
12 changes: 3 additions & 9 deletions src/qpython.cpp
Expand Up @@ -24,7 +24,6 @@

#include <QDebug>
#include <QJSEngine>
#include <QFile>

QPythonPriv *
QPython::priv = NULL;
Expand Down Expand Up @@ -85,12 +84,6 @@ QPython::importModule(QString name, QJSValue callback)
emit import(name, callback);
}

void
QPython::importModuleFromResources(QString path, QString name, QJSValue callback)
{
emit importFromResources(name, path, callback);
}

bool
QPython::importModule_sync(QString name)
{
Expand All @@ -114,7 +107,8 @@ QPython::importModule_sync(QString name)
return true;
}

bool

/*bool
QPython::importModuleFromResources_sync(QString path, QString name)
{
// Lesson learned: name.toUtf8().constData() doesn't work, as the
Expand Down Expand Up @@ -145,7 +139,7 @@ QPython::importModuleFromResources_sync(QString path, QString name)
PyDict_SetItemString(priv->globals, moduleName, module);
priv->leave();
return true;
}
}*/

void
QPython::receive(QVariant variant)
Expand Down
54 changes: 0 additions & 54 deletions src/qpython.h
Expand Up @@ -126,59 +126,6 @@ class QPython : public QObject {
Q_INVOKABLE QVariant
evaluate(QString expr);

/**
* \brief Asynchronously import a Python module
*
* Imports a Python module by name asynchronously from Qt Resources.
* The function will return immediately. If the module is successfully
* imported, the supplied \a callback will be called. Only then will the
* imported module be available:
*
* \code
* Python {
* Component.onCompleted: {
* importModuleFromResources(':/pyotherside.py', 'pyotherside', function() {
* // You can use the "os" module here
* });
* }
* }
* \endcode
*
* If an error occurs while trying to import, the signal error()
* will be emitted with detailed information about the error.
*
* \arg name The name of the Python module to import
* \arg callback The JS callback to be called when the module is
* successfully imported
**/
Q_INVOKABLE void
importModuleFromResources(QString path, QString name, QJSValue callback);

/**
* \brief Synchronously import a Python module from Qt Resources
*
* Imports a Python module by name synchronously. This function
* will block until the module is imported and available. In
* general, you should use importModule() instead of this function
* to avoid blocking the QML UI thread. Example use:
*
* \code
* Python {
* Component.onCompleted: {
* var success = importModuleFromResources_sync(':/pyotherside.py', 'pyotherside');
* if (success) {
* // You can use the "pyotherside" module here
* }
* }
* }
* \endcode
*
* \arg name The name of the Python module to import
* \result \c true if the import was successful, \c false otherwise
**/
Q_INVOKABLE bool
importModuleFromResources_sync(QString path, QString name);

/**
* \brief Asynchronously import a Python module
*
Expand Down Expand Up @@ -315,7 +262,6 @@ class QPython : public QObject {
/* For internal use only */
void process(QString func, QVariant args, QJSValue callback);
void import(QString name, QJSValue callback);
void importFromResources(QString name, QString path, QJSValue callback);

private slots:
void receive(QVariant data);
Expand Down
95 changes: 93 additions & 2 deletions src/qpython_priv.cpp
Expand Up @@ -17,10 +17,9 @@
**/

#include "qml_python_bridge.h"

#include "qpython_priv.h"

#include <QImage>
#include <QFile>

static QPythonPriv *priv = NULL;

Expand Down Expand Up @@ -57,10 +56,91 @@ pyotherside_set_image_provider(PyObject *self, PyObject *o)
Py_RETURN_NONE;
}

PyObject *
pyotherside_find_module(PyObject *self, PyObject *args) {

char *fullname, *path;
int err = PyArg_ParseTuple(args, "s|z", &fullname, &path);

if(err == 0)
{
PyObject_Print(PyErr_Occurred(), stdout, Py_PRINT_RAW);
PySys_WriteStdout("\n");
PyErr_Print();
PySys_WriteStdout("\n");
}

QString filename(fullname);
filename = ":/"+filename+".py";
QFile file(filename);

if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return Py_None;
} else {
return self;
}
}

PyObject *
pyotherside_load_module(PyObject *self, PyObject *args) {

qDebug() << "pyotherside_load_module called";

const char *module_source;
char *fullname;
PyArg_ParseTuple(args, "s", &fullname);
PyObject *mod, *dict;
PyObject *module_code;

mod = PyImport_AddModule(fullname);
if (mod == NULL) {
return NULL;
}

QString filename(fullname);
filename = ":/"+filename+".py";
QFile moduleContent(filename);

if(moduleContent.open(QIODevice::ReadOnly | QIODevice::Text)) {
module_source = moduleContent.readAll().constData();

//Next, we grab the reference to the new module's __dict__

if(module_source == NULL) {
//We couldnt load the module. Raise ImportError
return PyExc_ImportError;
}

module_code = Py_CompileString(module_source, fullname, Py_file_input);
//PyObject *new_module_dict = PyModule_GetDict(new_mod);


/* mod.__loader__ = self */
dict = PyModule_GetDict(mod);
if (PyDict_SetItemString(dict, "__loader__", (PyObject *)self) != 0)
return PyExc_ImportError;

/*
* This is really important. the second arg should be Py_file_input because we need
* the interpreter to believe is a file, and accept multiple statements in multiple lines.
* Else, the plug-in should throw a SegFault.
*/

/* Now eval in context with new_mod.__dict__ in both globals and locals;
* The following (I believe) would be the translation in C of the
* exec CODE in mod.__dict__
*/
mod = PyImport_ExecCodeModuleEx(fullname, module_code, fullname);
return mod;
}
}

static PyMethodDef PyOtherSideMethods[] = {
{"send", pyotherside_send, METH_VARARGS, "Send data to Qt."},
{"atexit", pyotherside_atexit, METH_O, "Function to call on shutdown."},
{"set_image_provider", pyotherside_set_image_provider, METH_O, "Set the QML image provider."},
{"find_module", pyotherside_find_module, METH_VARARGS},
{"load_module", pyotherside_load_module, METH_VARARGS},
{NULL, NULL, 0, NULL},
};

Expand Down Expand Up @@ -93,6 +173,17 @@ PyOtherSide_init()
// Custom constant - pixels are to be interpreted as encoded image file data
PyModule_AddIntConstant(pyotherside, "format_data", -1);

PyImport_ImportModule("sys");

PyObject *meta_path = PySys_GetObject("meta_path");
if (meta_path != NULL)
{
PyList_Append(meta_path, pyotherside);
}

//TODO


return pyotherside;
}
#endif
Expand Down
7 changes: 0 additions & 7 deletions src/qpython_worker.cpp
Expand Up @@ -44,10 +44,3 @@ QPythonWorker::import(QString name, QJSValue callback)
bool result = qpython->importModule_sync(name);
emit imported(result, callback);
}

void
QPythonWorker::importFromResources(QString name, QString path, QJSValue callback)
{
bool result = qpython->importModuleFromResources_sync(name, path);
emit imported(result, callback);
}
1 change: 0 additions & 1 deletion src/qpython_worker.h
Expand Up @@ -36,7 +36,6 @@ class QPythonWorker : public QObject {
public slots:
void process(QString func, QVariant args, QJSValue callback);
void import(QString func, QJSValue callback);
void importFromResources(QString path, QString name, QJSValue callback);

signals:
void finished(QVariant result, QJSValue callback);
Expand Down
5 changes: 5 additions & 0 deletions tests/test.qrc
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>tests.py</file>
</qresource>
</RCC>
11 changes: 11 additions & 0 deletions tests/tests.cpp
Expand Up @@ -124,6 +124,17 @@ TestPyOtherSide::testConvertToPythonAndBack()
QVERIFY(v == v2);
}

void
TestPyOtherSide::testImport()
{
QPython py;
QVERIFY(true == py.importModule_sync(QString("pyotherside")));
QVERIFY(true == py.importModule_sync(QString("tests")));
QVERIFY(true == py.importModule_sync(QString("tests")));
QString echo("echo");
QVERIFY(echo == py.call_sync("tests.echo", QVariantList()));
}

void
TestPyOtherSide::testEvaluate()
{
Expand Down
1 change: 1 addition & 0 deletions tests/tests.h
Expand Up @@ -32,6 +32,7 @@ class TestPyOtherSide : public QObject {

private slots:
void testEvaluate();
void testImport();
void testQVariantConverter();
void testPyObjectConverter();
void testConvertToPythonAndBack();
Expand Down
6 changes: 6 additions & 0 deletions tests/tests.pro
Expand Up @@ -19,3 +19,9 @@ DEPENDPATH += . ../src
INCLUDEPATH += . ../src

include(../python.pri)

RESOURCES += \
test.qrc

OTHER_FILES += \
tests.py
5 changes: 5 additions & 0 deletions tests/tests.py
@@ -0,0 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

def echo():
return 'echo'

0 comments on commit cc924d3

Please sign in to comment.