1
0
mirror of https://github.com/thp/pyotherside.git synced 2025-01-17 23:22:53 +08:00

Merge pull request #54 from xealits/importNames

Import names from module
This commit is contained in:
Thomas Perl 2016-01-08 15:23:19 +01:00
commit 066fbb5a51
4 changed files with 133 additions and 0 deletions

View File

@ -62,6 +62,8 @@ QPython::QPython(QObject *parent, int api_version_major, int api_version_minor)
QObject::connect(this, SIGNAL(import(QString,QJSValue *)),
worker, SLOT(import(QString,QJSValue *)));
QObject::connect(this, SIGNAL(import_names(QString, QVariant, QJSValue *)),
worker, SLOT(import_names(QString, QVariant, QJSValue *)));
QObject::connect(worker, SIGNAL(imported(bool,QJSValue *)),
this, SLOT(imported(bool,QJSValue *)));
@ -126,6 +128,67 @@ QPython::addImportPath(QString path)
PyList_Insert(sys_path, 0, cwd.borrow());
}
void
QPython::importNames(QString name, QVariant args, QJSValue callback)
{
QJSValue *cb = 0;
if (!callback.isNull() && !callback.isUndefined() && callback.isCallable()) {
cb = new QJSValue(callback);
}
emit import_names(name, args, cb);
}
bool
QPython::importNames_sync(QString module_name, QVariant args)
{
// The plan is to "from module_name import a, b, c". And args is the list with a, b, c.
// The module_name can be a packaged module "x.y.z" -- "from x.y.z import a, b, c".
// Thus:
// - import the module, given by module_name,
// - get the objects from the module, given by names in args,
// - put the objects into globals of priv
QByteArray utf8bytes = module_name.toUtf8();
const char *moduleName = utf8bytes.constData();
ENSURE_GIL_STATE;
// PyOtherSide API 1.2 behavior: "import x.y.z" -- where the module 'z' is needed
PyObjectRef module = PyObjectRef(PyImport_ImportModule(moduleName), true);
if (!module) {
emitError(QString("Cannot import module: %1 (%2)").arg(module_name).arg(priv->formatExc()));
return false;
}
// at this point the module with the target objects is in PyObjectRef module,
// it should be well imported in Python
// Get the names of functions/objects to import
QVariantList vl = args.toList();
QString obj_name; // object name to import
PyObjectRef result; // the object, obtained from globals_temp
// for each object name try to get it from the module
// - on success put it into priv.globals
// - on failure emit the error and continue
for (QVariantList::const_iterator obj = vl.begin(); obj != vl.end(); ++obj)
{
obj_name = obj->toString();
utf8bytes = obj_name.toUtf8();
PyObject *res = PyObject_GetAttrString(module.borrow(), utf8bytes);
result = PyObjectRef(res, true);
if (!result) {
emitError(QString("Object '%1' is not found in '%2': (%3)").arg(obj_name).arg(module_name).arg(priv->formatExc()));
continue;
}
PyDict_SetItemString(priv->globals.borrow(), utf8bytes.constData(), result.borrow());
}
return true;
}
void
QPython::importModule(QString name, QJSValue callback)
{

View File

@ -130,6 +130,65 @@ class QPython : public QObject {
Q_INVOKABLE QVariant
evaluate(QString expr);
/**
* \brief Asynchronously import objects from Python module
*
* Imports objects, given by list of names, from Python module asynchronously.
* 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: {
* importNames('os', ['path'], function (success) {
* if (success) {
* // You can use the "path" submodule here
* } else {
* console.log('Importing failed')
* }
* });
* }
* }
* \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 from
* \arg args The name of Python objects to import from the module
* \arg callback The JS callback to be called when the module is
* successfully imported
**/
Q_INVOKABLE void
importNames(QString name, QVariant args, QJSValue callback);
/**
* \brief Synchronously import objects from Python module
*
* Imports objects, given by list of names, from Python module synchronously.
* This function will block until the objects are imported and available.
* In general, you should use importNames() instead of this function
* to avoid blocking the QML UI thread. Example use:
*
* \code
* Python {
* Component.onCompleted: {
* var success = importNames_sync('os', ['path']);
* if (success) {
* // You can use the "path" submodule here
* }
* }
* }
* \endcode
*
* \arg name The name of the Python module to import from
* \arg args The name of Python objects to import from the module
* \result \c true if the import was successful, \c false otherwise
**/
Q_INVOKABLE bool
importNames_sync(QString name, QVariant args);
/**
* \brief Asynchronously import a Python module
*
@ -304,6 +363,7 @@ class QPython : public QObject {
/* For internal use only */
void process(QVariant func, QVariant args, QJSValue *callback);
void import(QString name, QJSValue *callback);
void import_names(QString name, QVariant args, QJSValue *callback);
private slots:
void receive(QVariant data);

View File

@ -48,3 +48,12 @@ QPythonWorker::import(QString name, QJSValue *callback)
emit imported(result, callback);
}
}
void
QPythonWorker::import_names(QString name, QVariant args, QJSValue *callback)
{
bool result = qpython->importNames_sync(name, args);
if (callback) {
emit imported(result, callback); // using the same imported signal at the end
}
}

View File

@ -36,6 +36,7 @@ class QPythonWorker : public QObject {
public slots:
void process(QVariant func, QVariant args, QJSValue *callback);
void import(QString func, QJSValue *callback);
void import_names(QString func, QVariant args, QJSValue *callback);
signals:
void finished(QVariant result, QJSValue *callback);