Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp99
-rw-r--r--sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h10
-rw-r--r--sources/pyside6/PySide6/QtCore/typesystem_core_common.xml5
-rw-r--r--sources/pyside6/PySide6/glue/qtcore.cpp51
-rw-r--r--sources/pyside6/libpyside/CMakeLists.txt2
-rw-r--r--sources/pyside6/libpyside/pysidevariantutils.cpp212
-rw-r--r--sources/pyside6/libpyside/pysidevariantutils.h37
-rw-r--r--testing/runner.py10
8 files changed, 266 insertions, 160 deletions
diff --git a/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp b/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp
index e58d54998..9b2b40e82 100644
--- a/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp
+++ b/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp
@@ -19,105 +19,6 @@
#include <QtCore/QObject>
#include <QtCore/QRegularExpression>
#include <QtCore/QStack>
-#include <QtCore/QVariant>
-
-// Helpers for QVariant conversion
-
-QMetaType QVariant_resolveMetaType(PyTypeObject *type)
-{
- if (!PyObject_TypeCheck(type, SbkObjectType_TypeF()))
- return {};
- const char *typeName = Shiboken::ObjectType::getOriginalName(type);
- if (!typeName)
- return {};
- const bool valueType = '*' != typeName[qstrlen(typeName) - 1];
- // Do not convert user type of value
- if (valueType && Shiboken::ObjectType::isUserType(type))
- return {};
- QMetaType metaType = QMetaType::fromName(typeName);
- if (metaType.isValid())
- return metaType;
- // Do not resolve types to value type
- if (valueType)
- return {};
- // Find in base types. First check tp_bases, and only after check tp_base, because
- // tp_base does not always point to the first base class, but rather to the first
- // that has added any python fields or slots to its object layout.
- // See https://mail.python.org/pipermail/python-list/2009-January/520733.html
- if (type->tp_bases) {
- const auto size = PyTuple_Size(type->tp_bases);
- Py_ssize_t i = 0;
- // PYSIDE-1887, PYSIDE-86: Skip QObject base class of QGraphicsObject;
- // it needs to use always QGraphicsItem as a QVariant type for
- // QGraphicsItem::itemChange() to work.
- if (qstrcmp(typeName, "QGraphicsObject*") == 0)
- ++i;
- for ( ; i < size; ++i) {
- auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, i));
- const QMetaType derived = QVariant_resolveMetaType(baseType);
- if (derived.isValid())
- return derived;
- }
- } else if (type->tp_base) {
- return QVariant_resolveMetaType(type->tp_base);
- }
- return {};
-}
-
-QVariant QVariant_convertToValueList(PyObject *list)
-{
- if (PySequence_Size(list) < 0) {
- // clear the error if < 0 which means no length at all
- PyErr_Clear();
- return {};
- }
-
- Shiboken::AutoDecRef element(PySequence_GetItem(list, 0));
-
- auto *type = reinterpret_cast<PyTypeObject *>(element.object());
- QMetaType metaType = QVariant_resolveMetaType(type);
- if (!metaType.isValid())
- return {};
-
- const QByteArray listTypeName = QByteArrayLiteral("QList<") + metaType.name() + '>';
- metaType = QMetaType::fromName(listTypeName);
- if (!metaType.isValid())
- return {};
-
- Shiboken::Conversions::SpecificConverter converter(listTypeName);
- if (!converter) {
- qWarning("Type converter for: %s not registered.", listTypeName.constData());
- return {};
- }
-
- QVariant var(metaType);
- converter.toCpp(list, &var);
- return var;
-}
-
-bool QVariant_isStringList(PyObject *list)
-{
- if (!PySequence_Check(list)) {
- // If it is not a list or a derived list class
- // we assume that will not be a String list neither.
- return false;
- }
-
- if (PySequence_Size(list) < 0) {
- // clear the error if < 0 which means no length at all
- PyErr_Clear();
- return false;
- }
-
- Shiboken::AutoDecRef fast(PySequence_Fast(list, "Failed to convert QVariantList"));
- const Py_ssize_t size = PySequence_Size(fast.object());
- for (Py_ssize_t i = 0; i < size; ++i) {
- Shiboken::AutoDecRef item(PySequence_GetItem(fast.object(), i));
- if (PyUnicode_Check(item) == 0)
- return false;
- }
- return true;
-}
// Helpers for qAddPostRoutine
diff --git a/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h b/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h
index 11e84b291..4c1867a1a 100644
--- a/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h
+++ b/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h
@@ -14,10 +14,8 @@
QT_FORWARD_DECLARE_CLASS(QGenericArgument)
QT_FORWARD_DECLARE_CLASS(QGenericReturnArgument)
-QT_FORWARD_DECLARE_CLASS(QMetaType)
QT_FORWARD_DECLARE_CLASS(QObject)
QT_FORWARD_DECLARE_CLASS(QRegularExpression)
-QT_FORWARD_DECLARE_CLASS(QVariant);
QT_BEGIN_NAMESPACE
namespace QtCoreHelper {
@@ -26,14 +24,6 @@ class QGenericReturnArgumentHolder;
}
QT_END_NAMESPACE
-// Helpers for QVariant conversion
-
-QMetaType QVariant_resolveMetaType(PyTypeObject *type);
-
-QVariant QVariant_convertToValueList(PyObject *list);
-
-bool QVariant_isStringList(PyObject *list);
-
// Helpers for qAddPostRoutine
namespace PySide {
void globalPostRoutineCallback();
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
index 6fca49c6b..c3130ccb2 100644
--- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
+++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
@@ -16,6 +16,7 @@
<extra-includes>
<include file-name="pysidemetatype.h" location="global"/>
<include file-name="pysideutils.h" location="global"/> <!-- QString conversion -->
+ <include file-name="pysidevariantutils.h" location="global"/> <!-- QVariant conversion -->
<include file-name="signalmanager.h" location="global"/>
<include file-name="sbkerrors.h" location="global"/>
<!-- QtCoreHelper::QGenericReturnArgumentHolder -->
@@ -320,6 +321,7 @@
<extra-includes>
<include file-name="optional" location="global"/>
<include file-name="pysideqenum.h" location="global"/>
+ <include file-name="pysidevariantutils.h" location="global"/>
</extra-includes>
<conversion-rule>
<native-to-target file="../glue/qtcore.cpp" snippet="return-qvariant"/>
@@ -391,6 +393,9 @@
</object-type>
<primitive-type name="QJsonObject">
+ <extra-includes>
+ <include file-name="pysidevariantutils.h" location="global"/>
+ </extra-includes>
<conversion-rule>
<native-to-target file="../glue/qtcore.cpp" snippet="return-qjsonobject"/>
<target-to-native>
diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp
index 7fbf6a785..cd1462676 100644
--- a/sources/pyside6/PySide6/glue/qtcore.cpp
+++ b/sources/pyside6/PySide6/glue/qtcore.cpp
@@ -225,49 +225,6 @@ return %out;
// @snippet conversion-qmetatype-pytypeobject
// @snippet qvariant-conversion
-static QVariant QVariant_convertToVariantMap(PyObject *map)
-{
- Py_ssize_t pos = 0;
- Shiboken::AutoDecRef keys(PyDict_Keys(map));
- if (!QVariant_isStringList(keys))
- return {};
- PyObject *key{};
- PyObject *value{};
- QMap<QString,QVariant> ret;
- while (PyDict_Next(map, &pos, &key, &value)) {
- QString cppKey = %CONVERTTOCPP[QString](key);
- QVariant cppValue = %CONVERTTOCPP[QVariant](value);
- ret.insert(cppKey, cppValue);
- }
- return QVariant(ret);
-}
-static QVariant QVariant_convertToVariantList(PyObject *list)
-{
- if (QVariant_isStringList(list)) {
- QList<QString > lst = %CONVERTTOCPP[QList<QString>](list);
- return QVariant(QStringList(lst));
- }
- QVariant valueList = QVariant_convertToValueList(list);
- if (valueList.isValid())
- return valueList;
-
- if (PySequence_Size(list) < 0) {
- // clear the error if < 0 which means no length at all
- PyErr_Clear();
- return {};
- }
-
- QList<QVariant> lst;
- Shiboken::AutoDecRef fast(PySequence_Fast(list, "Failed to convert QVariantList"));
- const Py_ssize_t size = PySequence_Size(fast.object());
- for (Py_ssize_t i = 0; i < size; ++i) {
- Shiboken::AutoDecRef pyItem(PySequence_GetItem(fast.object(), i));
- QVariant item = %CONVERTTOCPP[QVariant](pyItem);
- lst.append(item);
- }
- return QVariant(lst);
-}
-
using SpecificConverter = Shiboken::Conversions::SpecificConverter;
static std::optional<SpecificConverter> converterForQtType(const char *typeNameC)
@@ -1544,7 +1501,7 @@ if (Shiboken::Enum::check(%in)) {
metaType = QMetaType::fromName(typeName);
}
if (!metaType.isValid())
- metaType = QVariant_resolveMetaType(Py_TYPE(%in));
+ metaType = PySide::Variant::resolveMetaType(Py_TYPE(%in));
bool ok = false;
if (metaType.isValid()) {
@@ -1566,12 +1523,12 @@ if (!ok)
// @snippet conversion-sbkobject
// @snippet conversion-pydict
-QVariant ret = QVariant_convertToVariantMap(%in);
+QVariant ret = PySide::Variant::convertToVariantMap(%in);
%out = ret.isValid() ? ret : QVariant::fromValue(PySide::PyObjectWrapper(%in));
// @snippet conversion-pydict
// @snippet conversion-pylist
-QVariant ret = QVariant_convertToVariantList(%in);
+QVariant ret = PySide::Variant::convertToVariantList(%in);
%out = ret.isValid() ? ret : QVariant::fromValue(PySide::PyObjectWrapper(%in));
// @snippet conversion-pylist
@@ -1581,7 +1538,7 @@ QVariant ret = QVariant_convertToVariantList(%in);
// @snippet conversion-pyobject
// @snippet conversion-qjsonobject-pydict
-QVariant dict = QVariant_convertToVariantMap(%in);
+QVariant dict = PySide::Variant::convertToVariantMap(%in);
QJsonValue val = QJsonValue::fromVariant(dict);
%out = val.toObject();
// @snippet conversion-qjsonobject-pydict
diff --git a/sources/pyside6/libpyside/CMakeLists.txt b/sources/pyside6/libpyside/CMakeLists.txt
index 539f1f329..15ab47494 100644
--- a/sources/pyside6/libpyside/CMakeLists.txt
+++ b/sources/pyside6/libpyside/CMakeLists.txt
@@ -40,6 +40,7 @@ set(libpyside_HEADERS # installed below
pysideslot_p.h
pysidestaticstrings.h
pysideutils.h
+ pysidevariantutils.h
pysideweakref.h
qobjectconnect.h
signalmanager.h
@@ -59,6 +60,7 @@ set(libpyside_SRC
pysidesignal.cpp
pysideslot.cpp
pysideproperty.cpp
+ pysidevariantutils.cpp
pysideweakref.cpp
pyside.cpp
pyside_numpy.cpp
diff --git a/sources/pyside6/libpyside/pysidevariantutils.cpp b/sources/pyside6/libpyside/pysidevariantutils.cpp
new file mode 100644
index 000000000..7dbfb3afc
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidevariantutils.cpp
@@ -0,0 +1,212 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "pysidevariantutils.h"
+#include "pysideutils.h"
+
+#include <QtCore/qvariantmap.h>
+
+#include <autodecref.h>
+#include <sbkconverter.h>
+#include <basewrapper.h>
+
+using namespace Qt::StringLiterals;
+
+static const char qVariantTypeName[] = "QVariant";
+
+static void warnConverter(const char *name)
+{
+ qWarning("Type converter for: %s not registered.", name);
+}
+
+// Helper converting each item of a non-empty list using the "QVariant" converter
+static std::optional<QVariantList> pyListToVariantListHelper(PyObject *list, Py_ssize_t size)
+{
+ Q_ASSERT(size > 0);
+ QVariantList result;
+ result.reserve(size);
+ Shiboken::Conversions::SpecificConverter converter(qVariantTypeName);
+ if (!converter) {
+ warnConverter(qVariantTypeName);
+ return std::nullopt;
+ }
+ for (Py_ssize_t i = 0; i < size; ++i) {
+ Shiboken::AutoDecRef pyItem(PySequence_GetItem(list, i));
+ QVariant item;
+ converter.toCpp(pyItem.object(), &item);
+ result.append(item);
+ }
+ return result;
+}
+
+// Helper checking for a sequence of Unicode objects
+static bool isStringList(PyObject *list)
+{
+ const Py_ssize_t size = PySequence_Size(list);
+ if (size == 0)
+ return false;
+ for (Py_ssize_t i = 0; i < size; ++i) {
+ Shiboken::AutoDecRef item(PySequence_GetItem(list, i));
+ if (PyUnicode_Check(item) == 0)
+ return false;
+ }
+ return true;
+}
+
+// Helper to convert to a QStringList
+static std::optional<QStringList> listToStringList(PyObject *list)
+{
+ static const char listType[] = "QList<QString>";
+ Shiboken::Conversions::SpecificConverter converter(listType);
+ if (!converter) {
+ warnConverter(listType);
+ return std::nullopt;
+ }
+ QStringList result;
+ converter.toCpp(list, &result);
+ return result;
+}
+
+// Helper to convert a non-empty, homogenous list using the converter of the first item
+static QVariant convertToValueList(PyObject *list)
+{
+ Q_ASSERT(PySequence_Size(list) >= 0);
+
+ Shiboken::AutoDecRef element(PySequence_GetItem(list, 0));
+
+ auto *type = reinterpret_cast<PyTypeObject *>(element.object());
+ QMetaType metaType = PySide::Variant::resolveMetaType(type);
+ if (!metaType.isValid())
+ return {};
+
+ const QByteArray listTypeName = QByteArrayLiteral("QList<") + metaType.name() + '>';
+ metaType = QMetaType::fromName(listTypeName);
+ if (!metaType.isValid())
+ return {};
+
+ Shiboken::Conversions::SpecificConverter converter(listTypeName);
+ if (!converter) {
+ warnConverter(listTypeName.constData());
+ return {};
+ }
+
+ QVariant var(metaType);
+ converter.toCpp(list, &var);
+ return var;
+}
+
+namespace PySide::Variant
+{
+
+QMetaType resolveMetaType(PyTypeObject *type)
+{
+ if (!PyObject_TypeCheck(type, SbkObjectType_TypeF()))
+ return {};
+ const char *typeName = Shiboken::ObjectType::getOriginalName(type);
+ if (!typeName)
+ return {};
+ const bool valueType = '*' != typeName[qstrlen(typeName) - 1];
+ // Do not convert user type of value
+ if (valueType && Shiboken::ObjectType::isUserType(type))
+ return {};
+ QMetaType metaType = QMetaType::fromName(typeName);
+ if (metaType.isValid())
+ return metaType;
+ // Do not resolve types to value type
+ if (valueType)
+ return {};
+ // Find in base types. First check tp_bases, and only after check tp_base, because
+ // tp_base does not always point to the first base class, but rather to the first
+ // that has added any python fields or slots to its object layout.
+ // See https://mail.python.org/pipermail/python-list/2009-January/520733.html
+ if (type->tp_bases) {
+ const auto size = PyTuple_Size(type->tp_bases);
+ Py_ssize_t i = 0;
+ // PYSIDE-1887, PYSIDE-86: Skip QObject base class of QGraphicsObject;
+ // it needs to use always QGraphicsItem as a QVariant type for
+ // QGraphicsItem::itemChange() to work.
+ if (qstrcmp(typeName, "QGraphicsObject*") == 0)
+ ++i;
+ for ( ; i < size; ++i) {
+ auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, i));
+ const QMetaType derived = resolveMetaType(baseType);
+ if (derived.isValid())
+ return derived;
+ }
+ return {};
+ }
+ if (type->tp_base != nullptr)
+ return resolveMetaType(type->tp_base);
+ return {};
+}
+
+std::optional<QVariantList> pyListToVariantList(PyObject *list)
+{
+ if (list == nullptr || PySequence_Check(list) == 0)
+ return std::nullopt;
+ const auto size = PySequence_Size(list);
+ if (size < 0) { // Some infinite (I/O read) thing? - bail out
+ PyErr_Clear();
+ return std::nullopt;
+ }
+ if (size == 0)
+ return QVariantList{};
+ return pyListToVariantListHelper(list, size);
+}
+
+QVariant convertToVariantList(PyObject *list)
+{
+ const auto size = PySequence_Size(list);
+ if (size < 0) { // Some infinite (I/O read) thing? - bail out
+ PyErr_Clear();
+ return {};
+ }
+ if (size == 0)
+ return QVariantList{};
+
+ if (isStringList(list)) {
+ auto stringListO = listToStringList(list);
+ if (stringListO.has_value())
+ return {stringListO.value()};
+ }
+
+ if (QVariant valueList = convertToValueList(list); valueList.isValid())
+ return valueList;
+
+ if (auto vlO = pyListToVariantListHelper(list, size); vlO.has_value())
+ return vlO.value();
+
+ return {};
+}
+
+QVariant convertToVariantMap(PyObject *map)
+{
+ if (map == nullptr || PyDict_Check(map) == 0)
+ return {};
+
+ QVariantMap result;
+ if (PyDict_Size(map) == 0)
+ return result;
+
+ Py_ssize_t pos = 0;
+ Shiboken::AutoDecRef keys(PyDict_Keys(map));
+ if (!isStringList(keys))
+ return {};
+
+ Shiboken::Conversions::SpecificConverter converter(qVariantTypeName);
+ if (!converter) {
+ warnConverter(qVariantTypeName);
+ return {};
+ }
+
+ PyObject *key{};
+ PyObject *value{};
+ while (PyDict_Next(map, &pos, &key, &value)) {
+ QVariant cppValue;
+ converter.toCpp(value, &cppValue);
+ result.insert(PySide::pyUnicodeToQString(key), cppValue);
+ }
+ return result;
+}
+
+} // namespace PySide::Variant
diff --git a/sources/pyside6/libpyside/pysidevariantutils.h b/sources/pyside6/libpyside/pysidevariantutils.h
new file mode 100644
index 000000000..b53f7ce82
--- /dev/null
+++ b/sources/pyside6/libpyside/pysidevariantutils.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef PYSIDEVARIANTUTILS_H
+#define PYSIDEVARIANTUTILS_H
+
+#include <sbkpython.h>
+
+#include <pysidemacros.h>
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qvariantlist.h>
+
+#include <optional>
+
+namespace PySide::Variant
+{
+
+/// Return a QMetaType for a PyTypeObject for purposes of
+/// converting to a QVariant.
+PYSIDE_API QMetaType resolveMetaType(PyTypeObject *type);
+
+/// Convert a heterogenous Python list to a QVariantList by converting each
+/// item using the QVariant converter.
+PYSIDE_API std::optional<QVariantList> pyListToVariantList(PyObject *list);
+
+/// Converts a list to a QVariant following the PySide semantics:
+/// - A list of strings is returned as QVariant<QStringList>
+/// - A list of convertible values is returned as QVariant<QList<Value>>
+/// - Remaining types are returned as QVariant(QVariantList)
+PYSIDE_API QVariant convertToVariantList(PyObject *list);
+
+/// Converts a map to a QVariantMap (string keys and QVariant values)
+PYSIDE_API QVariant convertToVariantMap(PyObject *map);
+} // namespace PySide::Variant
+
+#endif // PYSIDEVARIANTUTILS_H
diff --git a/testing/runner.py b/testing/runner.py
index ad1e01d65..4d96fdfc2 100644
--- a/testing/runner.py
+++ b/testing/runner.py
@@ -20,7 +20,7 @@ this_dir = os.path.dirname(this_file)
build_scripts_dir = os.path.abspath(os.path.join(this_dir, ".."))
sys.path.append(build_scripts_dir)
-from build_scripts.utils import detect_clang
+from build_scripts.utils import detect_clang # noqa: E402
class TestRunner:
@@ -78,11 +78,12 @@ class TestRunner:
Helper for _find_ctest() that finds the ctest binary in a build
system file (ninja, Makefile).
"""
- look_for = "--force-new-ctest-process"
+ # Looking for a command ending this way:
+ look_for = "\\ctest.exe" if "win32" in sys.platform else "/ctest"
line = None
with open(file_name) as makefile:
for line in makefile:
- if look_for in line:
+ if look_for in line and line.lstrip().startswith("COMMAND"):
break
else:
# We have probably forgotten to build the tests.
@@ -98,7 +99,8 @@ class TestRunner:
raise RuntimeError(msg)
# the ctest program is on the left to look_for
assert line, f"Did not find {look_for}"
- ctest = re.search(r'(\S+|"([^"]+)")\s+' + look_for, line).groups()
+ look = re.escape(look_for)
+ ctest = re.search(fr'(\S+{look}|"([^"]+{look})")', line).groups()
return ctest[1] or ctest[0]
def _find_ctest(self):