1
0
mirror of https://github.com/thp/pyotherside.git synced 2025-01-28 23:52:55 +08:00

Print unhandled errors to the console

This commit is contained in:
Thomas Perl 2014-10-12 11:24:54 +02:00
parent eccdad94ee
commit 21b416a01e
3 changed files with 65 additions and 12 deletions

View File

@ -63,6 +63,9 @@ io.thp.pyotherside 1.4
* :func:`call` and :func:`call_sync` now accept a Python callable object
for the first parameter (previously, only strings were supported)
* If :func:`error` doesn't have a handler defined, error messages will be
printed to the console as warnings
QML ``Python`` Element
----------------------
@ -90,6 +93,12 @@ Signals
Error handler for errors from Python.
.. versionchanged:: 1.4.0
If the error signal is not connected, PyOtherSide will print the
error as QWarning on the console (previously, error messages
were only shown if the signal was connected and printed there).
To avoid printing the error, just define a no-op handler.
Methods
```````
@ -985,6 +994,8 @@ Version 1.4.0 (UNRELEASED)
* :func:`call` and :func:`call_sync` now also accept a Python callable as
first argument
* Support for `Accessing QObjects from Python`_ (properties and slots)
* Print error messages to the console if :func:`error` doesn't have any
handlers connected
Version 1.3.0 (2014-07-24)
--------------------------

View File

@ -27,6 +27,7 @@
#include <QDebug>
#include <QJSEngine>
#include <QMetaMethod>
#define SINCE_API_VERSION(smaj, smin) \
@ -43,6 +44,7 @@ QPython::QPython(QObject *parent, int api_version_major, int api_version_minor)
, handlers()
, api_version_major(api_version_major)
, api_version_minor(api_version_minor)
, error_connections(0)
{
if (priv == NULL) {
priv = new QPythonPriv;
@ -75,6 +77,22 @@ QPython::~QPython()
delete worker;
}
void
QPython::connectNotify(const QMetaMethod &signal)
{
if (signal == QMetaMethod::fromSignal(&QPython::error)) {
error_connections++;
}
}
void
QPython::disconnectNotify(const QMetaMethod &signal)
{
if (signal == QMetaMethod::fromSignal(&QPython::error)) {
error_connections--;
}
}
void
QPython::addImportPath(QString path)
{
@ -96,7 +114,7 @@ QPython::addImportPath(QString path)
QString filename = "/io/thp/pyotherside/qrc_importer.py";
QString errorMessage = priv->importFromQRC(module, filename);
if (!errorMessage.isNull()) {
emit error(errorMessage);
emitError(errorMessage);
}
}
@ -145,7 +163,7 @@ QPython::importModule_sync(QString name)
}
if (!module) {
emit error(QString("Cannot import module: %1 (%2)").arg(name).arg(priv->formatExc()));
emitError(QString("Cannot import module: %1 (%2)").arg(name).arg(priv->formatExc()));
return false;
}
@ -182,7 +200,7 @@ QPython::receive(QVariant variant)
// call is asynchronous (it returns before we call into JS), so do
// the next best thing and report the error to our error handler in
// QML instead.
emit error("pyotherside.send() failed handler: " +
emitError("pyotherside.send() failed handler: " +
result.property("fileName").toString() + ":" +
result.property("lineNumber").toString() + ": " +
result.toString());
@ -210,7 +228,7 @@ QPython::evaluate(QString expr)
PyObjectRef o(priv->eval(expr), true);
if (!o) {
emit error(QString("Cannot evaluate '%1' (%2)").arg(expr).arg(priv->formatExc()));
emitError(QString("Cannot evaluate '%1' (%2)").arg(expr).arg(priv->formatExc()));
return QVariant();
}
@ -233,29 +251,34 @@ QPython::call_sync(QVariant func, QVariant args)
ENSURE_GIL_STATE;
PyObjectRef callable;
QString name;
if (SINCE_API_VERSION(1, 4)) {
if (static_cast<QMetaType::Type>(func.type()) == QMetaType::QString) {
// Using version >= 1.4, but func is a string
callable = PyObjectRef(priv->eval(func.toString()), true);
name = func.toString();
} else {
// Try to interpret "func" as a Python object
callable = PyObjectRef(convertQVariantToPyObject(func), true);
PyObjectRef repr = PyObjectRef(PyObject_Repr(callable.borrow()), true);
name = convertPyObjectToQVariant(repr.borrow()).toString();
}
} else {
// Versions before 1.4 only support func as a string
callable = PyObjectRef(priv->eval(func.toString()), true);
name = func.toString();
}
if (!callable) {
emit error(QString("Function not found: '%1' (%2)").arg(func.toString()).arg(priv->formatExc()));
emitError(QString("Function not found: '%1' (%2)").arg(name).arg(priv->formatExc()));
return QVariant();
}
QVariant v;
QString errorMessage = priv->call(callable.borrow(), func.toString(), args, &v);
QString errorMessage = priv->call(callable.borrow(), name, args, &v);
if (!errorMessage.isNull()) {
emit error(errorMessage);
emitError(errorMessage);
}
return v;
}
@ -263,7 +286,7 @@ QPython::call_sync(QVariant func, QVariant args)
QVariant
QPython::getattr(QVariant obj, QString attr) {
if (!SINCE_API_VERSION(1, 4)) {
emit error(QString("Import PyOtherSide 1.4 or newer to use getattr()"));
emitError(QString("Import PyOtherSide 1.4 or newer to use getattr()"));
return QVariant();
}
@ -272,7 +295,7 @@ QPython::getattr(QVariant obj, QString attr) {
PyObjectRef pyobj(convertQVariantToPyObject(obj), true);
if (!pyobj) {
emit error(QString("Failed to convert %1 to python object: '%1' (%2)").arg(obj.toString()).arg(priv->formatExc()));
emitError(QString("Failed to convert %1 to python object: '%1' (%2)").arg(obj.toString()).arg(priv->formatExc()));
return QVariant();
}
@ -282,7 +305,7 @@ QPython::getattr(QVariant obj, QString attr) {
PyObjectRef o(PyObject_GetAttrString(pyobj.borrow(), attrStr), true);
if (!o) {
emit error(QString("Attribute not found: '%1' (%2)").arg(attr).arg(priv->formatExc()));
emitError(QString("Attribute not found: '%1' (%2)").arg(attr).arg(priv->formatExc()));
return QVariant();
}
@ -298,7 +321,7 @@ QPython::finished(QVariant result, QJSValue *callback)
QJSValue callbackResult = callback->call(args);
if (SINCE_API_VERSION(1, 2)) {
if (callbackResult.isError()) {
emit error(callbackResult.property("fileName").toString() + ":" +
emitError(callbackResult.property("fileName").toString() + ":" +
callbackResult.property("lineNumber").toString() + ": " +
callbackResult.toString());
}
@ -315,7 +338,7 @@ QPython::imported(bool result, QJSValue *callback)
QJSValue callbackResult = callback->call(args);
if (SINCE_API_VERSION(1, 2)) {
if (callbackResult.isError()) {
emit error(callbackResult.property("fileName").toString() + ":" +
emitError(callbackResult.property("fileName").toString() + ":" +
callbackResult.property("lineNumber").toString() + ": " +
callbackResult.toString());
}
@ -334,3 +357,16 @@ QPython::pythonVersion()
{
return QString(PY_VERSION);
}
void
QPython::emitError(const QString &message)
{
if (error_connections) {
emit error(message);
} else {
// We should only print the error if SINCE_API_VERSION(1, 4), but as
// the error messages are useful for debugging (especially if users
// don't import the latest API version), we do it unconditionally
qWarning("Unhandled PyOtherSide error: %s", message.toUtf8().constData());
}
}

View File

@ -311,6 +311,9 @@ class QPython : public QObject {
void finished(QVariant result, QJSValue *callback);
void imported(bool result, QJSValue *callback);
void connectNotify(const QMetaMethod &signal);
void disconnectNotify(const QMetaMethod &signal);
private:
static QPythonPriv *priv;
@ -320,6 +323,9 @@ class QPython : public QObject {
int api_version_major;
int api_version_minor;
void emitError(const QString &message);
int error_connections;
};
class QPython10 : public QPython {