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()); }
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); }
PyObject * pyotherside_qrc_list_dir(PyObject *self, PyObject *dirname) { QString qdirname = qstring_from_pyobject_arg(dirname); if (qdirname.isNull()) { return NULL; } QDir dir(":" + qdirname); if (!dir.exists()) { PyErr_SetString(PyExc_ValueError, "Directory not found"); return NULL; } return convertQVariantToPyObject(dir.entryList()); }
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); }
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; }