diff options
| author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2024-07-02 10:01:25 +0200 |
|---|---|---|
| committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2024-07-04 11:55:48 +0200 |
| commit | 0e920a721830ca0ed900492756335ceafea1fedd (patch) | |
| tree | 457748ec7b5bdd4874d1942f57ab81902d210ca8 /sources/pyside6/libpyside | |
| parent | 225e18558f37e2b228be5da60f27ec8186a26bc7 (diff) | |
libpyside: Decouple SignalManager::callPythonMetaMethod() from QMetaMethod
Extract a helper for calling Python slots and add an overload
that only takes a list of parameter types and return type.
Change-Id: I407c3b1ae66eb4f01370ceac3112eb9407796efa
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources/pyside6/libpyside')
| -rw-r--r-- | sources/pyside6/libpyside/signalmanager.cpp | 164 | ||||
| -rw-r--r-- | sources/pyside6/libpyside/signalmanager.h | 7 |
2 files changed, 123 insertions, 48 deletions
diff --git a/sources/pyside6/libpyside/signalmanager.cpp b/sources/pyside6/libpyside/signalmanager.cpp index 5b17be510..23ce36cf7 100644 --- a/sources/pyside6/libpyside/signalmanager.cpp +++ b/sources/pyside6/libpyside/signalmanager.cpp @@ -41,7 +41,6 @@ using namespace Qt::StringLiterals; #include "globalreceiverv2.h" static PyObject *metaObjectAttr = nullptr; -static PyObject *parseArguments(const QMetaMethod &method, void **args); static bool qAppRunning = false; @@ -84,16 +83,28 @@ static QByteArray methodSignature(const QMetaMethod &method) return result; } -static QByteArray msgCannotConvertParameter(const QMetaMethod &method, qsizetype p) +static QByteArray msgCannotConvertParameter(const QByteArray ¶meterTypeName, + const QByteArray &signature, qsizetype p) { - return "Cannot call meta function \""_ba + methodSignature(method) + return "Cannot call meta function \""_ba + signature + "\" because parameter " + QByteArray::number(p) + " of type \""_ba - + method.parameterTypeName(p) + "\" cannot be converted."_ba; + + parameterTypeName + "\" cannot be converted."_ba; } -static QByteArray msgCannotConvertReturn(const QMetaMethod &method) +static inline QByteArray msgCannotConvertParameter(QMetaMethod method, qsizetype p) { - return "The return value of \""_ba + methodSignature(method) + "\" cannot be converted."_ba; + return msgCannotConvertParameter(method.parameterTypeName(p), + methodSignature(method), p); +} + +static QByteArray msgCannotConvertReturn(const QByteArray &signature) +{ + return "The return value of \""_ba + signature + "\" cannot be converted."_ba; +} + +static inline QByteArray msgCannotConvertReturn(QMetaMethod method) +{ + return msgCannotConvertReturn(methodSignature(method)); } namespace PySide { @@ -627,33 +638,114 @@ int SignalManager::qt_metacall(QObject *object, QMetaObject::Call call, int id, return id; } -int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args, PyObject *pyMethod) +// Helper for calling a Python pyCallable matching a Qt signal / slot. +enum CallResult : int { - Q_ASSERT(pyMethod); + CallOk, + CallOtherError, // Python error set + CallReturnValueError, + CallArgumentError // Argument (return - CallArgumentError) caused an error +}; - Shiboken::GilState gil; - PyObject *pyArguments = parseArguments(method, args); - - if (pyArguments) { - QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter; - const char *returnType = method.typeName(); - if (returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0) { - retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType)); - if (!retConverter->isValid()) { - PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData()); - return -1; - } - } +static inline bool isNonVoidReturn(const char *returnType) +{ + return returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0; +} - Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments)); +static int callPythonMetaMethodHelper(const QByteArrayList ¶mTypes, + const char *returnType /* = nullptr */, + void **args, PyObject *pyCallable) +{ + const qsizetype argsSize = paramTypes.size(); + Shiboken::AutoDecRef preparedArgs(PyTuple_New(argsSize)); - Py_DECREF(pyArguments); + for (qsizetype i = 0; i < argsSize; ++i) { + void *data = args[i + 1]; + auto param = paramTypes.at(i); + Shiboken::Conversions::SpecificConverter converter(param.constData()); + if (!converter.isValid()) + return CallResult::CallArgumentError + int(i); + PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data)); + } - if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter) - retConverter->toCpp(retval, args[0]); + QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter; + if (isNonVoidReturn(returnType)) { + retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType)); + if (!retConverter->isValid()) + return CallResult::CallReturnValueError; } - return -1; + Shiboken::AutoDecRef retval(PyObject_CallObject(pyCallable, preparedArgs.object())); + if (PyErr_Occurred() != nullptr || retval.isNull()) + return CallResult::CallOtherError; + + if (retval != Py_None && !retConverter.isNull()) + retConverter->toCpp(retval, args[0]); + return CallResult::CallOk; +} + +int SignalManager::callPythonMetaMethod(QMetaMethod method, void **args, + PyObject *callable) +{ + Q_ASSERT(callable); + + Shiboken::GilState gil; + int callResult = callPythonMetaMethodHelper(method.parameterTypes(), + method.typeName(), args, callable); + switch (callResult) { + case CallOk: + return 0; + case CallOtherError: + return -1; + case CallReturnValueError: + PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData()); + return -1; + default: { // CallArgumentError + n + const int arg = callResult - CallArgumentError; + PyErr_SetString(PyExc_TypeError, msgCannotConvertParameter(method, arg).constData()); + return -1; + } + } + return 0; +} + +static QByteArray signature(const char *name, const QByteArrayList ¶meterTypes, + const char *returnType) +{ + QByteArray result; + if (isNonVoidReturn(returnType)) + result += QByteArray(returnType) + ' '; + result += QByteArray(name) + '(' + parameterTypes.join(", ") + ')'; + return result; +} + +int SignalManager::callPythonMetaMethod(const QByteArrayList ¶meterTypes, + const char *returnType, + void **args, PyObject *callable) +{ + Q_ASSERT(callable); + + Shiboken::GilState gil; + int callResult = callPythonMetaMethodHelper(parameterTypes, returnType, args, callable); + switch (callResult) { + case CallOk: + return 0; + case CallOtherError: + return -1; + case CallReturnValueError: { + const auto &sig = signature("slot", parameterTypes, returnType); + PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(sig).constData()); + return -1; + } + default: { // CallArgumentError + n + const int arg = callResult - CallArgumentError; + const auto &sig = signature("slot", parameterTypes, returnType); + const auto &msg = msgCannotConvertParameter(parameterTypes.at(arg), sig, arg); + PyErr_SetString(PyExc_TypeError, msg.constData()); + return -1; + } + } + return 0; } bool SignalManager::registerMetaMethod(QObject *source, const char *signature, QMetaMethod::MethodType type) @@ -794,24 +886,4 @@ const QMetaObject *SignalManager::retrieveMetaObject(PyObject *self) return builder->update(); } -static PyObject *parseArguments(const QMetaMethod &method, void **args) -{ - const auto ¶mTypes = method.parameterTypes(); - const qsizetype argsSize = paramTypes.size(); - PyObject *preparedArgs = PyTuple_New(argsSize); - - for (qsizetype i = 0; i < argsSize; ++i) { - void *data = args[i+1]; - auto param = paramTypes.at(i); - Shiboken::Conversions::SpecificConverter converter(param.constData()); - if (!converter) { - PyErr_SetString(PyExc_TypeError, msgCannotConvertParameter(method, i).constData()); - Py_DECREF(preparedArgs); - return nullptr; - } - PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data)); - } - return preparedArgs; -} - #include "signalmanager.moc" diff --git a/sources/pyside6/libpyside/signalmanager.h b/sources/pyside6/libpyside/signalmanager.h index 3fdf3e9b5..443117447 100644 --- a/sources/pyside6/libpyside/signalmanager.h +++ b/sources/pyside6/libpyside/signalmanager.h @@ -80,8 +80,11 @@ public: void clear(); void purgeEmptyGlobalReceivers(); - // Utility function to call a python method usign args received in qt_metacall - static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj); + // Utility function to call a python method using args received in qt_metacall + static int callPythonMetaMethod(QMetaMethod method, void **args, PyObject *callable); + static int callPythonMetaMethod(const QByteArrayList ¶meterTypes, + const char *returnType /* = nullptr */, + void **args, PyObject *callable); static void deleteGlobalReceiver(const QObject *globalReceiver); |
