aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.pyside6.md2
-rw-r--r--README.pyside6_addons.md2
-rw-r--r--README.pyside6_essentials.md2
-rw-r--r--README.pyside6_examples.md2
-rw-r--r--coin/instructions/find_path_to_msvc_compiler.yaml7
-rw-r--r--doc/changelogs/changes-6.7.161
-rw-r--r--examples/quickcontrols/filesystemexplorer/main.py2
-rw-r--r--sources/pyside6/libpyside/pysidesignal.cpp4
-rw-r--r--sources/pyside6/libpysideqml/pysideqmllistproperty.cpp2
-rw-r--r--sources/pyside6/libpysideqml/pysideqmlregistertype.cpp51
-rw-r--r--sources/pyside6/libpysideqml/pysideqmluncreatable.cpp7
-rw-r--r--sources/pyside6/libpysideqml/pysideqmluncreatable.h3
-rw-r--r--sources/pyside6/tests/QtQml/CMakeLists.txt1
-rw-r--r--sources/pyside6/tests/QtQml/listproperty.py87
-rw-r--r--sources/pyside6/tests/QtQml/listproperty.qml50
-rw-r--r--sources/pyside6/tests/QtQml/qmlregistertype_test.py53
-rw-r--r--sources/pyside6/tests/QtQml/qmlregistertype_test.qml7
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp106
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetatype.cpp1
-rw-r--r--sources/shiboken6/ApiExtractor/messages.cpp15
-rw-r--r--sources/shiboken6/ApiExtractor/messages.h4
-rw-r--r--sources/shiboken6/doc/typediscovery.rst145
-rw-r--r--sources/shiboken6/doc/typesystem.rst1
-rw-r--r--sources/shiboken6/doc/typesystem_converters.rst58
-rw-r--r--sources/shiboken6/doc/typesystem_specifying_types.rst33
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp49
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.h3
-rw-r--r--sources/shiboken6/libshiboken/basewrapper.cpp41
-rw-r--r--sources/shiboken6/libshiboken/bindingmanager.cpp73
-rw-r--r--sources/shiboken6/libshiboken/bindingmanager.h6
-rw-r--r--sources/shiboken6/libshiboken/pep384impl.cpp8
-rw-r--r--sources/shiboken6/libshiboken/sbknumpy.cpp4
32 files changed, 659 insertions, 231 deletions
diff --git a/README.pyside6.md b/README.pyside6.md
index 8c70b1c8f..9178660d5 100644
--- a/README.pyside6.md
+++ b/README.pyside6.md
@@ -88,7 +88,7 @@ and [join our community](https://wiki.qt.io/Qt_for_Python#Community)!
### Licensing
-PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial
+PySide6 is available under both Open Source (LGPLv3/GPLv3) and commercial
license. Using PyPi is the recommended installation source, because the
content of the wheels is valid for both cases. For more information, refer to
the [Qt Licensing page](https://www.qt.io/licensing/).
diff --git a/README.pyside6_addons.md b/README.pyside6_addons.md
index 3247a550d..e6044c4a4 100644
--- a/README.pyside6_addons.md
+++ b/README.pyside6_addons.md
@@ -65,7 +65,7 @@ and [join our community](https://wiki.qt.io/Qt_for_Python#Community)!
### Licensing
-PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial
+PySide6 is available under both Open Source (LGPLv3/GPLv3) and commercial
license. Using PyPi is the recommended installation source, because the
content of the wheels is valid for both cases. For more information, refer to
the [Qt Licensing page](https://www.qt.io/licensing/).
diff --git a/README.pyside6_essentials.md b/README.pyside6_essentials.md
index ef0376e43..7f96c19b1 100644
--- a/README.pyside6_essentials.md
+++ b/README.pyside6_essentials.md
@@ -51,7 +51,7 @@ and [join our community](https://wiki.qt.io/Qt_for_Python#Community)!
### Licensing
-PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial
+PySide6 is available under both Open Source (LGPLv3/GPLv3) and commercial
license. Using PyPi is the recommended installation source, because the
content of the wheels is valid for both cases. For more information, refer to
the [Qt Licensing page](https://www.qt.io/licensing/).
diff --git a/README.pyside6_examples.md b/README.pyside6_examples.md
index ffa7d83d5..15e318151 100644
--- a/README.pyside6_examples.md
+++ b/README.pyside6_examples.md
@@ -28,7 +28,7 @@ and [join our community](https://wiki.qt.io/Qt_for_Python#Community)!
### Licensing
-PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial
+PySide6 is available under both Open Source (LGPLv3/GPLv3) and commercial
licenses. Using PyPi is the recommended installation source, because the
content of the wheels is valid for both cases. For more information, refer to
the [Qt Licensing page](https://www.qt.io/licensing/).
diff --git a/coin/instructions/find_path_to_msvc_compiler.yaml b/coin/instructions/find_path_to_msvc_compiler.yaml
index 8f9bed82f..015a86eab 100644
--- a/coin/instructions/find_path_to_msvc_compiler.yaml
+++ b/coin/instructions/find_path_to_msvc_compiler.yaml
@@ -18,6 +18,13 @@ instructions:
condition: property
property: host.compiler
equals_value: MSVC2019
+ - type: EnvironmentVariable
+ variableName: VC_SCRIPT
+ variableValue: "\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Auxiliary\\Build\\vcvarsall.bat"
+ enable_if:
+ condition: property
+ property: host.compiler
+ equals_value: MSVC2022
- type: WriteFile
fileContents: "call \"{{.Env.VC_SCRIPT}}\" {{.Env.TARGET_ARCHITECTURE}} \r\ncmd /c %*"
filename: "c:\\users\\qt\\MSVC.bat"
diff --git a/doc/changelogs/changes-6.7.1 b/doc/changelogs/changes-6.7.1
new file mode 100644
index 000000000..66263ed80
--- /dev/null
+++ b/doc/changelogs/changes-6.7.1
@@ -0,0 +1,61 @@
+Qt for Python 6.7.1 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-487] A number of missing classes have been added.
+ - [PYSIDE-2629] Tooling: pyside6-qsb, pyside6-balsam and pyside6-balsamui
+ have been added.
+ - [PYSIDE-2644] QtAsyncio: An issue with tasks with loop not cancelling
+ has been fixed.
+ - [PYSIDE-2663] A crash when browsing https://outlook.com has been fixed.
+ - [PYSIDE-2665] A syntax error in the pyi-files has been fixed.
+ - [PYSIDE-2668] The comparison of QOperatingSystemVersion.OSType has been
+ fixed.
+ - [PYSIDE-2675] Lazy Load: An issue with polymorphic classes has been
+ fixed.
+ - [PYSIDE-2676] A crash with Python 3.12 when creating classes from
+ meta classes has been fixed.
+ - [PYSIDE-2685] An error in the pyi-files related to the import of
+ NoneType has been fixed.
+ - [PYSIDE-2686] Missing imports for types of return values
+ have been added to the pyi-files.
+ - [PYSIDE-2698] A crash when querying the size of QtQml.ListProperty
+ has been fixed and documentation for QtQml.ListProperty
+ has been added.
+ - [PYSIDE-2705] Warnings about failures of QObject.disconnect() can
+ now be suppressed.
+ - [PYSIDE-2709] A bug using legacy qmlRegisterType() for class hierarchies
+ has been fixed.
+ - [QTBUG-123997] Multimedia: The renaming of the namespace QAudio to
+ QtAudio has been undone following a revert in Qt.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-2590] An attribute for global inline namespace scopes has been
+ added.
+ - [PYSIDE-2602] Generate Python override code for added virtuals
+ - [PYSIDE-2602] Support for virtual functions with return type
+ modifications has been added and the function
+ QWebEnginePage.javaScriptPrompt()
+ has been fixed accordingly.
+ - [PYSIDE-2675] A code snippet placeholder for the base class for
+ polymorphic-id-expressions has been added, fixing
+ a potentially undefined behavior when using the
+ derived classes.
diff --git a/examples/quickcontrols/filesystemexplorer/main.py b/examples/quickcontrols/filesystemexplorer/main.py
index 1477023e9..8fad951cb 100644
--- a/examples/quickcontrols/filesystemexplorer/main.py
+++ b/examples/quickcontrols/filesystemexplorer/main.py
@@ -22,7 +22,7 @@ if __name__ == '__main__':
app.setOrganizationName("QtProject")
app.setApplicationName("File System Explorer")
app.setApplicationVersion(qVersion())
- app.setWindowIcon(QIcon("FileSystemModule/icons/app_icon.svg"))
+ app.setWindowIcon(QIcon(sys.path[0] + "/FileSystemModule/icons/app_icon.svg"))
parser = QCommandLineParser()
parser.setApplicationDescription("Qt Filesystemexplorer Example")
diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp
index c93cbadfb..774837e5b 100644
--- a/sources/pyside6/libpyside/pysidesignal.cpp
+++ b/sources/pyside6/libpyside/pysidesignal.cpp
@@ -664,10 +664,10 @@ static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key)
static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signature)
{
if (PyErr_Occurred() != nullptr) { // avoid "%S" invoking str() when an error is set.
- PyErr_WarnFormat(PyExc_RuntimeError, 0, "Failed to disconnect (%s) from signal \"%s\".",
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%s) from signal \"%s\".",
Py_TYPE(aSlot)->tp_name, signature.constData());
} else {
- PyErr_WarnFormat(PyExc_RuntimeError, 0, "Failed to disconnect (%S) from signal \"%s\".",
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%S) from signal \"%s\".",
aSlot, signature.constData());
}
}
diff --git a/sources/pyside6/libpysideqml/pysideqmllistproperty.cpp b/sources/pyside6/libpysideqml/pysideqmllistproperty.cpp
index 97d6ce91b..75bb5af96 100644
--- a/sources/pyside6/libpysideqml/pysideqmllistproperty.cpp
+++ b/sources/pyside6/libpysideqml/pysideqmllistproperty.cpp
@@ -169,7 +169,7 @@ qsizetype propListCount(QQmlListProperty<QObject> *propList)
return 0;
}
- int cppResult = 0;
+ qsizetype cppResult = 0;
auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>();
if (auto *pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(converter, retVal))
pythonToCpp(retVal, &cppResult);
diff --git a/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp b/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
index 618d621bd..223c6eaa3 100644
--- a/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
+++ b/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
@@ -28,6 +28,7 @@
#include <QtQml/QJSValue>
#include <QtQml/QQmlListProperty>
#include <private/qqmlmetatype_p.h>
+#include <private/qmetaobjectbuilder_p.h>
#include <memory>
@@ -191,19 +192,15 @@ namespace PySide::Qml {
// Modern (6.7) type registration using RegisterTypeAndRevisions
// and information set to QMetaClassInfo.
-static int qmlRegisterType(PyObject *pyObj, PyObject *pyClassInfoObj,
- const ImportData &importData)
+static int qmlRegisterType(PyObject *pyObj,
+ const ImportData &importData,
+ const QMetaObject *metaObject,
+ const QMetaObject *classInfoMetaObject = nullptr)
{
- using namespace Shiboken;
-
PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
- if (!isQObjectDerived(pyObjType, true))
- return -1;
- const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
- Q_ASSERT(metaObject);
- const QMetaObject *classInfoMetaObject = pyObj == pyClassInfoObj
- ? metaObject : PySide::retrieveMetaObject(pyClassInfoObj);
+ if (classInfoMetaObject == nullptr)
+ classInfoMetaObject = metaObject;
// Register as simple QObject rather than Qt Quick item.
// Incref the type object, don't worry about decref'ing it because
@@ -270,18 +267,44 @@ static int qmlRegisterType(PyObject *pyObj, PyObject *pyClassInfoObj,
return qmlTypeId;
}
+static int qmlRegisterType(PyObject *pyObj, PyObject *pyClassInfoObj,
+ const ImportData &importData)
+{
+ PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ if (!isQObjectDerived(pyObjType, true))
+ return -1;
+
+ const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
+ Q_ASSERT(metaObject);
+ const QMetaObject *classInfoMetaObject = pyObj == pyClassInfoObj
+ ? metaObject : PySide::retrieveMetaObject(pyClassInfoObj);
+ return qmlRegisterType(pyObj, importData, metaObject, classInfoMetaObject);
+}
+
// Legacy (pre 6.7) compatibility helper for the free register functions.
int qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, int versionMinor,
const char *qmlName, const char *noCreationReason,
bool creatable)
{
auto *type = checkTypeObject(pyObj, "qmlRegisterType()");
- if (type == nullptr || !PySide::isQObjectDerived(type, true)
- || !setClassInfo(type, qmlElementKey, qmlName))
+ if (type == nullptr || !PySide::isQObjectDerived(type, true))
return -1;
+
+ const QMetaObject *metaObject = PySide::retrieveMetaObject(type);
+ Q_ASSERT(metaObject);
+
+ // PYSIDE-2709: Use a separate QMetaObject for the class information
+ // as modifying metaObject breaks inheritance.
+ QMetaObjectBuilder classInfobuilder(&QObject::staticMetaObject);
+ classInfobuilder.addClassInfo(qmlElementKey, qmlName);
if (!creatable)
- setUncreatableClassInfo(type, noCreationReason);
- return qmlRegisterType(pyObj, pyObj, {uri, versionMajor, versionMinor});
+ setUncreatableClassInfo(&classInfobuilder, noCreationReason);
+ auto *classInfoMetaObject = classInfobuilder.toMetaObject();
+
+ const int qmlTypeId = qmlRegisterType(pyObj, {uri, versionMajor, versionMinor},
+ metaObject, classInfoMetaObject);
+ free(classInfoMetaObject);
+ return qmlTypeId;
}
// Singleton helpers
diff --git a/sources/pyside6/libpysideqml/pysideqmluncreatable.cpp b/sources/pyside6/libpysideqml/pysideqmluncreatable.cpp
index 55b15ba5b..7c0f6b8ff 100644
--- a/sources/pyside6/libpysideqml/pysideqmluncreatable.cpp
+++ b/sources/pyside6/libpysideqml/pysideqmluncreatable.cpp
@@ -10,6 +10,7 @@
#include <sbkcppstring.h>
#include <QtCore/qbytearray.h>
+#include <private/qmetaobjectbuilder_p.h>
using namespace Qt::StringLiterals;
@@ -109,3 +110,9 @@ void setUncreatableClassInfo(PyTypeObject *type, const QByteArray &reason)
{"QML.Creatable"_ba, "false"_ba},
{"QML.UncreatableReason"_ba, reason} });
}
+
+void setUncreatableClassInfo(QMetaObjectBuilder *builder, const QByteArray &reason)
+{
+ builder->addClassInfo("QML.Creatable", "false");
+ builder->addClassInfo("QML.UncreatableReason", reason);
+}
diff --git a/sources/pyside6/libpysideqml/pysideqmluncreatable.h b/sources/pyside6/libpysideqml/pysideqmluncreatable.h
index 772ad4ccb..8a8adb3c8 100644
--- a/sources/pyside6/libpysideqml/pysideqmluncreatable.h
+++ b/sources/pyside6/libpysideqml/pysideqmluncreatable.h
@@ -8,6 +8,8 @@
#include <QtCore/QByteArray>
+QT_FORWARD_DECLARE_CLASS(QMetaObjectBuilder)
+
// The QmlUncreatable decorator modifies QmlElement to register an uncreatable
// type. Due to the (reverse) execution order of decorators, it needs to follow
// QmlElement.
@@ -19,5 +21,6 @@ extern "C"
void initQmlUncreatable(PyObject *module);
void setUncreatableClassInfo(PyTypeObject *type, const QByteArray &reason);
+void setUncreatableClassInfo(QMetaObjectBuilder *builder, const QByteArray &reason);
#endif // PYSIDEQMLUNCREATABLE_H
diff --git a/sources/pyside6/tests/QtQml/CMakeLists.txt b/sources/pyside6/tests/QtQml/CMakeLists.txt
index 720f0ef99..30bf7e786 100644
--- a/sources/pyside6/tests/QtQml/CMakeLists.txt
+++ b/sources/pyside6/tests/QtQml/CMakeLists.txt
@@ -17,6 +17,7 @@ PYSIDE_TEST(bug_997.py)
PYSIDE_TEST(bug_1029.py)
PYSIDE_TEST(groupedproperty.py)
PYSIDE_TEST(listproperty.py)
+PYSIDE_TEST(qmlregistertype_test.py)
PYSIDE_TEST(qqmlapplicationengine_test.py)
PYSIDE_TEST(qqmlnetwork_test.py)
PYSIDE_TEST(qqmlcomponent_test.py)
diff --git a/sources/pyside6/tests/QtQml/listproperty.py b/sources/pyside6/tests/QtQml/listproperty.py
index 8916aefe5..884600d29 100644
--- a/sources/pyside6/tests/QtQml/listproperty.py
+++ b/sources/pyside6/tests/QtQml/listproperty.py
@@ -7,11 +7,25 @@ import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from PySide6.QtCore import QObject
-from PySide6.QtQml import ListProperty
+from helper.usesqapplication import UsesQApplication # noqa: E402, F401
+
+from PySide6.QtCore import QObject, QUrl, Property, qInstallMessageHandler # noqa: E402
+from PySide6.QtQml import ListProperty, QmlElement # noqa: E402
+from PySide6.QtQuick import QQuickView # noqa: E402
+
+
+QML_IMPORT_NAME = "test.ListPropertyTest"
+QML_IMPORT_MAJOR_VERSION = 1
+
+output_messages = []
+
+
+def message_handler(mode, context, message):
+ global output_messages
+ output_messages.append(f"{message}")
class InheritsQObject(QObject):
@@ -22,7 +36,46 @@ def dummyFunc():
pass
-class TestListProperty(unittest.TestCase):
+@QmlElement
+class Person(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent=None)
+ self._name = ''
+ self._friends = []
+
+ def appendFriend(self, friend):
+ self._friends.append(friend)
+
+ def friendCount(self):
+ return len(self._friends)
+
+ def friend(self, index):
+ return self._friends[index]
+
+ def removeLastItem(self):
+ if len(self._friends) > 0:
+ self._friends.pop()
+
+ def replace(self, index, friend):
+ if 0 <= index < len(self._friends):
+ self._friends[index] = friend
+
+ def clear(self):
+ self._friends.clear()
+
+ @Property(str, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, value):
+ self._name = value
+
+ friends = ListProperty(QObject, append=appendFriend, count=friendCount, at=friend,
+ removeLast=removeLastItem, replace=replace, clear=clear)
+
+
+class TestListProperty(UsesQApplication):
def testIt(self):
# Verify that type checking works properly
@@ -31,7 +84,7 @@ class TestListProperty(unittest.TestCase):
try:
ListProperty(QObject)
ListProperty(InheritsQObject)
- except:
+ except Exception:
type_check_error = True
self.assertFalse(type_check_error)
@@ -47,21 +100,37 @@ class TestListProperty(unittest.TestCase):
method_check_error = False
try:
- ListProperty(QObject, append=None, at=None, count=None, replace=None, clear=None, removeLast=None) # Explicitly setting None
+ ListProperty(QObject, append=None, at=None, count=None, replace=None, clear=None,
+ removeLast=None) # Explicitly setting None
ListProperty(QObject, append=dummyFunc)
ListProperty(QObject, count=dummyFunc, at=dummyFunc)
- except:
+ except Exception:
method_check_error = True
self.assertFalse(method_check_error)
try:
- ListPropery(QObject, append=QObject())
- except:
+ ListProperty(QObject, append=QObject())
+ except Exception:
method_check_error = True
self.assertTrue(method_check_error)
+ def testListPropParameters(self):
+ global output_messages
+ qInstallMessageHandler(message_handler)
+ view = QQuickView()
+ file = Path(__file__).resolve().parent / 'listproperty.qml'
+ self.assertTrue(file.is_file())
+ view.setSource(QUrl.fromLocalFile(file))
+ view.show()
+ self.assertEqual(output_messages[0], "List length: 3")
+ self.assertEqual(output_messages[1], "First element: Alice")
+ self.assertEqual(output_messages[2], "Removing last item: Charlie")
+ self.assertEqual(output_messages[3], "Replacing last item: Bob")
+ self.assertEqual(output_messages[4], "Replaced last item: David")
+ self.assertEqual(output_messages[5], "List length after clearing: 0")
+
if __name__ == '__main__':
unittest.main()
diff --git a/sources/pyside6/tests/QtQml/listproperty.qml b/sources/pyside6/tests/QtQml/listproperty.qml
new file mode 100644
index 000000000..7b71e30ba
--- /dev/null
+++ b/sources/pyside6/tests/QtQml/listproperty.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 2.0
+import test.ListPropertyTest
+
+Rectangle {
+ width: 360
+ height: 360
+
+ Person {
+ id: person
+ friends: [
+ Person{
+ name: "Alice"
+ },
+ Person{
+ name: "Bob"
+ },
+ Person{
+ name: "Charlie"
+ }
+ ]
+ }
+
+ Person{
+ id: david
+ name: "David"
+ }
+
+ Component.onCompleted: {
+ // Access the length of the list
+ console.log("List length: " + person.friends.length);
+
+ // Access the first element of the list
+ console.log("First element: " + person.friends[0].name);
+
+ // Remove the last item of the list
+ console.log("Removing last item: " + person.friends.pop().name);
+
+ // Repalce the last item of the list
+ console.log("Replacing last item: " + person.friends[person.friends.length - 1].name);
+ person.friends[person.friends.length - 1] = david;
+ console.log("Replaced last item: " + person.friends[person.friends.length - 1].name);
+
+ // Clear the list
+ person.friends = [];
+ console.log("List length after clearing: " + person.friends.length);
+ }
+}
diff --git a/sources/pyside6/tests/QtQml/qmlregistertype_test.py b/sources/pyside6/tests/QtQml/qmlregistertype_test.py
new file mode 100644
index 000000000..0042d6fd3
--- /dev/null
+++ b/sources/pyside6/tests/QtQml/qmlregistertype_test.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import os
+import sys
+import unittest
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from helper.usesqapplication import UsesQApplication
+
+
+from PySide6.QtCore import QCoreApplication, QObject # noqa: F401
+from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterType
+
+
+class BaseClass(QObject):
+ def __init__(self, p=None):
+ super().__init__(p)
+
+
+class ChildClass(BaseClass):
+ def __init__(self, p=None):
+ super().__init__(p)
+
+
+class TestQmlRegisterType(UsesQApplication):
+ """Test the legacy QML register functions."""
+
+ def test(self):
+ qmlRegisterType(BaseClass, 'test', 1, 0, 'BaseClass')
+ qmlRegisterType(ChildClass, 'test', 1, 0, 'ChildClass')
+ # PYSIDE-2709: qmlRegisterType() would set additional class info
+ # on the meta objects for registration which caused another meta
+ # object to be created, breaking inheritance.
+ child = ChildClass()
+ base = BaseClass()
+ self.assertTrue(child.metaObject().inherits(base.metaObject()))
+
+ engine = QQmlApplicationEngine()
+ file = Path(__file__).resolve().parent / 'qmlregistertype_test.qml'
+
+ engine.load(file)
+ rootObjects = engine.rootObjects()
+ self.assertTrue(rootObjects)
+ self.assertTrue(type(rootObjects[0]), ChildClass)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/sources/pyside6/tests/QtQml/qmlregistertype_test.qml b/sources/pyside6/tests/QtQml/qmlregistertype_test.qml
new file mode 100644
index 000000000..108bb84b1
--- /dev/null
+++ b/sources/pyside6/tests/QtQml/qmlregistertype_test.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import test
+
+ChildClass {
+}
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
index 50ef1b94a..22f6267a7 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
@@ -1574,7 +1574,8 @@ bool AbstractMetaBuilderPrivate::setupInheritance(const AbstractMetaClassPtr &me
&info, &baseContainerType);
if (templ) {
setupInheritance(templ);
- inheritTemplate(metaClass, templ, info);
+ if (!inheritTemplate(metaClass, templ, info))
+ return false;
metaClass->typeEntry()->setBaseContainerType(templ->typeEntry());
return true;
}
@@ -3143,54 +3144,81 @@ AbstractMetaClassPtr
return result;
}
+
+static std::optional<AbstractMetaType>
+ inheritTemplateParameter(const AbstractMetaClassPtr &subclass,
+ const AbstractMetaClassCPtr &templateClass,
+ const TypeInfo &info, QString *errorMessage)
+{
+ QString typeName = info.qualifiedName().join("::"_L1);
+ TypeDatabase *typeDb = TypeDatabase::instance();
+ TypeEntryPtr t;
+ // Check for a non-type template integer parameter, that is, for a base
+ // "template <int R, int C> Matrix<R, C>" and subclass
+ // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of
+ // EnumValueTypeEntry for the integer values encountered on the fly.
+ if (isNumber(typeName)) {
+ t = typeDb->findType(typeName);
+ if (!t) {
+ auto parent = typeSystemTypeEntry(subclass->typeEntry());
+ t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent);
+ }
+ } else {
+ QStringList possibleNames;
+ possibleNames << subclass->qualifiedCppName() + "::"_L1 + typeName;
+ possibleNames << templateClass->qualifiedCppName() + "::"_L1 + typeName;
+ if (subclass->enclosingClass())
+ possibleNames << subclass->enclosingClass()->qualifiedCppName() + "::"_L1 + typeName;
+ possibleNames << typeName;
+
+ for (const QString &possibleName : std::as_const(possibleNames)) {
+ t = typeDb->findType(possibleName);
+ if (t)
+ break;
+ }
+ }
+
+ if (!t) {
+ *errorMessage = msgIgnoringTemplateParameter(typeName,
+ "The corresponding type was not found in the typesystem.");
+ return std::nullopt;
+ }
+
+ if (t->isContainer()) {
+ *errorMessage = msgIgnoringTemplateParameter(typeName,
+ "Template inheritance from nested containers is not supported");
+ return std::nullopt;
+ }
+ AbstractMetaType result(t);
+ result.setConstant(info.isConstant());
+ result.setReferenceType(info.referenceType());
+ result.setIndirectionsV(info.indirectionsV());
+ result.decideUsagePattern();
+ return result;
+}
+
bool AbstractMetaBuilderPrivate::inheritTemplate(const AbstractMetaClassPtr &subclass,
const AbstractMetaClassCPtr &templateClass,
const TypeInfo &info)
{
AbstractMetaTypeList templateTypes;
+ QString errorMessage;
for (const TypeInfo &i : info.instantiations()) {
- QString typeName = i.qualifiedName().join(u"::"_s);
- TypeDatabase *typeDb = TypeDatabase::instance();
- TypeEntryPtr t;
- // Check for a non-type template integer parameter, that is, for a base
- // "template <int R, int C> Matrix<R, C>" and subclass
- // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of
- // EnumValueTypeEntry for the integer values encountered on the fly.
- if (isNumber(typeName)) {
- t = typeDb->findType(typeName);
- if (!t) {
- auto parent = typeSystemTypeEntry(subclass->typeEntry());
- t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent);
- }
- } else {
- QStringList possibleNames;
- possibleNames << subclass->qualifiedCppName() + u"::"_s + typeName;
- possibleNames << templateClass->qualifiedCppName() + u"::"_s + typeName;
- if (subclass->enclosingClass())
- possibleNames << subclass->enclosingClass()->qualifiedCppName() + u"::"_s + typeName;
- possibleNames << typeName;
-
- for (const QString &possibleName : std::as_const(possibleNames)) {
- t = typeDb->findType(possibleName);
- if (t)
- break;
- }
- }
-
- if (t) {
- AbstractMetaType temporaryType(t);
- temporaryType.setConstant(i.isConstant());
- temporaryType.setReferenceType(i.referenceType());
- temporaryType.setIndirectionsV(i.indirectionsV());
- temporaryType.decideUsagePattern();
- templateTypes << temporaryType;
+ const auto typeO = inheritTemplateParameter(subclass, templateClass, i, &errorMessage);
+ if (typeO.has_value()) {
+ templateTypes.append(typeO.value());
} else {
- qCWarning(lcShiboken).noquote().nospace()
- << "Ignoring template parameter " << typeName << " from "
- << info.toString() << ". The corresponding type was not found in the typesystem.";
+ errorMessage = msgInheritTemplateIssue(subclass, info, errorMessage);
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
}
}
+ if (templateTypes.isEmpty()) {
+ errorMessage = msgInheritTemplateIssue(subclass, info,
+ "No template parameters could be inherited"_L1);
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
+ return false;
+ }
return inheritTemplate(subclass, templateClass, templateTypes);
}
diff --git a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
index dcfc74bbb..3ec07509d 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetatype.cpp
@@ -543,6 +543,7 @@ void AbstractMetaType::decideUsagePattern()
pattern = ObjectPattern;
}
setTypeUsagePattern(pattern);
+ Q_ASSERT(pattern != ContainerPattern || !d->m_instantiations.isEmpty());
}
bool AbstractMetaTypeData::hasTemplateChildren() const
diff --git a/sources/shiboken6/ApiExtractor/messages.cpp b/sources/shiboken6/ApiExtractor/messages.cpp
index f9f46f520..b1f0b240e 100644
--- a/sources/shiboken6/ApiExtractor/messages.cpp
+++ b/sources/shiboken6/ApiExtractor/messages.cpp
@@ -481,6 +481,21 @@ QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &s
+ u"\" for instantiation of \""_s +smartPointerType + u"\"."_s;
}
+QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass,
+ const TypeInfo &info,
+ const QString &what)
+{
+ return "While inheriting template "_L1 + subclass->name()
+ + " from "_L1 + info.toString() + ": "_L1 + what;
+}
+
+QString msgIgnoringTemplateParameter(const QString &typeName,
+ const char *why)
+{
+ return "Ignoring template parameter "_L1 + typeName +
+ ": "_L1 + QLatin1StringView(why);
+}
+
QString msgInvalidSmartPointerType(const TypeInfo &i)
{
return u"Invalid smart pointer type \""_s +i.toString() + u"\"."_s;
diff --git a/sources/shiboken6/ApiExtractor/messages.h b/sources/shiboken6/ApiExtractor/messages.h
index 2899cbdfa..e3f582b49 100644
--- a/sources/shiboken6/ApiExtractor/messages.h
+++ b/sources/shiboken6/ApiExtractor/messages.h
@@ -126,6 +126,10 @@ QString msgUnableToTranslateType(const TypeInfo &typeInfo,
QString msgCannotFindTypeEntry(const QString &t);
QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &smartPointerType);
+QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass,
+ const TypeInfo &info, const QString &what);
+QString msgIgnoringTemplateParameter(const QString &typeName,
+ const char *why);
QString msgInvalidSmartPointerType(const TypeInfo &i);
QString msgCannotFindSmartPointerInstantion(const TypeInfo &i);
diff --git a/sources/shiboken6/doc/typediscovery.rst b/sources/shiboken6/doc/typediscovery.rst
new file mode 100644
index 000000000..76d3adf7b
--- /dev/null
+++ b/sources/shiboken6/doc/typediscovery.rst
@@ -0,0 +1,145 @@
+.. _typediscovery:
+
+**************
+Type Discovery
+**************
+
+When converting objects which are part of a class hierarchy from a pointer to a
+base class, it is expected to get the Python type of the actual, most derived
+type, as opposed to C++ which requires a cast for this:
+
+.. code-block:: python
+
+ def event(self, event):
+ if event.type() == QEvent.Type.MousePress:
+ self.do_things(event.position())
+ ...
+
+
+.. code-block:: c++
+
+ bool event(QEvent *event) override
+ {
+ if (event->type() == QEvent::MousePress) {
+ auto *mouseEvent = static_cast<QMouseEvent *>(event);
+ doThings(mouseEvent->position());
+ ...
+ }
+
+The process of determining the type of the event is called `type discovery`.
+
+Shiboken generates code to automatically detect the type. First, it tries to
+find a converter for the name obtained by ``typeid(*pointer).name()``. This
+should normally work as this name is registered by the binding. If that fails,
+it starts walking a type inheritance graph built up in libshiboken to find the
+most derived class by using a cast function (``dynamic_cast<>`` by default) to
+check.
+
+For normal class hierarchies with virtual destructors, no special handling
+is required since ``typeid()`` usually detects the proper class name.
+
+Multiple inheritance
+====================
+
+In case of multiple inheritance in C++, the conversion to the derived class is
+not done in case it is not a single-line direct inheritance. For example, in
+Qt, the class ``QWidget`` inherits both ``QObject`` (base of the ``QObject``
+hierarchy) and ``QPaintDevice``.
+
+When calling a function returning a ``QPaintDevice *``, for example
+``QPainter.device()``, a Python type representing ``QPaintDevice`` is returned
+instead of the underlying widget type. This restriction exists because the
+underlying pointer in C++ is a pointer to a ``QPaintDevice *`` and differs from
+the pointer to the ``QWidget``.
+
+Hierarchies of classes with non-virtual destructors
+===================================================
+
+There are some hierarchies of value-ish C++ classes that do not have virtual
+destructors. This makes type discovery based on ``typeid()`` and
+``dynamic_cast<>`` impossible.
+
+Examples in Qt are the ``QStyleOption``-derived or the ``QGradient``
+-derived classes.
+
+For such classes, some attributes need to be specified on the type entries:
+
+Primarily, a :ref:`polymorphic-id-expression` attribute
+must be specified to be used as a check replacing ``dynamic_cast<>``.
+
+In addition, a :ref:`polymorphic-name-function` attribute can be specified.
+This replaces the type name guess obtained by ``typeid()`` and is mainly a hint
+to speed things up by skipping the checks for each type in the inheritance
+graph.
+
+A :ref:`polymorphic-base` attribute identifies the base class of a hierarchy.
+It should be given in case the base class inherits from another class to
+prevent the logic from going below the base class.
+
+Using type discovery attributes for class hierarchies with virtual destructors
+==============================================================================
+
+It is possible to use :ref:`polymorphic-id-expression` and
+:ref:`polymorphic-name-function` for normal class hierarchies with virtual
+destructors as well since they basically replace ``typeid()`` and
+``dynamic_cast<>``. This makes sense if expressions can be specified that are
+faster than the checks on virtual tables.
+
+Specifying :ref:`polymorphic-base` can also make sense for generating special
+cast functions in case of multiple inheritance. For example, in Qt,
+``QWindow``, ``QLayout``, ``QWidget`` are base classes of hierarchies. Since
+they all inherit from ``QObject``, indicating the base classes prevents
+the logic from using ``QObject`` as a base class.
+
+.. _typediscovery-attributes:
+
+Type discovery attributes reference
+===================================
+
+The following attributes related to type discovery may be be specified on the
+:ref:`object-type` or :ref:`value-type` elements:
+
+.. _polymorphic-id-expression:
+
+polymorphic-id-expression
++++++++++++++++++++++++++
+
+The **polymorphic-id-expression** attribute specifies an expression checking
+whether a base class pointer is of the matching type. For example, in a
+``virtual eventHandler(BaseEvent *e)`` function, this is used to construct a
+Python wrapper matching the derived class (for example, a ``MouseEvent`` or
+similar). The attribute value may contain placeholders:
+
+%1
+ Fully qualified class name
+
+%B
+ Fully qualified name of the base class (found by base class
+ search or as indicated by **polymorphic-base**).
+
+To check for a class inheriting ``BaseEvent``, specify:
+
+.. code-block:: xml
+
+ <object-type name="MouseEvent"
+ polymorphic-id-expression="%B-&gt;type() == BaseEvent::MouseEvent"/>
+
+.. _polymorphic-name-function:
+
+polymorphic-name-function
++++++++++++++++++++++++++
+
+The **polymorphic-name-function** attribute specifies the name of a function
+returning the type name of a derived class on the base class type entry.
+Normally, ``typeid(ptr).name()`` is used for this.
+
+The function is expected to return ``const char *``.
+
+.. _polymorphic-base:
+
+polymorphic-base
+++++++++++++++++
+
+The boolean **polymorphic-base** attribute indicates whether the class is the
+base class of a class hierarchy. It is used for the *%B* placeholder in
+**polymorphic-id-expression** and for cast operations in multiple inheritance.
diff --git a/sources/shiboken6/doc/typesystem.rst b/sources/shiboken6/doc/typesystem.rst
index e1e4fdda2..26f929801 100644
--- a/sources/shiboken6/doc/typesystem.rst
+++ b/sources/shiboken6/doc/typesystem.rst
@@ -65,3 +65,4 @@ Extra options and Python caveats
typesystem_solving_compilation.rst
typesystem_specialfunctions.rst
+ typediscovery.rst
diff --git a/sources/shiboken6/doc/typesystem_converters.rst b/sources/shiboken6/doc/typesystem_converters.rst
index 7bdabc49c..ab6fba930 100644
--- a/sources/shiboken6/doc/typesystem_converters.rst
+++ b/sources/shiboken6/doc/typesystem_converters.rst
@@ -233,61 +233,3 @@ Variables & Functions
**%CHECKTYPE[CPPTYPE]**
Replaced by a |project| type checking function for a Python variable.
The C++ type is indicated by ``CPPTYPE``.
-
-
-.. _oldconverters:
-
-Converting The Old Converters
-=============================
-
-If you use |project| for your bindings, and has defined some type conversions
-using the ``Shiboken::Converter`` template, then you must update your converters
-to the new scheme.
-
-Previously your conversion rules were declared in one line, like this:
-
-
-.. code-block:: xml
-
- <primitive-type name="Complex" target-lang-api-name="PyComplex">
- <include file-name="complex.h" location="global"/>
- <conversion-rule file="complex_conversions.h"/>
- </primitive-type>
-
-
-And implemented in a separate C++ file, like this:
-
-
-.. code-block:: c++
-
- namespace Shiboken {
- template<> struct Converter<Complex>
- {
- static inline bool checkType(PyObject* pyObj) {
- return PyComplex_Check(pyObj);
- }
- static inline bool isConvertible(PyObject* pyObj) {
- return PyComplex_Check(pyObj);
- }
- static inline PyObject* toPython(void* cppobj) {
- return toPython(*reinterpret_cast<Complex*>(cppobj));
- }
- static inline PyObject* toPython(const Complex& cpx) {
- return PyComplex_FromDoubles(cpx.real(), cpx.imag());
- }
- static inline Complex toCpp(PyObject* pyobj) {
- double real = PyComplex_RealAsDouble(pyobj);
- double imag = PyComplex_ImagAsDouble(pyobj);
- return Complex(real, imag);
- }
- };
- }
-
-
-In this case, the parts of the implementation that will be used in the new
-conversion-rule are the ones in the two last method
-``static inline PyObject* toPython(const Complex& cpx)`` and
-``static inline Complex toCpp(PyObject* pyobj)``. The ``isConvertible`` method
-is gone, and the ``checkType`` is now an attribute of the :ref:`add-conversion <add-conversion>`
-tag. Refer back to the first example in this page and you will be able to
-correlate the above template with the new scheme of conversion rule definition.
diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst
index 66e68ae2b..f65b79bb4 100644
--- a/sources/shiboken6/doc/typesystem_specifying_types.rst
+++ b/sources/shiboken6/doc/typesystem_specifying_types.rst
@@ -536,37 +536,8 @@ type system has this attribute set, the heuristics will be applied
to all classes. In shiboken 7, it will be mandatory to set the
attribute.
-The *optional* **polymorphic-id-expression** attribute specifies an
-expression checking whether a base class pointer is of the matching
-type. For example, in a ``virtual eventHandler(BaseEvent *e)``
-function, this is used to construct a Python wrapper matching
-the derived class (for example, a ``MouseEvent`` or similar).
-The attribute value may contain placeholders:
-
-%1
- Fully qualified class name
-
-%B
- Fully qualified name of the base class (found by base class
- search or as indicated by **polymorphic-base**).
-
-To check for a class inheriting ``BaseEvent``, specify:
-
-.. code-block:: xml
-
- <object-type name="MouseEvent"
- polymorphic-id-expression="%B-&gt;type() == BaseEvent::MouseEvent"/>
-
-The *optional* **polymorphic-name-function** specifies the name of a
-function returning the type name of a derived class on the base class
-type entry. Normally, ``typeid(ptr).name()`` is used for this.
-However, this fails if the type hierarchy does not have virtual functions.
-In this case, a function is required which typically decides depending
-on some type enumeration.
-
-The *optional* **polymorphic-base** attribute indicates
-whether the class is the base class of a class hierarchy
-(used for the *%B* placeholder in **polymorphic-id-expression**).
+For the *optional* **polymorphic-id-expression**, **polymorphic-name-function**
+and **polymorphic-base** attributes, see :ref:`typediscovery-attributes`.
interface-type
^^^^^^^^^^^^^^
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
index ad4071c35..84d3c93d3 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -61,6 +61,7 @@ static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nu
static constexpr auto virtualMethodStaticReturnVar = "result"_L1;
static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1;
+static const char initInheritanceFunction[] = "initInheritance";
static QString mangleName(QString name)
{
@@ -1638,7 +1639,7 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas
<< "if (pyOut) {\n" << indent
<< "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent
<< "}\n"
- << "bool changedTypeName = false;\n"
+ << "bool exactType = false;\n"
<< "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn);
const char *typeName = )";
@@ -1648,15 +1649,11 @@ const char *typeName = )";
else
c << nameFunc << "(tCppIn);\n";
c << R"(auto *sbkType = Shiboken::ObjectType::typeForTypeName(typeName);
-if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) {
- typeName = Shiboken::typeNameOf(typeid(*tCppIn).name());
- changedTypeName = true;
-}
+if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType))
+ exactType = true;
)"
<< "PyObject *result = Shiboken::Object::newObject(" << cpythonType
- << R"(, const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName);
-if (changedTypeName)
- delete [] typeName;
+ << R"(, const_cast<void *>(cppIn), false, exactType, typeName);
return result;)";
}
std::swap(targetTypeName, sourceTypeName);
@@ -4260,6 +4257,12 @@ QString CppGenerator::writeContainerConverterInitialization(TextStream &s,
return converter;
}
+QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te)
+{
+ return cppApiVariableName(te->targetLangPackage()) + u'['
+ + getTypeIndexVariableName(te) + u']';
+}
+
void CppGenerator::writeExtendedConverterInitialization(TextStream &s,
const TypeEntryCPtr &externalType,
const AbstractMetaClassCList &conversions)
@@ -4267,15 +4270,13 @@ void CppGenerator::writeExtendedConverterInitialization(TextStream &s,
s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName()
<< ".\n";
for (const auto &sourceClass : conversions) {
- const QString converterVar = cppApiVariableName(externalType->targetLangPackage()) + u'['
- + getTypeIndexVariableName(externalType) + u']';
QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry());
QString targetTypeName = fixedCppTypeName(externalType);
QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
if (!externalType->isPrimitive())
s << cpythonTypeNameExt(externalType) << ";\n";
- writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
+ writeAddPythonToCppConversion(s, typeInitStruct(externalType), toCpp, isConv);
}
}
@@ -5473,6 +5474,27 @@ QStringList CppGenerator::pyBaseTypes(const AbstractMetaClassCPtr &metaClass)
return result;
}
+void CppGenerator::writeInitInheritance(TextStream &s) const
+{
+ s << "static void " << initInheritanceFunction << "()\n{\n" << indent
+ << "auto &bm = Shiboken::BindingManager::instance();\n"
+ << sbkUnusedVariableCast("bm");
+ for (const auto &cls : api().classes()){
+ auto te = cls->typeEntry();
+ if (shouldGenerate(te)) {
+ const auto &baseEntries = pyBaseTypeEntries(cls);
+ if (!baseEntries.isEmpty()) {
+ const QString childTypeInitStruct = typeInitStruct(cls->typeEntry());
+ for (const auto &baseEntry : baseEntries) {
+ s << "bm.addClassInheritance(&" << typeInitStruct(baseEntry) << ",\n"
+ << Pad(' ', 23) << '&' << childTypeInitStruct << ");\n";
+ }
+ }
+ }
+ }
+ s << outdent << "}\n\n";
+}
+
void CppGenerator::writeClassRegister(TextStream &s,
const AbstractMetaClassCPtr &metaClass,
const GeneratorContext &classContext,
@@ -6337,6 +6359,8 @@ bool CppGenerator::finishGeneration()
// PYSIDE-510: Create a signatures string for the introspection feature.
writeSignatureStrings(s, signatureStream.toString(), moduleName(), "global functions");
+ writeInitInheritance(s);
+
// Write module init function
const QString globalModuleVar = pythonModuleObjectName();
s << "extern \"C\" LIBSHIBOKEN_EXPORT PyObject *PyInit_"
@@ -6474,7 +6498,8 @@ bool CppGenerator::finishGeneration()
}
}
- s << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent
+ s << '\n' << initInheritanceFunction << "();\n"
+ << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent
<< "PyErr_Print();\n"
<< "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n"
<< outdent << "}\n";
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h
index 1ad576895..a31c2ca14 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.h
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.h
@@ -395,6 +395,7 @@ private:
static void writeSignatureStrings(TextStream &s, const QString &signatures,
const QString &arrayName,
const char *comment);
+ void writeInitInheritance(TextStream &s) const;
void writeClassRegister(TextStream &s,
const AbstractMetaClassCPtr &metaClass,
const GeneratorContext &classContext,
@@ -480,6 +481,8 @@ private:
const AbstractMetaType &type,
const ApiExtractorResult &api);
void writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &ype) const;
+
+ static QString typeInitStruct(const TypeEntryCPtr &te);
static void writeExtendedConverterInitialization(TextStream &s,
const TypeEntryCPtr &externalType,
const AbstractMetaClassCList &conversions);
diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp
index f34e053ca..0dfd3cff8 100644
--- a/sources/shiboken6/libshiboken/basewrapper.cpp
+++ b/sources/shiboken6/libshiboken/basewrapper.cpp
@@ -129,31 +129,6 @@ static PyGetSetDef SbkObjectType_tp_getset[] = {
static PyTypeObject *createObjectTypeType()
{
- // PYSIDE-2676: When using the new type extension, we need to use an
- // extra meta type that provides the extra size.
- // This is a hairy part of Python 3.12 .
- //
- // The problem here is that we use the type extension both in types
- // and also in meta types. This was invisible with extender dicts.
- // Please study carefully:
- // https://docs.python.org/3/c-api/type.html#c.PyType_Spec.basicsize
-
- PyType_Slot SbkObjectTypeMeta_Type_slots[] = {
- {Py_tp_base, static_cast<void *>(&PyType_Type)},
- {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)},
- {0, nullptr}
- };
-
- PyType_Spec SbkObjectTypeMeta_Type_spec = {
- "1:Shiboken.ObjectTypeMeta",
- -long(sizeof(SbkObjectTypePrivate)),
- 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS,
- SbkObjectTypeMeta_Type_slots,
- };
-
- auto specMeta = &SbkObjectTypeMeta_Type_spec;
-
PyType_Slot SbkObjectType_Type_slots[] = {
{Py_tp_dealloc, reinterpret_cast<void *>(SbkObjectType_tp_dealloc)},
{Py_tp_getattro, reinterpret_cast<void *>(mangled_type_getattro)},
@@ -191,14 +166,9 @@ static PyTypeObject *createObjectTypeType()
SbkObjectType_Type_slots,
};
- if (_PepRuntimeVersion() >= 0x030C00) {
- auto *meta = SbkType_FromSpec(specMeta);
- auto spec = &SbkObjectType_Type_spec_312;
- return SbkType_FromSpecWithMeta(spec, meta);
- }
-
- auto spec = &SbkObjectType_Type_spec;
- return SbkType_FromSpec(spec);
+ return SbkType_FromSpec(_PepRuntimeVersion() >= 0x030C00 ?
+ &SbkObjectType_Type_spec_312 :
+ &SbkObjectType_Type_spec);
}
PyTypeObject *SbkObjectType_TypeF(void)
@@ -1007,11 +977,6 @@ introduceWrapperType(PyObject *enclosingObject,
auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF());
- for (Py_ssize_t i = 0; i < basesSize; ++i) {
- auto *st = reinterpret_cast<PyTypeObject *>(PySequence_Fast_GET_ITEM(bases, i));
- BindingManager::instance().addClassInheritance(st, type);
- }
-
auto sotp = PepType_SOTP(type);
if (wrapperFlags & DeleteInMainThread)
sotp->delete_in_main_thread = 1;
diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp
index 60460c6ab..83c927ae5 100644
--- a/sources/shiboken6/libshiboken/bindingmanager.cpp
+++ b/sources/shiboken6/libshiboken/bindingmanager.cpp
@@ -7,6 +7,7 @@
#include "bindingmanager.h"
#include "gilstate.h"
#include "helper.h"
+#include "sbkmodule.h"
#include "sbkstring.h"
#include "sbkstaticstrings.h"
#include "sbkfeature_base.h"
@@ -21,6 +22,29 @@
#include <unordered_map>
#include <unordered_set>
+// GraphNode for the dependency graph. It keeps a pointer to
+// the TypeInitStruct to be able to lazily create the type and hashes
+// by the full type name.
+struct GraphNode
+{
+ explicit GraphNode(Shiboken::Module::TypeInitStruct *i) : name(i->fullName), initStruct(i) {}
+ explicit GraphNode(const char *n) : name(n), initStruct(nullptr) {} // Only for searching
+
+ std::string_view name;
+ Shiboken::Module::TypeInitStruct *initStruct;
+
+ friend bool operator==(const GraphNode &n1, const GraphNode &n2) { return n1.name == n2.name; }
+ friend bool operator!=(const GraphNode &n1, const GraphNode &n2) { return n1.name != n2.name; }
+};
+
+template <>
+struct std::hash<GraphNode> {
+ size_t operator()(const GraphNode &n) const noexcept
+ {
+ return std::hash<std::string_view>{}(n.name);
+ }
+};
+
namespace Shiboken
{
@@ -56,28 +80,45 @@ public:
}
};
-class Graph : public BaseGraph<PyTypeObject *>
+class Graph : public BaseGraph<GraphNode>
{
public:
- Graph() = default;
+ using TypeCptrPair = BindingManager::TypeCptrPair;
- BindingManager::TypeCptrPair identifyType(void *cptr, PyTypeObject *type, PyTypeObject *baseType) const;
+ TypeCptrPair identifyType(void *cptr, PyTypeObject *type, PyTypeObject *baseType) const
+ {
+ return identifyType(cptr, GraphNode(type->tp_name), type, baseType);
+ }
bool dumpTypeGraph(const char *fileName) const;
+
+private:
+ TypeCptrPair identifyType(void *cptr, const GraphNode &typeNode, PyTypeObject *type,
+ PyTypeObject *baseType) const;
};
-BindingManager::TypeCptrPair Graph::identifyType(void *cptr, PyTypeObject *type, PyTypeObject *baseType) const
+Graph::TypeCptrPair Graph::identifyType(void *cptr,
+ const GraphNode &typeNode, PyTypeObject *type,
+ PyTypeObject *baseType) const
{
- auto edgesIt = m_edges.find(type);
+ assert(typeNode.initStruct != nullptr || type != nullptr);
+ auto edgesIt = m_edges.find(typeNode);
if (edgesIt != m_edges.end()) {
const NodeList &adjNodes = edgesIt->second;
- for (PyTypeObject *node : adjNodes) {
- auto newType = identifyType(cptr, node, baseType);
+ for (const auto &node : adjNodes) {
+ auto newType = identifyType(cptr, node, nullptr, baseType);
if (newType.first != nullptr)
return newType;
}
}
+ if (type == nullptr) {
+ if (typeNode.initStruct->type == nullptr) // Layzily create type
+ type = Shiboken::Module::get(*typeNode.initStruct);
+ else
+ type = typeNode.initStruct->type;
+ }
+
auto *sotp = PepType_SOTP(type);
if (sotp->type_discovery != nullptr) {
if (void *derivedCPtr = sotp->type_discovery(cptr, baseType))
@@ -86,9 +127,8 @@ BindingManager::TypeCptrPair Graph::identifyType(void *cptr, PyTypeObject *type,
return {nullptr, nullptr};
}
-static void formatDotNode(const char *nameC, std::ostream &file)
+static void formatDotNode(std::string_view name, std::ostream &file)
{
- std::string_view name(nameC);
auto lastDot = name.rfind('.');
file << " \"" << name << "\" [ label=";
if (lastDot != std::string::npos) {
@@ -109,15 +149,15 @@ bool Graph::dumpTypeGraph(const char *fileName) const
file << "digraph D {\n";
// Define nodes with short names
- for (const auto *node : nodeSet())
- formatDotNode(node->tp_name, file);
+ for (const auto &node : nodeSet())
+ formatDotNode(node.name, file);
// Write edges
for (const auto &p : m_edges) {
- auto *node1 = p.first;
+ const auto &node1 = p.first;
const NodeList &nodeList = p.second;
- for (const PyTypeObject *node2 : nodeList)
- file << " \"" << node2->tp_name << "\" -> \"" << node1->tp_name << "\"\n";
+ for (const auto &node2 : nodeList)
+ file << " \"" << node2.name << "\" -> \"" << node1.name << "\"\n";
}
file << "}\n";
return true;
@@ -380,9 +420,10 @@ PyObject *BindingManager::getOverride(const void *cptr,
return nullptr;
}
-void BindingManager::addClassInheritance(PyTypeObject *parent, PyTypeObject *child)
+void BindingManager::addClassInheritance(Module::TypeInitStruct *parent,
+ Module::TypeInitStruct *child)
{
- m_d->classHierarchy.addEdge(parent, child);
+ m_d->classHierarchy.addEdge(GraphNode(parent), GraphNode(child));
}
BindingManager::TypeCptrPair BindingManager::findDerivedType(void *cptr, PyTypeObject *type) const
diff --git a/sources/shiboken6/libshiboken/bindingmanager.h b/sources/shiboken6/libshiboken/bindingmanager.h
index 281de2c73..54c4e486a 100644
--- a/sources/shiboken6/libshiboken/bindingmanager.h
+++ b/sources/shiboken6/libshiboken/bindingmanager.h
@@ -15,6 +15,10 @@ struct SbkObject;
namespace Shiboken
{
+namespace Module {
+struct TypeInitStruct;
+}
+
struct DestructorEntry;
using ObjectVisitor = void (*)(SbkObject *, void *);
@@ -40,7 +44,7 @@ public:
SbkObject *retrieveWrapper(const void *cptr);
PyObject *getOverride(const void *cptr, PyObject *nameCache[], const char *methodName);
- void addClassInheritance(PyTypeObject *parent, PyTypeObject *child);
+ void addClassInheritance(Module::TypeInitStruct *parent, Module::TypeInitStruct *child);
/// Try to find the correct type of cptr via type discovery knowing that it's at least
/// of type \p type. If a derived class is found, it returns a cptr cast to the type
/// (which may be different in case of multiple inheritance.
diff --git a/sources/shiboken6/libshiboken/pep384impl.cpp b/sources/shiboken6/libshiboken/pep384impl.cpp
index 4b3759456..f926107e2 100644
--- a/sources/shiboken6/libshiboken/pep384impl.cpp
+++ b/sources/shiboken6/libshiboken/pep384impl.cpp
@@ -1009,9 +1009,12 @@ long _PepRuntimeVersion()
SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type)
{
+ // PYSIDE-2676: Use the meta type explicitly.
+ // A derived type would fail the offset calculation.
+ static auto *meta = SbkObjectType_TypeF();
assert(SbkObjectType_Check(type));
auto *obType = reinterpret_cast<PyObject *>(type);
- void *data = PyObject_GetTypeData(obType, Py_TYPE(obType));
+ void *data = PyObject_GetTypeData(obType, meta);
return reinterpret_cast<SbkObjectTypePrivate *>(data);
}
@@ -1061,11 +1064,12 @@ static thread_local SbkObjectTypePrivate *SOTP_value{};
SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type)
{
+ static auto *meta = SbkObjectType_TypeF();
static bool use_312 = _PepRuntimeVersion() >= 0x030C00;
assert(SbkObjectType_Check(type));
if (use_312) {
auto *obType = reinterpret_cast<PyObject *>(type);
- void *data = PepObject_GetTypeData(obType, Py_TYPE(obType));
+ void *data = PepObject_GetTypeData(obType, meta);
return reinterpret_cast<SbkObjectTypePrivate *>(data);
}
if (type == SOTP_key)
diff --git a/sources/shiboken6/libshiboken/sbknumpy.cpp b/sources/shiboken6/libshiboken/sbknumpy.cpp
index 2e1c64d73..b6422e73f 100644
--- a/sources/shiboken6/libshiboken/sbknumpy.cpp
+++ b/sources/shiboken6/libshiboken/sbknumpy.cpp
@@ -29,10 +29,8 @@ static void initNumPy()
// Expanded from macro "import_array" in __multiarray_api.h
// Make sure to read about the magic defines PY_ARRAY_UNIQUE_SYMBOL etc.,
// when changing this or spreading the code over several source files.
- if (_import_array() < 0) {
+ if (_import_array() < 0)
PyErr_Print();
- PyErr_Clear();
- }
}
#endif // HAVE_NUMPY