Beispiel #1
0
QVariant
QPython::getattr(QVariant obj, QString attr) {
    if (!SINCE_API_VERSION(1, 4)) {
        emitError(QString("Import PyOtherSide 1.4 or newer to use getattr()"));
        return QVariant();
    }

    ENSURE_GIL_STATE;

    PyObjectRef pyobj(convertQVariantToPyObject(obj), true);

    if (!pyobj) {
        emitError(QString("Failed to convert %1 to python object: '%1' (%2)").arg(obj.toString()).arg(priv->formatExc()));
        return QVariant();
    }

    QByteArray byteArray = attr.toUtf8();
    const char *attrStr = byteArray.data();

    PyObjectRef o(PyObject_GetAttrString(pyobj.borrow(), attrStr), true);

    if (!o) {
        emitError(QString("Attribute not found: '%1' (%2)").arg(attr).arg(priv->formatExc()));
        return QVariant();
    }

    return convertPyObjectToQVariant(o.borrow());
}
Beispiel #2
0
void
TestPyOtherSide::testIntMoreThan32Bits()
{
    // See https://github.com/thp/pyotherside/issues/86
    // Affected: Devices and OSes where long is 32 bits, but long long is 64 bits
    long long two_fortytwo = 4398046511104LL;
    PyObject *o = PyLong_FromLongLong(two_fortytwo);
    QVERIFY(o);
    QVariant v = convertPyObjectToQVariant(o);
    QVERIFY(v.toLongLong() == two_fortytwo);
}
Beispiel #3
0
QString
QPythonPriv::formatExc()
{
    PyObject *type = NULL;
    PyObject *value = NULL;
    PyObject *traceback = NULL;
    PyErr_Fetch(&type, &value, &traceback);
    PyErr_Clear();

    if (type == NULL && value == NULL && traceback == NULL) {
        return "No Error";
    }

    if (value != NULL && (type == NULL || traceback == NULL)) {
        return convertPyObjectToQVariant(PyObject_Str(value)).toString();
    }

    PyObject *list = PyObject_CallMethod(traceback_mod,
                                         "format_exception", "OOO", type, value, traceback);
    Q_ASSERT(list != NULL);
    PyObject *n = PyUnicode_FromString("\n");
    Q_ASSERT(n != NULL);
    PyObject *s = PyUnicode_Join(n, list);
    Q_ASSERT(s != NULL);
    if (s == NULL) {
        PyErr_Print();
        return "Exception";
    }
    QVariant v = convertPyObjectToQVariant(s);
    Q_ASSERT(v.isValid());
    Py_DECREF(s);
    Py_DECREF(n);
    Py_DECREF(list);

    Py_DECREF(type);
    Py_DECREF(value);
    Py_DECREF(traceback);

    qDebug() << v.toString();
    return v.toString();
}
Beispiel #4
0
QVariant
QPython::evaluate(QString expr)
{
    ENSURE_GIL_STATE;

    PyObjectRef o(priv->eval(expr), true);
    if (!o) {
        emitError(QString("Cannot evaluate '%1' (%2)").arg(expr).arg(priv->formatExc()));
        return QVariant();
    }

    return convertPyObjectToQVariant(o.borrow());
}
Beispiel #5
0
void
TestPyOtherSide::testConvertToPythonAndBack()
{
    QVariantList l;
    l << "Hello" << 123 << 45.667 << true;
    QVariantList l2;
    l2 << "A" << QVariant() << "B" << 4711;
    l << QVariant(l2);
    QVariant v(l);

    PyObject *o = convertQVariantToPyObject(v);
    QVariant v2 = convertPyObjectToQVariant(o);
    QVERIFY(v == v2);
}
Beispiel #6
0
void
TestPyOtherSide::testPyObjectRefRoundTrip()
{
    // Simulate a complete round-trip of a PyObject reference, from PyOtherSide
    // to QML and back.

    // Create a Python object, i.e. in a Python function.
    bool destructor_called = false;
    PyObject *o = PyCapsule_New(&destructor_called, "test", destruct);
    QVERIFY(o->ob_refcnt == 1);

    // Convert the object to a QVariant and increment its refcount.
    QVariant v = convertPyObjectToQVariant(o);

    // Decrement refcount and pass QVariant to QML.
    QVERIFY(o->ob_refcnt == 2);
    Py_DECREF(o);
    QVERIFY(o->ob_refcnt == 1);

    // Pass QVariant back to PyOtherSide, which converts it to a PyObject,
    // incrementing its refcount.
    PyObject *o2 = convertQVariantToPyObject(v);
    QVERIFY(o->ob_refcnt == 2);

    // The QVariant is deleted, i.e. by a JS variable falling out of scope.
    // This deletes the PyObjectRef and thus decrements the object's refcount.
    v = QVariant();

    // At this point, we only have one reference (the one from o2)
    QVERIFY(o->ob_refcnt == 1);

    // There's still a reference, so the destructor must not have been called
    QVERIFY(!destructor_called);

    // Now, at this point, the last remaining reference is removed, which
    // will cause the destructor to be called
    Py_DECREF(o2);

    // There are no references left, so the capsule's destructor is called.
    QVERIFY(destructor_called);
}
Beispiel #7
0
QVariant
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) {
        emitError(QString("Function not found: '%1' (%2)").arg(name).arg(priv->formatExc()));
        return QVariant();
    }

    QVariant v;
    QString errorMessage = priv->call(callable.borrow(), name, args, &v);
    if (!errorMessage.isNull()) {
        emitError(errorMessage);
    }
    return v;
}
Beispiel #8
0
void
TestPyOtherSide::testSetToList()
{
    // Test if a Python set is converted to a list
    PyObject *set = PySet_New(NULL);
    QVERIFY(set != NULL);
    PyObject *o = NULL;

    o = PyLong_FromLong(123);
    QVERIFY(o != NULL);
    QVERIFY(PySet_Add(set, o) == 0);

    o = PyLong_FromLong(321);
    QVERIFY(o != NULL);
    QVERIFY(PySet_Add(set, o) == 0);

    o = PyLong_FromLong(444);
    QVERIFY(o != NULL);
    QVERIFY(PySet_Add(set, o) == 0);

    // This will not be added (no duplicates in a set)
    o = PyLong_FromLong(123);
    QVERIFY(o != NULL);
    QVERIFY(PySet_Add(set, o) == 0);

    // At this point, we should have 3 items (123, 321 and 444)
    QVERIFY(PySet_Size(set) == 3);

    QVariant v = convertPyObjectToQVariant(set);
    QVERIFY(v.canConvert(QMetaType::QVariantList));

    QList<QVariant> l = v.toList();
    QVERIFY(l.size() == 3);
    QVERIFY(l.contains(123));
    QVERIFY(l.contains(321));
    QVERIFY(l.contains(444));
}
Beispiel #9
0
QString
QPythonPriv::formatExc()
{
    PyObject *type = NULL;
    PyObject *value = NULL;
    PyObject *traceback = NULL;

    PyObject *list = NULL;
    PyObject *n = NULL;
    PyObject *s = NULL;

    PyErr_Fetch(&type, &value, &traceback);
    PyErr_NormalizeException(&type, &value, &traceback);

    QString message;
    QVariant v;

    if (type == NULL && value == NULL && traceback == NULL) {
        // No exception thrown?
        goto cleanup;
    }

    if (value != NULL) {
        // We can at least format the exception as string
        message = convertPyObjectToQVariant(PyObject_Str(value)).toString();
    }

    if (type == NULL || traceback == NULL) {
        // Cannot get a traceback for this exception
        goto cleanup;
    }

    list = PyObject_CallMethod(traceback_mod,
            "format_exception", "OOO", type, value, traceback);

    if (list == NULL) {
        // Could not format exception, fall back to original message
        PyErr_Print();
        goto cleanup;
    }

    n = PyUnicode_FromString("\n");
    if (n == NULL) {
        PyErr_Print();
        goto cleanup;
    }

    s = PyUnicode_Join(n, list);
    if (s == NULL) {
        PyErr_Print();
        goto cleanup;
    }

    v = convertPyObjectToQVariant(s);
    if (v.isValid()) {
        message = v.toString();
    }

cleanup:
    Py_XDECREF(s);
    Py_XDECREF(n);
    Py_XDECREF(list);

    Py_XDECREF(type);
    Py_XDECREF(value);
    Py_XDECREF(traceback);

    qDebug() << QString("PyOtherSide error: %1").arg(message);
    return message;
}
Beispiel #10
0
void
QPythonPriv::receiveObject(PyObject *o)
{
    emit receive(convertPyObjectToQVariant(o));
}