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--build_scripts/wheel_files.py2
-rw-r--r--doc/changelogs/changes-6.7.161
-rw-r--r--examples/graphs/3d/widgetgallery/doc/widgetgallery.rst11
-rw-r--r--examples/graphs/3d/widgetgraphgallery/axesinputhandler.py (renamed from examples/graphs/3d/widgetgallery/axesinputhandler.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/bargraph.py (renamed from examples/graphs/3d/widgetgallery/bargraph.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/custominputhandler.py (renamed from examples/graphs/3d/widgetgallery/custominputhandler.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/layer_1.png (renamed from examples/graphs/3d/widgetgallery/data/layer_1.png)bin34540 -> 34540 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/layer_2.png (renamed from examples/graphs/3d/widgetgallery/data/layer_2.png)bin10553 -> 10553 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/layer_3.png (renamed from examples/graphs/3d/widgetgallery/data/layer_3.png)bin7119 -> 7119 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/license.txt (renamed from examples/graphs/3d/widgetgallery/data/license.txt)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/maptexture.jpg (renamed from examples/graphs/3d/widgetgallery/data/maptexture.jpg)bin352922 -> 352922 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/narrowarrow.mesh (renamed from examples/graphs/3d/widgetgallery/data/narrowarrow.mesh)bin15420 -> 15420 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/oilrig.mesh (renamed from examples/graphs/3d/widgetgallery/data/oilrig.mesh)bin69728 -> 69728 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/pipe.mesh (renamed from examples/graphs/3d/widgetgallery/data/pipe.mesh)bin4760 -> 4760 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/raindata.txt (renamed from examples/graphs/3d/widgetgallery/data/raindata.txt)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/refinery.mesh (renamed from examples/graphs/3d/widgetgallery/data/refinery.mesh)bin75216 -> 75216 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/data/topography.png (renamed from examples/graphs/3d/widgetgallery/data/topography.png)bin395504 -> 395504 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.rst11
-rw-r--r--examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.webp (renamed from examples/graphs/3d/widgetgallery/doc/widgetgallery.webp)bin93150 -> 93150 bytes
-rw-r--r--examples/graphs/3d/widgetgraphgallery/graphmodifier.py (renamed from examples/graphs/3d/widgetgallery/graphmodifier.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/highlightseries.py (renamed from examples/graphs/3d/widgetgallery/highlightseries.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/main.py (renamed from examples/graphs/3d/widgetgallery/main.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/rainfalldata.py (renamed from examples/graphs/3d/widgetgallery/rainfalldata.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/scatterdatamodifier.py (renamed from examples/graphs/3d/widgetgallery/scatterdatamodifier.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/scattergraph.py (renamed from examples/graphs/3d/widgetgallery/scattergraph.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/surfacegraph.py (renamed from examples/graphs/3d/widgetgallery/surfacegraph.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/surfacegraphmodifier.py (renamed from examples/graphs/3d/widgetgallery/surfacegraphmodifier.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/topographicseries.py (renamed from examples/graphs/3d/widgetgallery/topographicseries.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/variantbardatamapping.py (renamed from examples/graphs/3d/widgetgallery/variantbardatamapping.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/variantbardataproxy.py (renamed from examples/graphs/3d/widgetgallery/variantbardataproxy.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/variantdataset.py (renamed from examples/graphs/3d/widgetgallery/variantdataset.py)0
-rw-r--r--examples/graphs/3d/widgetgraphgallery/widgetgraphgallery.pyproject (renamed from examples/graphs/3d/widgetgallery/widgetgallery.pyproject)0
-rw-r--r--sources/pyside-tools/deploy_lib/android/buildozer.py7
-rw-r--r--sources/pyside-tools/deploy_lib/nuitka_helper.py14
-rw-r--r--sources/pyside6/PySide6/QtAsyncio/__init__.py4
-rw-r--r--sources/pyside6/PySide6/QtAsyncio/events.py130
-rw-r--r--sources/pyside6/PySide6/QtAsyncio/futures.py6
-rw-r--r--sources/pyside6/PySide6/QtAsyncio/tasks.py41
-rw-r--r--sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp6
-rw-r--r--sources/pyside6/PySide6/QtCore/typesystem_core_common.xml15
-rw-r--r--sources/pyside6/PySide6/QtGui/CMakeLists.txt1
-rw-r--r--sources/pyside6/PySide6/QtGui/typesystem_gui_common.xml3
-rw-r--r--sources/pyside6/PySide6/QtNetwork/typesystem_network.xml5
-rw-r--r--sources/pyside6/PySide6/QtQml/pysideqmlvolatilebool.cpp11
-rw-r--r--sources/pyside6/PySide6/QtQml/typesystem_qml.xml9
-rw-r--r--sources/pyside6/PySide6/QtQuick/typesystem_quick.xml2
-rw-r--r--sources/pyside6/PySide6/QtWebEngineQuick/CMakeLists.txt13
-rw-r--r--sources/pyside6/PySide6/QtWebEngineQuick/typesystem_webenginequick.xml1
-rw-r--r--sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml7
-rw-r--r--sources/pyside6/PySide6/glue/qtcore.cpp20
-rw-r--r--sources/pyside6/PySide6/glue/qtgui.cpp6
-rw-r--r--sources/pyside6/PySide6/glue/qtuitools.cpp2
-rw-r--r--sources/pyside6/PySide6/glue/qtwidgets.cpp6
-rw-r--r--sources/pyside6/PySide6/qtdatavisualization_helper.h4
-rw-r--r--sources/pyside6/PySide6/qtgraphs_helper.h4
-rw-r--r--sources/pyside6/doc/gettingstarted/index.rst4
-rw-r--r--sources/pyside6/doc/gettingstarted/linux.rst20
-rw-r--r--sources/pyside6/doc/gettingstarted/macOS.rst18
-rw-r--r--sources/pyside6/doc/gettingstarted/windows.rst14
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.cpp3
-rw-r--r--sources/pyside6/libpyside/pyside.cpp53
-rw-r--r--sources/pyside6/libpyside/pysideclassinfo.cpp12
-rw-r--r--sources/pyside6/libpyside/pysideproperty.cpp1
-rw-r--r--sources/pyside6/libpyside/pysidesignal.cpp68
-rw-r--r--sources/pyside6/libpyside/pysidesignal.h4
-rw-r--r--sources/pyside6/libpyside/signalmanager.cpp29
-rw-r--r--sources/pyside6/libpyside/signalmanager.h2
-rw-r--r--sources/pyside6/libpysideqml/pysideqmlregistertype.cpp53
-rw-r--r--sources/pyside6/libpysideqml/pysideqmluncreatable.cpp7
-rw-r--r--sources/pyside6/libpysideqml/pysideqmluncreatable.h3
-rw-r--r--sources/pyside6/plugins/designer/CMakeLists.txt3
-rw-r--r--sources/pyside6/tests/QtCore/CMakeLists.txt1
-rw-r--r--sources/pyside6/tests/QtCore/bug_927.py8
-rw-r--r--sources/pyside6/tests/QtCore/bug_PYSIDE-2745.py37
-rw-r--r--sources/pyside6/tests/QtGui/qbrush_test.py10
-rw-r--r--sources/pyside6/tests/QtQml/CMakeLists.txt1
-rw-r--r--sources/pyside6/tests/QtQml/qmlregistertype_test.py53
-rw-r--r--sources/pyside6/tests/QtQml/qmlregistertype_test.qml7
-rw-r--r--sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py9
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp108
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetatype.cpp1
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp7
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangparser.h4
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp5
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/compilersupport.h2
-rw-r--r--sources/shiboken6/ApiExtractor/messages.cpp15
-rw-r--r--sources/shiboken6/ApiExtractor/messages.h4
-rw-r--r--sources/shiboken6/ApiExtractor/typedatabase.cpp3
-rw-r--r--sources/shiboken6/doc/shibokenmodule.rst5
-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/generator.cpp5
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp87
-rw-r--r--sources/shiboken6/generator/shiboken/headergenerator.cpp4
-rw-r--r--sources/shiboken6/libshiboken/basewrapper.cpp287
-rw-r--r--sources/shiboken6/libshiboken/basewrapper.h45
-rw-r--r--sources/shiboken6/libshiboken/basewrapper_p.h102
-rw-r--r--sources/shiboken6/libshiboken/pep384impl.cpp70
-rw-r--r--sources/shiboken6/libshiboken/pep384impl.h11
-rw-r--r--sources/shiboken6/libshiboken/sbkcontainer.h66
-rw-r--r--sources/shiboken6/libshiboken/sbkconverter.cpp147
-rw-r--r--sources/shiboken6/libshiboken/sbkconverter_p.h6
-rw-r--r--sources/shiboken6/libshiboken/sbkcpptonumpy.cpp6
-rw-r--r--sources/shiboken6/libshiboken/sbkerrors.cpp47
-rw-r--r--sources/shiboken6/libshiboken/sbkerrors.h5
-rw-r--r--sources/shiboken6/libshiboken/sbkmodule.cpp153
-rw-r--r--sources/shiboken6/libshiboken/sbkmodule.h12
-rw-r--r--sources/shiboken6/libshiboken/sbknumpy.cpp4
-rw-r--r--sources/shiboken6/libshiboken/sbkstring.cpp6
-rw-r--r--sources/shiboken6/libshiboken/sbkstring.h2
-rw-r--r--sources/shiboken6/libshiboken/voidptr.cpp7
-rw-r--r--sources/shiboken6/shibokenmodule/shibokenmodule.cpp4
-rw-r--r--sources/shiboken6/shibokenmodule/typesystem_shiboken.xml5
-rw-r--r--sources/shiboken6/tests/libother/othermultiplederived.h2
-rw-r--r--sources/shiboken6/tests/libsample/derived.h2
-rw-r--r--sources/shiboken6/tests/libsample/point.cpp2
-rw-r--r--sources/shiboken6/tests/libsample/point.h2
-rw-r--r--sources/shiboken6/tests/libsample/pointf.cpp2
-rw-r--r--sources/shiboken6/tests/libsample/pointf.h2
-rw-r--r--sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp20
-rw-r--r--sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h5
-rw-r--r--sources/shiboken6/tests/otherbinding/typediscovery_test.py17
-rw-r--r--sources/shiboken6/tests/smartbinding/CMakeLists.txt1
-rw-r--r--sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py7
-rw-r--r--sources/shiboken6/tests/smartbinding/typesystem_smart.xml2
-rw-r--r--tools/cross_compile_android/main.py34
-rw-r--r--tools/cross_compile_android/templates/cross_compile.tmpl.sh10
-rw-r--r--tools/snippets_translate/module_classes.py1
134 files changed, 1618 insertions, 793 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/build_scripts/wheel_files.py b/build_scripts/wheel_files.py
index d34ada113..2112bba9a 100644
--- a/build_scripts/wheel_files.py
+++ b/build_scripts/wheel_files.py
@@ -997,6 +997,8 @@ def module_QtSerialBus() -> ModuleData:
def module_QtVirtualKeyboard() -> ModuleData:
data = ModuleData("VirtualKeyboard")
data.plugins.append("virtualkeyboard")
+ data.qtlib.append("libQt6VirtualKeyboardSettings")
+
return data
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/graphs/3d/widgetgallery/doc/widgetgallery.rst b/examples/graphs/3d/widgetgallery/doc/widgetgallery.rst
deleted file mode 100644
index 1470001d6..000000000
--- a/examples/graphs/3d/widgetgallery/doc/widgetgallery.rst
+++ /dev/null
@@ -1,11 +0,0 @@
-Widget Gallery
-==============
-
-
-Widget Gallery demonstrates all three graph types and some of their special
-features. The graphs have their own tabs in the application.
-
-
-.. image:: widgetgallery.webp
- :width: 400
- :alt: Widget Screenshot
diff --git a/examples/graphs/3d/widgetgallery/axesinputhandler.py b/examples/graphs/3d/widgetgraphgallery/axesinputhandler.py
index 4c4202974..4c4202974 100644
--- a/examples/graphs/3d/widgetgallery/axesinputhandler.py
+++ b/examples/graphs/3d/widgetgraphgallery/axesinputhandler.py
diff --git a/examples/graphs/3d/widgetgallery/bargraph.py b/examples/graphs/3d/widgetgraphgallery/bargraph.py
index 822acb4a9..822acb4a9 100644
--- a/examples/graphs/3d/widgetgallery/bargraph.py
+++ b/examples/graphs/3d/widgetgraphgallery/bargraph.py
diff --git a/examples/graphs/3d/widgetgallery/custominputhandler.py b/examples/graphs/3d/widgetgraphgallery/custominputhandler.py
index 15fe00e70..15fe00e70 100644
--- a/examples/graphs/3d/widgetgallery/custominputhandler.py
+++ b/examples/graphs/3d/widgetgraphgallery/custominputhandler.py
diff --git a/examples/graphs/3d/widgetgallery/data/layer_1.png b/examples/graphs/3d/widgetgraphgallery/data/layer_1.png
index 9138c710a..9138c710a 100644
--- a/examples/graphs/3d/widgetgallery/data/layer_1.png
+++ b/examples/graphs/3d/widgetgraphgallery/data/layer_1.png
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/data/layer_2.png b/examples/graphs/3d/widgetgraphgallery/data/layer_2.png
index 61631ae8b..61631ae8b 100644
--- a/examples/graphs/3d/widgetgallery/data/layer_2.png
+++ b/examples/graphs/3d/widgetgraphgallery/data/layer_2.png
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/data/layer_3.png b/examples/graphs/3d/widgetgraphgallery/data/layer_3.png
index 066ffbe75..066ffbe75 100644
--- a/examples/graphs/3d/widgetgallery/data/layer_3.png
+++ b/examples/graphs/3d/widgetgraphgallery/data/layer_3.png
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/data/license.txt b/examples/graphs/3d/widgetgraphgallery/data/license.txt
index 749daf31f..749daf31f 100644
--- a/examples/graphs/3d/widgetgallery/data/license.txt
+++ b/examples/graphs/3d/widgetgraphgallery/data/license.txt
diff --git a/examples/graphs/3d/widgetgallery/data/maptexture.jpg b/examples/graphs/3d/widgetgraphgallery/data/maptexture.jpg
index ae5d66ebe..ae5d66ebe 100644
--- a/examples/graphs/3d/widgetgallery/data/maptexture.jpg
+++ b/examples/graphs/3d/widgetgraphgallery/data/maptexture.jpg
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/data/narrowarrow.mesh b/examples/graphs/3d/widgetgraphgallery/data/narrowarrow.mesh
index 288867b1e..288867b1e 100644
--- a/examples/graphs/3d/widgetgallery/data/narrowarrow.mesh
+++ b/examples/graphs/3d/widgetgraphgallery/data/narrowarrow.mesh
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/data/oilrig.mesh b/examples/graphs/3d/widgetgraphgallery/data/oilrig.mesh
index 4a7baeddf..4a7baeddf 100644
--- a/examples/graphs/3d/widgetgallery/data/oilrig.mesh
+++ b/examples/graphs/3d/widgetgraphgallery/data/oilrig.mesh
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/data/pipe.mesh b/examples/graphs/3d/widgetgraphgallery/data/pipe.mesh
index 984b6d443..984b6d443 100644
--- a/examples/graphs/3d/widgetgallery/data/pipe.mesh
+++ b/examples/graphs/3d/widgetgraphgallery/data/pipe.mesh
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/data/raindata.txt b/examples/graphs/3d/widgetgraphgallery/data/raindata.txt
index d95589219..d95589219 100644
--- a/examples/graphs/3d/widgetgallery/data/raindata.txt
+++ b/examples/graphs/3d/widgetgraphgallery/data/raindata.txt
diff --git a/examples/graphs/3d/widgetgallery/data/refinery.mesh b/examples/graphs/3d/widgetgraphgallery/data/refinery.mesh
index a7e249353..a7e249353 100644
--- a/examples/graphs/3d/widgetgallery/data/refinery.mesh
+++ b/examples/graphs/3d/widgetgraphgallery/data/refinery.mesh
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/data/topography.png b/examples/graphs/3d/widgetgraphgallery/data/topography.png
index 9349cdb31..9349cdb31 100644
--- a/examples/graphs/3d/widgetgallery/data/topography.png
+++ b/examples/graphs/3d/widgetgraphgallery/data/topography.png
Binary files differ
diff --git a/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.rst b/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.rst
new file mode 100644
index 000000000..33a4da15b
--- /dev/null
+++ b/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.rst
@@ -0,0 +1,11 @@
+Widget Graph Gallery
+====================
+
+
+Widget Graph Gallery demonstrates all three graph types and some of their
+special features. The graphs have their own tabs in the application.
+
+
+.. image:: widgetgraphgallery.webp
+ :width: 400
+ :alt: Widget Screenshot
diff --git a/examples/graphs/3d/widgetgallery/doc/widgetgallery.webp b/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.webp
index eb5767264..eb5767264 100644
--- a/examples/graphs/3d/widgetgallery/doc/widgetgallery.webp
+++ b/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.webp
Binary files differ
diff --git a/examples/graphs/3d/widgetgallery/graphmodifier.py b/examples/graphs/3d/widgetgraphgallery/graphmodifier.py
index 2eaafa792..2eaafa792 100644
--- a/examples/graphs/3d/widgetgallery/graphmodifier.py
+++ b/examples/graphs/3d/widgetgraphgallery/graphmodifier.py
diff --git a/examples/graphs/3d/widgetgallery/highlightseries.py b/examples/graphs/3d/widgetgraphgallery/highlightseries.py
index 8c7b91633..8c7b91633 100644
--- a/examples/graphs/3d/widgetgallery/highlightseries.py
+++ b/examples/graphs/3d/widgetgraphgallery/highlightseries.py
diff --git a/examples/graphs/3d/widgetgallery/main.py b/examples/graphs/3d/widgetgraphgallery/main.py
index 7bb2238a7..7bb2238a7 100644
--- a/examples/graphs/3d/widgetgallery/main.py
+++ b/examples/graphs/3d/widgetgraphgallery/main.py
diff --git a/examples/graphs/3d/widgetgallery/rainfalldata.py b/examples/graphs/3d/widgetgraphgallery/rainfalldata.py
index d74f45a8b..d74f45a8b 100644
--- a/examples/graphs/3d/widgetgallery/rainfalldata.py
+++ b/examples/graphs/3d/widgetgraphgallery/rainfalldata.py
diff --git a/examples/graphs/3d/widgetgallery/scatterdatamodifier.py b/examples/graphs/3d/widgetgraphgallery/scatterdatamodifier.py
index 15064b412..15064b412 100644
--- a/examples/graphs/3d/widgetgallery/scatterdatamodifier.py
+++ b/examples/graphs/3d/widgetgraphgallery/scatterdatamodifier.py
diff --git a/examples/graphs/3d/widgetgallery/scattergraph.py b/examples/graphs/3d/widgetgraphgallery/scattergraph.py
index 79e8933eb..79e8933eb 100644
--- a/examples/graphs/3d/widgetgallery/scattergraph.py
+++ b/examples/graphs/3d/widgetgraphgallery/scattergraph.py
diff --git a/examples/graphs/3d/widgetgallery/surfacegraph.py b/examples/graphs/3d/widgetgraphgallery/surfacegraph.py
index 4052da821..4052da821 100644
--- a/examples/graphs/3d/widgetgallery/surfacegraph.py
+++ b/examples/graphs/3d/widgetgraphgallery/surfacegraph.py
diff --git a/examples/graphs/3d/widgetgallery/surfacegraphmodifier.py b/examples/graphs/3d/widgetgraphgallery/surfacegraphmodifier.py
index b2706c6fa..b2706c6fa 100644
--- a/examples/graphs/3d/widgetgallery/surfacegraphmodifier.py
+++ b/examples/graphs/3d/widgetgraphgallery/surfacegraphmodifier.py
diff --git a/examples/graphs/3d/widgetgallery/topographicseries.py b/examples/graphs/3d/widgetgraphgallery/topographicseries.py
index 4f286a222..4f286a222 100644
--- a/examples/graphs/3d/widgetgallery/topographicseries.py
+++ b/examples/graphs/3d/widgetgraphgallery/topographicseries.py
diff --git a/examples/graphs/3d/widgetgallery/variantbardatamapping.py b/examples/graphs/3d/widgetgraphgallery/variantbardatamapping.py
index 50bdefa6a..50bdefa6a 100644
--- a/examples/graphs/3d/widgetgallery/variantbardatamapping.py
+++ b/examples/graphs/3d/widgetgraphgallery/variantbardatamapping.py
diff --git a/examples/graphs/3d/widgetgallery/variantbardataproxy.py b/examples/graphs/3d/widgetgraphgallery/variantbardataproxy.py
index 5ab2a2cd2..5ab2a2cd2 100644
--- a/examples/graphs/3d/widgetgallery/variantbardataproxy.py
+++ b/examples/graphs/3d/widgetgraphgallery/variantbardataproxy.py
diff --git a/examples/graphs/3d/widgetgallery/variantdataset.py b/examples/graphs/3d/widgetgraphgallery/variantdataset.py
index 752bc3887..752bc3887 100644
--- a/examples/graphs/3d/widgetgallery/variantdataset.py
+++ b/examples/graphs/3d/widgetgraphgallery/variantdataset.py
diff --git a/examples/graphs/3d/widgetgallery/widgetgallery.pyproject b/examples/graphs/3d/widgetgraphgallery/widgetgraphgallery.pyproject
index 581b21483..581b21483 100644
--- a/examples/graphs/3d/widgetgallery/widgetgallery.pyproject
+++ b/examples/graphs/3d/widgetgraphgallery/widgetgraphgallery.pyproject
diff --git a/sources/pyside-tools/deploy_lib/android/buildozer.py b/sources/pyside-tools/deploy_lib/android/buildozer.py
index 828982b5b..0c314c356 100644
--- a/sources/pyside-tools/deploy_lib/android/buildozer.py
+++ b/sources/pyside-tools/deploy_lib/android/buildozer.py
@@ -39,6 +39,13 @@ class BuildozerConfig(BaseConfig):
self.set_value("app", "p4a.bootstrap", "qt")
self.set_value('app', "p4a.local_recipes", str(pysidedeploy_config.recipe_dir))
+ # add p4a branch
+ # by default the master branch is used
+ # https://github.com/kivy/python-for-android/commit/b92522fab879dbfc0028966ca3c59ef46ab7767d
+ # has not been merged to master yet. So, we use the develop branch for now
+ # TODO: remove this once the above commit is merged to master
+ self.set_value("app", "p4a.branch", "develop")
+
# add permissions
permissions = self.__find_permissions(pysidedeploy_config.dependency_files)
permissions = ", ".join(permissions)
diff --git a/sources/pyside-tools/deploy_lib/nuitka_helper.py b/sources/pyside-tools/deploy_lib/nuitka_helper.py
index d202db25e..ac9a83f3f 100644
--- a/sources/pyside-tools/deploy_lib/nuitka_helper.py
+++ b/sources/pyside-tools/deploy_lib/nuitka_helper.py
@@ -35,6 +35,12 @@ class Nuitka:
"generic" # plugins that error with Nuitka
]
+ # .webp are considered to be dlls by Nuitka instead of data files causing
+ # the packaging to fail
+ # https://github.com/Nuitka/Nuitka/issues/2854
+ # TODO: Remove .webp when the issue is fixed
+ self.files_to_ignore = [".cpp.o", ".qsb", ".webp"]
+
@staticmethod
def icon_option():
if sys.platform == "linux":
@@ -81,6 +87,14 @@ class Nuitka:
dll_name = plugin.replace("Qt", f"Qt{MAJOR_VERSION}")
qml_args.append(f"--noinclude-dlls={prefix}{dll_name}*")
+ # Exclude .qen json files from QtQuickEffectMaker
+ # These files are not relevant for PySide6 applications
+ qml_args.append("--noinclude-dlls=*/qml/QtQuickEffectMaker/*")
+
+ # Exclude files that cannot be processed by Nuitka
+ for file in self.files_to_ignore:
+ extra_args.append(f"--noinclude-dlls=*{file}")
+
output_dir = source_file.parent / "deployment"
if not dry_run:
output_dir.mkdir(parents=True, exist_ok=True)
diff --git a/sources/pyside6/PySide6/QtAsyncio/__init__.py b/sources/pyside6/PySide6/QtAsyncio/__init__.py
index 4baa8134e..60d1846d1 100644
--- a/sources/pyside6/PySide6/QtAsyncio/__init__.py
+++ b/sources/pyside6/PySide6/QtAsyncio/__init__.py
@@ -28,8 +28,8 @@ def run(coro: typing.Optional[typing.Coroutine] = None,
# subsequent removal in Python 3.15. At that point, part of the current
# logic of the QAsyncioEventLoopPolicy constructor will have to be moved
# here and/or to a loop factory class (to be provided as an argument to
- # asyncio.run()), namely setting up the QCoreApplication and the SIGINT
- # handler.
+ # asyncio.run()). In particular, this concerns the logic of setting up the
+ # QCoreApplication and the SIGINT handler.
#
# More details:
# https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553 # noqa: E501
diff --git a/sources/pyside6/PySide6/QtAsyncio/events.py b/sources/pyside6/PySide6/QtAsyncio/events.py
index a29e480b7..86b89014a 100644
--- a/sources/pyside6/PySide6/QtAsyncio/events.py
+++ b/sources/pyside6/PySide6/QtAsyncio/events.py
@@ -26,6 +26,19 @@ __all__ = [
class QAsyncioExecutorWrapper(QObject):
+ """
+ Executors in asyncio allow running synchronous code in a separate thread or
+ process without blocking the event loop or interrupting the asynchronous
+ program flow. Callables are scheduled for execution by calling submit() or
+ map() on an executor object.
+
+ Executors require a bit of extra work for QtAsyncio, as we can't use
+ naked Python threads; instead, we must make sure that the thread created
+ by executor.submit() has an event loop. This is achieved by not submitting
+ the callable directly, but a small wrapper that attaches a QEventLoop to
+ the executor thread, and then creates a zero-delay singleshot timer to push
+ the actual callable for the executor into this new event loop.
+ """
def __init__(self, func: typing.Callable, *args: typing.Tuple) -> None:
super().__init__()
@@ -37,16 +50,21 @@ class QAsyncioExecutorWrapper(QObject):
def _cb(self):
try:
+ # Call the synchronous callable that we submitted with submit() or
+ # map().
self._result = self._func(*self._args)
except BaseException as e:
self._exception = e
self._loop.exit()
def do(self):
- # This creates a new event loop and dispatcher for the thread, if not already created.
+ # This creates a new event loop and dispatcher for the thread, if not
+ # already created.
self._loop = QEventLoop()
asyncio.events._set_running_loop(self._loop)
+
QTimer.singleShot(0, self._loop, lambda: self._cb())
+
self._loop.exec()
if self._exception is not None:
raise self._exception
@@ -57,6 +75,17 @@ class QAsyncioExecutorWrapper(QObject):
class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
+ """
+ Event loop policies are expected to be deprecated with Python 3.13, with
+ subsequent removal in Python 3.15. At that point, part of the current
+ logic of the QAsyncioEventLoopPolicy constructor will have to be moved
+ to QtAsyncio.run() and/or to a loop factory class (to be provided as an
+ argument to asyncio.run()). In particular, this concerns the logic of
+ setting up the QCoreApplication and the SIGINT handler.
+
+ More details:
+ https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553
+ """
def __init__(self,
application: typing.Optional[QCoreApplication] = None,
quit_qapp: bool = True,
@@ -68,7 +97,14 @@ class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
else:
application = QCoreApplication.instance()
self._application: QCoreApplication = application # type: ignore[assignment]
+
+ # Configure whether the QCoreApplication at the core of QtAsyncio
+ # should be shut down when asyncio finishes. A special case where one
+ # would want to disable this is test suites that want to reuse a single
+ # QCoreApplication instance across all unit tests, which would fail if
+ # this instance is shut down every time.
self._quit_qapp = quit_qapp
+
self._event_loop: typing.Optional[asyncio.AbstractEventLoop] = None
if handle_sigint:
@@ -99,6 +135,14 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
"""
class ShutDownThread(QThread):
+ """
+ Used to shut down the default executor when calling
+ shutdown_default_executor(). As the executor is a ThreadPoolExecutor,
+ it must be shut down in a separate thread as all the threads from the
+ thread pool must join, which we want to do without blocking the event
+ loop.
+ """
+
def __init__(self, future: futures.QAsyncioFuture, loop: "QAsyncioEventLoop") -> None:
super().__init__()
self._future = future
@@ -123,22 +167,48 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
QObject.__init__(self)
self._application: QCoreApplication = application
+
+ # Configure whether the QCoreApplication at the core of QtAsyncio
+ # should be shut down when asyncio finishes. A special case where one
+ # would want to disable this is test suites that want to reuse a single
+ # QCoreApplication instance across all unit tests, which would fail if
+ # this instance is shut down every time.
self._quit_qapp = quit_qapp
+
self._thread = QThread.currentThread()
self._closed = False
+ # These two flags are used to determine whether the loop was stopped
+ # from inside the loop (i.e., coroutine or callback called stop()) or
+ # from outside the loop (i.e., the QApplication is being shut down, for
+ # example, by the user closing the window or by calling
+ # QApplication.quit()). The different cases can trigger slightly
+ # different behaviors (see the comments where the flags are used).
+ # There are two variables for this as in a third case the loop is still
+ # running and both flags are False.
self._quit_from_inside = False
self._quit_from_outside = False
+ # A set of all asynchronous generators that are currently running.
self._asyncgens: typing.Set[collections.abc.AsyncGenerator] = set()
# Starting with Python 3.11, this must be an instance of
# ThreadPoolExecutor.
self._default_executor = concurrent.futures.ThreadPoolExecutor()
+ # The exception handler, if set with set_exception_handler(). The
+ # exception handler is currently called in two places: One, if an
+ # asynchonrous generator raises an exception when closed, and two, if
+ # an exception is raised during the execution of a task. Currently, the
+ # default exception handler just prints the exception to the console.
self._exception_handler: typing.Optional[typing.Callable] = self.default_exception_handler
+
+ # The task factory, if set with set_task_factory(). Otherwise, a new
+ # task is created with the QAsyncioTask constructor.
self._task_factory: typing.Optional[typing.Callable] = None
+
+ # The future that is currently being awaited with run_until_complete().
self._future_to_complete: typing.Optional[futures.QAsyncioFuture] = None
self._debug = bool(os.getenv("PYTHONASYNCIODEBUG", False))
@@ -148,6 +218,10 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
# Running and stopping the loop
def _run_until_complete_cb(self, future: futures.QAsyncioFuture) -> None:
+ """
+ A callback that stops the loop when the future is done, used when
+ running the loop with run_until_complete().
+ """
if not future.cancelled():
if isinstance(future.exception(), (SystemExit, KeyboardInterrupt)):
return
@@ -188,7 +262,12 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
asyncio.events._set_running_loop(None)
def _about_to_quit_cb(self):
+ """ A callback for the aboutToQuit signal of the QCoreApplication. """
if not self._quit_from_inside:
+ # If the aboutToQuit signal is emitted, the user is closing the
+ # application window or calling QApplication.quit(). In this case,
+ # we want to close the event loop, and we consider this a quit from
+ # outside the loop.
self._quit_from_outside = True
self.close()
@@ -197,8 +276,15 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
if self._future_to_complete.done():
self._future_to_complete = None
else:
+ # Do not stop the loop if there is a future still being awaited
+ # with run_until_complete().
return
+
self._quit_from_inside = True
+
+ # The user might want to keep the QApplication running after the event
+ # event loop finishes, which they can control with the quit_qapp
+ # argument.
if self._quit_qapp:
self._application.quit()
@@ -298,6 +384,7 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
callback: typing.Callable, *args: typing.Any,
context: typing.Optional[contextvars.Context] = None,
is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
+ """ All call_at() and call_later() methods map to this method. """
if not isinstance(when, (int, float)):
raise TypeError("when must be an int or float")
return QAsyncioTimerHandle(when, callback, args, self, context, is_threadsafe=is_threadsafe)
@@ -482,6 +569,13 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
raise RuntimeError("Event loop is closed")
if executor is None:
executor = self._default_executor
+
+ # Executors require a bit of extra work for QtAsyncio, as we can't use
+ # naked Python threads; instead, we must make sure that the thread
+ # created by executor.submit() has an event loop. This is achieved by
+ # not submitting the callable directly, but a small wrapper that
+ # attaches a QEventLoop to the executor thread, and then pushes the
+ # actual callable for the executor into this new event loop.
wrapper = QAsyncioExecutorWrapper(func, *args)
return asyncio.futures.wrap_future(
executor.submit(wrapper.do), loop=self
@@ -541,6 +635,12 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
class QAsyncioHandle():
+ """
+ The handle enqueues a callback to be executed by the event loop, and allows
+ for this callback to be cancelled before it is executed. This callback will
+ typically execute the step function for a task. This makes the handle one
+ of the main components of asyncio.
+ """
class HandleState(enum.Enum):
PENDING = enum.auto()
CANCELLED = enum.auto()
@@ -560,18 +660,30 @@ class QAsyncioHandle():
self._state = QAsyncioHandle.HandleState.PENDING
self._start()
+ def _start(self) -> None:
+ self._schedule_event(self._timeout, lambda: self._cb())
+
def _schedule_event(self, timeout: int, func: typing.Callable) -> None:
+ # Do not schedule events from asyncio when the app is quit from outside
+ # the event loop, as this would cause events to be enqueued after the
+ # event loop was destroyed.
if not self._loop.is_closed() and not self._loop._quit_from_outside:
if self._is_threadsafe:
+ # This singleShot overload will push func into self._loop
+ # instead of the current thread's loop. This allows scheduling
+ # a callback from a different thread, which is necessary for
+ # thread-safety.
+ # https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading
QTimer.singleShot(timeout, self._loop, func)
else:
QTimer.singleShot(timeout, func)
- def _start(self) -> None:
- self._schedule_event(self._timeout, lambda: self._cb())
-
@Slot()
def _cb(self) -> None:
+ """
+ A slot, enqueued into the event loop, that wraps around the actual
+ callback, typically the step function of a task.
+ """
if self._state == QAsyncioHandle.HandleState.PENDING:
if self._context is not None:
self._context.run(self._callback, *self._args)
@@ -581,7 +693,9 @@ class QAsyncioHandle():
def cancel(self) -> None:
if self._state == QAsyncioHandle.HandleState.PENDING:
- # The old timer that was created in _start will still trigger but _cb won't do anything.
+ # The old timer that was created in _start will still trigger but
+ # _cb won't do anything, therefore the callback is effectively
+ # cancelled.
self._state = QAsyncioHandle.HandleState.CANCELLED
def cancelled(self) -> bool:
@@ -600,9 +714,11 @@ class QAsyncioTimerHandle(QAsyncioHandle, asyncio.TimerHandle):
QAsyncioHandle._start(self)
- # Override this so that timer.start() is only called once at the end
- # of the constructor for both QtHandle and QtTimerHandle.
def _start(self) -> None:
+ """
+ Overridden so that timer.start() is only called once at the end of the
+ constructor for both QtHandle and QtTimerHandle.
+ """
pass
def when(self) -> float:
diff --git a/sources/pyside6/PySide6/QtAsyncio/futures.py b/sources/pyside6/PySide6/QtAsyncio/futures.py
index 611bd5634..cbb005fc9 100644
--- a/sources/pyside6/PySide6/QtAsyncio/futures.py
+++ b/sources/pyside6/PySide6/QtAsyncio/futures.py
@@ -36,10 +36,11 @@ class QAsyncioFuture():
self._result: typing.Any = None
self._exception: typing.Optional[BaseException] = None
- self._callbacks: typing.List[typing.Callable] = list()
-
self._cancel_message: typing.Optional[str] = None
+ # List of callbacks that are called when the future is done.
+ self._callbacks: typing.List[typing.Callable] = list()
+
def __await__(self):
if not self.done():
self._asyncio_future_blocking = True
@@ -51,6 +52,7 @@ class QAsyncioFuture():
__iter__ = __await__
def _schedule_callbacks(self, context: typing.Optional[contextvars.Context] = None):
+ """ A future can optionally have callbacks that are called when the future is done. """
for cb in self._callbacks:
self._loop.call_soon(
cb, self, context=context if context else self._context)
diff --git a/sources/pyside6/PySide6/QtAsyncio/tasks.py b/sources/pyside6/PySide6/QtAsyncio/tasks.py
index c04006686..7edc15093 100644
--- a/sources/pyside6/PySide6/QtAsyncio/tasks.py
+++ b/sources/pyside6/PySide6/QtAsyncio/tasks.py
@@ -20,17 +20,22 @@ class QAsyncioTask(futures.QAsyncioFuture):
context: typing.Optional[contextvars.Context] = None) -> None:
super().__init__(loop=loop, context=context)
- self._coro = coro
+ self._coro = coro # The coroutine for which this task was created.
self._name = name if name else "QtTask"
+ # The task creates a handle for its coroutine. The handle enqueues the
+ # task's step function as its callback in the event loop.
self._handle = self._loop.call_soon(self._step, context=self._context)
- self._cancellation_requests = 0
-
+ # The task step function executes the coroutine until it finishes,
+ # raises an exception or returns a future. If a future was returned,
+ # the task will await its completion (or exception).
self._future_to_await: typing.Optional[asyncio.Future] = None
- self._cancel_message: typing.Optional[str] = None
+
self._cancelled = False
+ self._cancel_message: typing.Optional[str] = None
+ # https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
asyncio._register_task(self) # type: ignore[arg-type]
def __repr__(self) -> str:
@@ -59,6 +64,14 @@ class QAsyncioTask(futures.QAsyncioFuture):
def _step(self,
exception_or_future: typing.Union[
BaseException, futures.QAsyncioFuture, None] = None) -> None:
+ """
+ The step function is the heart of a task. It is scheduled in the event
+ loop repeatedly, executing the coroutine "step" by "step" (i.e.,
+ iterating through the asynchronous generator) until it finishes with an
+ exception or successfully. Each step can optionally receive an
+ exception or a future as a result from a previous step to handle.
+ """
+
if self.done():
return
result = None
@@ -72,7 +85,15 @@ class QAsyncioTask(futures.QAsyncioFuture):
try:
asyncio._enter_task(self._loop, self) # type: ignore[arg-type]
+
+ # It is at this point that the coroutine is resumed for the current
+ # step (i.e. asynchronous generator iteration). It will now be
+ # executed until it yields (and potentially returns a future),
+ # raises an exception, is cancelled, or finishes successfully.
+
if isinstance(exception_or_future, BaseException):
+ # If the coroutine doesn't handle this exception, it propagates
+ # to the caller.
result = self._coro.throw(exception_or_future)
else:
result = self._coro.send(None)
@@ -87,6 +108,9 @@ class QAsyncioTask(futures.QAsyncioFuture):
self._exception = e
else:
if asyncio.futures.isfuture(result):
+ # If the coroutine yields a future, the task will await its
+ # completion, and at that point the step function will be
+ # called again.
result.add_done_callback(
self._step, context=self._context) # type: ignore[arg-type]
self._future_to_await = result
@@ -100,12 +124,16 @@ class QAsyncioTask(futures.QAsyncioFuture):
# very quickly.
self._future_to_await.cancel(self._cancel_message)
elif result is None:
+ # If no future was yielded, we schedule the step function again
+ # without any arguments.
self._loop.call_soon(self._step, context=self._context)
else:
+ # This is not supposed to happen.
exception = RuntimeError(f"Bad task result: {result}")
self._loop.call_soon(self._step, exception, context=self._context)
finally:
asyncio._leave_task(self._loop, self) # type: ignore[arg-type]
+
if self._exception:
self._loop.call_exception_handler({
"message": (str(self._exception) if self._exception
@@ -117,8 +145,11 @@ class QAsyncioTask(futures.QAsyncioFuture):
if asyncio.futures.isfuture(exception_or_future)
else None)
})
+
if self.done():
self._schedule_callbacks()
+
+ # https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
asyncio._unregister_task(self) # type: ignore[arg-type]
def get_stack(self, *, limit=None) -> typing.List[typing.Any]:
@@ -144,6 +175,8 @@ class QAsyncioTask(futures.QAsyncioFuture):
self._cancel_message = msg
self._handle.cancel()
if self._future_to_await is not None:
+ # A task that is awaiting a future must also cancel this future in
+ # order for the cancellation to be successful.
self._future_to_await.cancel(msg)
self._cancelled = True
return True
diff --git a/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp b/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp
index 4266e868c..f6acf9d60 100644
--- a/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp
+++ b/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp
@@ -340,9 +340,7 @@ PyObject *invokeMetaMethodWithReturn(const InvokeMetaMethodFuncWithReturn &f,
a6.toGenericArgument(), a7.toGenericArgument(), a8.toGenericArgument(),
a9.toGenericArgument());
PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS
- if (!callResult) {
- PyErr_SetString(PyExc_RuntimeError, "QMetaMethod invocation failed.");
- return nullptr;
- }
+ if (!callResult)
+ return PyErr_Format(PyExc_RuntimeError, "QMetaMethod invocation failed.");
return convertGenericReturnArgument(r.data(), r.metaType());
}
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
index 0a08928f5..110f86f25 100644
--- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
+++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
@@ -1289,6 +1289,7 @@
qRegisterMetaType<QList<QPersistentModelIndex> >("QList_QPersistentModelIndex");
</inject-code>
<modify-function signature="internalPointer()const">
+ <modify-argument index="return" pyi-type="Any"/>
<inject-code class="target" position="beginning">
<insert-template name="return_internal_pointer" />
</inject-code>
@@ -1677,8 +1678,9 @@
<add-function signature="operator==(const QItemSelection&amp;)" return-type="bool"/>
<add-function signature="operator!=(const QItemSelection&amp;)" return-type="bool"/>
<!-- For some reason, the empty selection is not seen. Maybe related to the new [default]
- tag in Qt6? -->
- <declare-function signature="QItemSelection()" return-type="QItemSelection" />
+ tag in Qt6?
+ PYSIDE-2756: The return-type attribute is unnecessary -->
+ <declare-function signature="QItemSelection()"/>
<!-- The __add__ function creates a result list, instead of using the inherited type.
Fixed by adding with the correct type. -->
<add-function signature="operator+(QItemSelection)" return-type="QItemSelection">
@@ -1759,9 +1761,6 @@
</modify-function>
<!-- End of Invalidate-after-use fix -->
<modify-function signature="parent()const">
- <modify-argument index="this">
- <parent index="return" action="add"/>
- </modify-argument>
<modify-argument index="return">
<define-ownership class="target" owner="default"/>
</modify-argument>
@@ -2552,6 +2551,11 @@
</modify-argument>
<inject-code file="../glue/qtcore.cpp" snippet="qtranslator-load"/>
</modify-function>
+ <modify-function signature="translate(const char*,const char*, const char*,int)const">
+ <modify-argument index="1" pyi-type="str"/>
+ <modify-argument index="2" pyi-type="str"/>
+ <modify-argument index="3" pyi-type="Optional[str]"/>
+ </modify-function>
</object-type>
<object-type name="QWaitCondition">
<configuration condition="QT_CONFIG(thread)"/>
@@ -3103,6 +3107,7 @@
</object-type>
<value-type name="QModelIndex">
<modify-function signature="internalPointer()const">
+ <modify-argument index="return" pyi-type="Any"/>
<inject-code class="target" position="beginning">
<insert-template name="return_internal_pointer" />
</inject-code>
diff --git a/sources/pyside6/PySide6/QtGui/CMakeLists.txt b/sources/pyside6/PySide6/QtGui/CMakeLists.txt
index 7cd7871f3..99e0789d1 100644
--- a/sources/pyside6/PySide6/QtGui/CMakeLists.txt
+++ b/sources/pyside6/PySide6/QtGui/CMakeLists.txt
@@ -88,6 +88,7 @@ ${QtGui_GEN_DIR}/qaccessibleactioninterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleeditabletextinterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleevent_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleobject_wrapper.cpp
+${QtGui_GEN_DIR}/qaccessibleselectioninterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessiblestatechangeevent_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibletablecellinterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibletablemodelchangeevent_wrapper.cpp
diff --git a/sources/pyside6/PySide6/QtGui/typesystem_gui_common.xml b/sources/pyside6/PySide6/QtGui/typesystem_gui_common.xml
index dd17952f0..feb2dc11b 100644
--- a/sources/pyside6/PySide6/QtGui/typesystem_gui_common.xml
+++ b/sources/pyside6/PySide6/QtGui/typesystem_gui_common.xml
@@ -122,6 +122,7 @@
<object-type name="QAccessibleEditableTextInterface"/>
<object-type name="QAccessibleInterface"/>
<object-type name="QAccessibleObject" qt-register-metatype="base"/>
+ <object-type name="QAccessibleSelectionInterface" since="6.7"/>
<object-type name="QAccessibleTableCellInterface"/>
<object-type name="QAccessibleTextInterface"/>
<object-type name="QAccessibleValueInterface"/>
@@ -2572,7 +2573,7 @@
</extra-includes>
</primitive-type>
- <object-type name="QWindow" delete-in-main-thread="true">
+ <object-type name="QWindow" delete-in-main-thread="true" polymorphic-base="true">
<enum-type name="AncestorMode"/>
<enum-type name="Visibility"/>
<modify-function signature="raise()" rename="raise_"/>
diff --git a/sources/pyside6/PySide6/QtNetwork/typesystem_network.xml b/sources/pyside6/PySide6/QtNetwork/typesystem_network.xml
index 508043626..4dc7c9b0a 100644
--- a/sources/pyside6/PySide6/QtNetwork/typesystem_network.xml
+++ b/sources/pyside6/PySide6/QtNetwork/typesystem_network.xml
@@ -179,6 +179,11 @@
<modify-function signature="put(const QNetworkRequest &amp;,QIODevice*)" allow-thread="yes"/>
<modify-function signature="put(const QNetworkRequest &amp;,const QByteArray &amp;)" allow-thread="yes"/>
<modify-function signature="sendCustomRequest(const QNetworkRequest &amp;,const QByteArray &amp;,QIODevice*)" allow-thread="yes" since="4.7"/>
+ <modify-function signature="setCache(QAbstractNetworkCache*)">
+ <modify-argument index="1">
+ <define-ownership class="target" owner="c++"/>
+ </modify-argument>
+ </modify-function>
<modify-function signature="setCookieJar(QNetworkCookieJar*)">
<modify-argument index="1">
<define-ownership class="target" owner="c++"/>
diff --git a/sources/pyside6/PySide6/QtQml/pysideqmlvolatilebool.cpp b/sources/pyside6/PySide6/QtQml/pysideqmlvolatilebool.cpp
index 6e403ab72..ca3dfebed 100644
--- a/sources/pyside6/PySide6/QtQml/pysideqmlvolatilebool.cpp
+++ b/sources/pyside6/PySide6/QtQml/pysideqmlvolatilebool.cpp
@@ -49,7 +49,10 @@ static void QtQml_VolatileBoolObject_dealloc(PyObject *self)
static PyObject *
QtQml_VolatileBoolObject_get(QtQml_VolatileBoolObject *self)
{
- return *self->flag ? Py_True : Py_False;
+ if (*self->flag) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
}
static PyObject *
@@ -63,10 +66,8 @@ QtQml_VolatileBoolObject_set(QtQml_VolatileBoolObject *self, PyObject *args)
}
ok = PyObject_IsTrue(value);
- if (ok < 0) {
- PyErr_SetString(PyExc_TypeError, "Not a boolean value.");
- return nullptr;
- }
+ if (ok < 0)
+ return PyErr_Format(PyExc_TypeError, "Not a boolean value.");
*self->flag = ok > 0;
diff --git a/sources/pyside6/PySide6/QtQml/typesystem_qml.xml b/sources/pyside6/PySide6/QtQml/typesystem_qml.xml
index 6a739bbdf..3392ef379 100644
--- a/sources/pyside6/PySide6/QtQml/typesystem_qml.xml
+++ b/sources/pyside6/PySide6/QtQml/typesystem_qml.xml
@@ -124,9 +124,12 @@
<function signature="qmlProtectModule(const char*,int)" doc-file="qqmlengine"/>
<function signature="qmlRegisterModule(const char*,int,int)" doc-file="qqmlengine"/>
<function signature="qmlTypeId(const char*,int,int,const char*)" doc-file="qqmlengine"/>
- <function signature="qmlRegisterType(const QUrl &amp;,const char *,int,int,const char *)"/>
- <function signature="qmlRegisterSingletonType(const QUrl &amp;,const char *,int,int,const char *)"/>
- <function signature="qmlRegisterUncreatableMetaObject(const QMetaObject&amp;,const char*,int,int, const char*,const QString&amp;)"/>
+ <function signature="qmlRegisterType(const QUrl &amp;,const char *,int,int,const char *)"
+ doc-file="qqmlengine"/>
+ <function signature="qmlRegisterSingletonType(const QUrl &amp;,const char *,int,int,const char *)"
+ doc-file="qqmlengine"/>
+ <function signature="qmlRegisterUncreatableMetaObject(const QMetaObject&amp;,const char*,int,int, const char*,const QString&amp;)"
+ doc-file="qqmlengine"/>
<enum-type identified-by-value="QML_HAS_ATTACHED_PROPERTIES">
<extra-includes>
diff --git a/sources/pyside6/PySide6/QtQuick/typesystem_quick.xml b/sources/pyside6/PySide6/QtQuick/typesystem_quick.xml
index 0a24123f4..61aed6005 100644
--- a/sources/pyside6/PySide6/QtQuick/typesystem_quick.xml
+++ b/sources/pyside6/PySide6/QtQuick/typesystem_quick.xml
@@ -53,7 +53,7 @@
<object-type name="QQuickImageResponse"/>
<object-type name="QQuickTransform"/>
- <object-type name="QQuickItem" delete-in-main-thread="true">
+ <object-type name="QQuickItem" delete-in-main-thread="true" polymorphic-base="true">
<value-type name="UpdatePaintNodeData"/>
<enum-type name="Flag" flags="Flags"/>
<enum-type name="ItemChange"/>
diff --git a/sources/pyside6/PySide6/QtWebEngineQuick/CMakeLists.txt b/sources/pyside6/PySide6/QtWebEngineQuick/CMakeLists.txt
index 5aedc1147..d87dc55a4 100644
--- a/sources/pyside6/PySide6/QtWebEngineQuick/CMakeLists.txt
+++ b/sources/pyside6/PySide6/QtWebEngineQuick/CMakeLists.txt
@@ -14,17 +14,26 @@ set(QtWebEngineQuick_include_dirs
${QtWebEngineQuick_SOURCE_DIR}
${QtWebEngineQuick_BINARY_DIR}
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Network_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}PrintSupport_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}WebChannel_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
+ ${QtGui_GEN_DIR}
+ ${QtWidgets_GEN_DIR}
${QtNetwork_GEN_DIR}
- ${QtQml_GEN_DIR})
+ ${QtWebEngineCore_GEN_DIR}
+ ${QtPrintSupport_GEN_DIR}
+ ${QtQml_GEN_DIR}
+ ${QtWebChannel_GEN_DIR})
set(QtWebEngineQuick_libraries pyside6
${Qt${QT_MAJOR_VERSION}WebEngineQuick_LIBRARIES})
-set(QtWebEngineQuick_deps QtQml QtNetwork QtCore)
+set(QtWebEngineQuick_deps QtQml QtWebEngineCore)
create_pyside_module(NAME QtWebEngineQuick
INCLUDE_DIRS QtWebEngineQuick_include_dirs
diff --git a/sources/pyside6/PySide6/QtWebEngineQuick/typesystem_webenginequick.xml b/sources/pyside6/PySide6/QtWebEngineQuick/typesystem_webenginequick.xml
index 108ba5fae..2e7d22334 100644
--- a/sources/pyside6/PySide6/QtWebEngineQuick/typesystem_webenginequick.xml
+++ b/sources/pyside6/PySide6/QtWebEngineQuick/typesystem_webenginequick.xml
@@ -6,6 +6,7 @@
<typesystem package="PySide6.QtWebEngineQuick"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtQml/typesystem_qml.xml" generate="no"/>
+ <load-typesystem name="QtWebEngineCore/typesystem_webenginecore.xml" generate="no"/>
<namespace-type name="QtWebEngineQuick"/> <!-- initialize() -->
diff --git a/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml b/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml
index 0669c18f6..b1188a514 100644
--- a/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml
+++ b/sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml
@@ -1205,7 +1205,7 @@
<modify-function signature="getColor(const QColor&amp;,QWidget*,const QString&amp;,QFlags&lt;QColorDialog::ColorDialogOption>)" allow-thread="yes"/>
</object-type>
- <object-type name="QLayout">
+ <object-type name="QLayout" polymorphic-base="true">
<inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp"
snippet="qwidget-retrieveobjectname"/>
<inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp" snippet="qlayout-help-functions"/>
@@ -1980,7 +1980,7 @@
<modify-function signature="removeItemWidget(QListWidgetItem*)" allow-thread="yes"/>
</object-type>
- <object-type name="QWidget" delete-in-main-thread="true">
+ <object-type name="QWidget" delete-in-main-thread="true" polymorphic-base="true">
<!-- see QWindow::nativeEvent(), QAbstractNativeEventFilter::nativeEventFilter() -->
<inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp" snippet="qwidget-addaction-glue"/>
<inject-code class="native" position="beginning">
@@ -2032,9 +2032,6 @@
</modify-function>
<modify-function signature="parentWidget()const">
- <modify-argument index="this">
- <parent index="return" action="add"/>
- </modify-argument>
<modify-argument index="return">
<define-ownership class="target" owner="default"/>
</modify-argument>
diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp
index bc51d26d7..0c206db72 100644
--- a/sources/pyside6/PySide6/glue/qtcore.cpp
+++ b/sources/pyside6/PySide6/glue/qtcore.cpp
@@ -84,11 +84,9 @@ static PyObject *convertToPrimitiveType(const QVariant &out, int metaTypeId)
return PyFloat_FromDouble(out.toFloat());
case QMetaType::Bool:
if (out.toBool()) {
- Py_INCREF(Py_True);
- return Py_True;
+ Py_RETURN_TRUE;
}
- Py_INCREF(Py_False);
- return Py_False;
+ Py_RETURN_FALSE;
default:
break;
}
@@ -632,20 +630,16 @@ if (ret == nullptr) {
// @snippet qbytearray-mgetitem
if (PyIndex_Check(_key)) {
const Py_ssize_t _i = PyNumber_AsSsize_t(_key, PyExc_IndexError);
- if (_i < 0 || _i >= %CPPSELF.size()) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return nullptr;
- }
+ if (_i < 0 || _i >= %CPPSELF.size())
+ return PyErr_Format(PyExc_IndexError, "index out of bounds");
char res[2] = {%CPPSELF.at(_i), '\0'};
return PyBytes_FromStringAndSize(res, 1);
}
-if (PySlice_Check(_key) == 0) {
- PyErr_Format(PyExc_TypeError,
+if (PySlice_Check(_key) == 0)
+ return PyErr_Format(PyExc_TypeError,
"list indices must be integers or slices, not %.200s",
Py_TYPE(_key)->tp_name);
- return nullptr;
-}
Py_ssize_t start, stop, step, slicelength;
if (PySlice_GetIndicesEx(_key, %CPPSELF.size(), &start, &stop, &step, &slicelength) < 0)
@@ -1028,6 +1022,8 @@ void QSingleShotTimerFunctor::operator()()
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(0));
Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
+ if (Shiboken::Errors::occurred())
+ Shiboken::Errors::storeErrorOrPrint();
release(); // single shot
}
// @snippet qtimer-singleshot-functorclass
diff --git a/sources/pyside6/PySide6/glue/qtgui.cpp b/sources/pyside6/PySide6/glue/qtgui.cpp
index 130de11bb..5c860a2bf 100644
--- a/sources/pyside6/PySide6/glue/qtgui.cpp
+++ b/sources/pyside6/PySide6/glue/qtgui.cpp
@@ -912,10 +912,8 @@ return %CPPSELF.rectCount();
// @snippet qregion-len
// @snippet qregion-getitem
-if (_i < 0 || _i >= %CPPSELF.rectCount()) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return nullptr;
-}
+if (_i < 0 || _i >= %CPPSELF.rectCount())
+ return PyErr_Format(PyExc_IndexError, "index out of bounds");
const QRect cppResult = *(%CPPSELF.cbegin() + _i);
return %CONVERTTOPYTHON[QRect](cppResult);
diff --git a/sources/pyside6/PySide6/glue/qtuitools.cpp b/sources/pyside6/PySide6/glue/qtuitools.cpp
index 0f4405944..1835ed096 100644
--- a/sources/pyside6/PySide6/glue/qtuitools.cpp
+++ b/sources/pyside6/PySide6/glue/qtuitools.cpp
@@ -45,7 +45,7 @@ static PyObject *QUiLoadedLoadUiFromDevice(QUiLoader *self, QIODevice *dev, QWid
}
if (!PyErr_Occurred())
- PyErr_SetString(PyExc_RuntimeError, "Unable to open/read ui device");
+ PyErr_Format(PyExc_RuntimeError, "Unable to open/read ui device");
return nullptr;
}
diff --git a/sources/pyside6/PySide6/glue/qtwidgets.cpp b/sources/pyside6/PySide6/glue/qtwidgets.cpp
index 1b3e94016..f886106cf 100644
--- a/sources/pyside6/PySide6/glue/qtwidgets.cpp
+++ b/sources/pyside6/PySide6/glue/qtwidgets.cpp
@@ -792,10 +792,8 @@ const char *styleOptionType(const QStyleOption *o)
// @snippet qwizardpage-registerfield
auto *signalInst = reinterpret_cast<PySideSignalInstance *>(%PYARG_4);
const auto data = PySide::Signal::getEmitterData(signalInst);
-if (data.methodIndex == -1) {
- PyErr_SetString(PyExc_RuntimeError, "QWizardPage::registerField(): Unable to retrieve signal emitter.");
- return nullptr;
-}
+if (data.methodIndex == -1)
+ return PyErr_Format(PyExc_RuntimeError, "QWizardPage::registerField(): Unable to retrieve signal emitter.");
const auto method = data.emitter->metaObject()->method(data.methodIndex);
const QByteArray signature = QByteArrayLiteral("2") + method.methodSignature();
%BEGIN_ALLOW_THREADS
diff --git a/sources/pyside6/PySide6/qtdatavisualization_helper.h b/sources/pyside6/PySide6/qtdatavisualization_helper.h
index 6884900ee..8fee4492d 100644
--- a/sources/pyside6/PySide6/qtdatavisualization_helper.h
+++ b/sources/pyside6/PySide6/qtdatavisualization_helper.h
@@ -6,8 +6,8 @@
#include <sbkpython.h>
-#include <QtDataVisualization/QSurfaceDataProxy>
-#include <QtCore/QList>
+#include <QtDataVisualization/qsurfacedataproxy.h>
+#include <QtCore/qlist.h>
namespace QtDataVisualizationHelper {
diff --git a/sources/pyside6/PySide6/qtgraphs_helper.h b/sources/pyside6/PySide6/qtgraphs_helper.h
index 726f5fb37..e488fc7d3 100644
--- a/sources/pyside6/PySide6/qtgraphs_helper.h
+++ b/sources/pyside6/PySide6/qtgraphs_helper.h
@@ -6,8 +6,8 @@
#include <sbkpython.h>
-#include <QtGraphs/QSurfaceDataProxy>
-#include <QtCore/QList>
+#include <QtGraphs/qsurfacedataproxy.h>
+#include <QtCore/qlist.h>
namespace QtGraphsHelper {
diff --git a/sources/pyside6/doc/gettingstarted/index.rst b/sources/pyside6/doc/gettingstarted/index.rst
index 9d36061ad..51d8bea26 100644
--- a/sources/pyside6/doc/gettingstarted/index.rst
+++ b/sources/pyside6/doc/gettingstarted/index.rst
@@ -18,10 +18,10 @@ you might get them with ``brew``, and on **Windows** you can download the instal
website.
* **Python**: 3.9+ `[official Python website] <https://www.python.org/downloads/>`_
-* **Qt:** 6.4+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
+* **Qt:** 6.8+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
* **CMake:** 3.18+ `[official CMake website] <https://cmake.org/download/>`_
* **Git:** 2.0+. `[official Git website] <https://git-scm.com/downloads>`_
-* **libclang:** The libclang library, recommended: version 10 for 6.0+.
+* **libclang:** The libclang library, recommended: version 16+ for 6.8+.
Prebuilt versions for each OS can be `downloaded here`_.
* Check the `Supported Platforms of Qt`_
diff --git a/sources/pyside6/doc/gettingstarted/linux.rst b/sources/pyside6/doc/gettingstarted/linux.rst
index 912105ef8..eb1b5869d 100644
--- a/sources/pyside6/doc/gettingstarted/linux.rst
+++ b/sources/pyside6/doc/gettingstarted/linux.rst
@@ -30,12 +30,12 @@ Setting up CLANG
If you don't have libclang already in your system, you can download from the Qt servers::
- wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_140-based-linux-Rhel8.2-gcc9.2-x86_64.7z
+ wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_18.1.5-based-linux-Rhel8.6-gcc10.3-x86_64.7z
Extract the files, and leave it on any desired path, and set the environment
variable required::
- 7z x libclang-release_140-based-linux-Rhel8.2-gcc9.2-x86_64.7z
+ 7z x libclang-release_18.1.5-based-linux-Rhel8.6-gcc10.3-x86_64.7z
export LLVM_INSTALL_DIR=$PWD/libclang
Getting the source
@@ -45,16 +45,20 @@ Cloning the official repository can be done by::
git clone https://code.qt.io/pyside/pyside-setup
-Checking out the version that we want to build, for example 6.5::
+Checking out the version that we want to build, for example 6.8::
- cd pyside-setup && git checkout 6.5
+ cd pyside-setup && git checkout 6.8
Install the general dependencies::
pip install -r requirements.txt
+For building the documentation::
+
+ pip install -r requirements-doc.txt
+
.. note:: Keep in mind you need to use the same version as your Qt installation.
- Additionally, :command:`git checkout -b 6.5 --track origin/6.5` could be a better option
+ Additionally, :command:`git checkout -b 6.8 --track origin/6.8` could be a better option
in case you want to work on it.
Building and Installing (setuptools)
@@ -64,15 +68,15 @@ The ``setuptools`` approach uses the ``setup.py`` file to execute the build,
install, and packaging steps.
Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, :command:`/opt/Qt/6.5.0/gcc_64/bin/qtpaths`.
+for example, :command:`/opt/Qt/6.8.0/gcc_64/bin/qtpaths`.
Build can take a few minutes, so it is recommended to use more than one CPU core::
- python setup.py build --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+ python setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
To install on the current directory, just run::
- python setup.py install --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+ python setup.py install --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
Building and Installing (cmake)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/sources/pyside6/doc/gettingstarted/macOS.rst b/sources/pyside6/doc/gettingstarted/macOS.rst
index ff457a4e2..accffbe92 100644
--- a/sources/pyside6/doc/gettingstarted/macOS.rst
+++ b/sources/pyside6/doc/gettingstarted/macOS.rst
@@ -34,12 +34,12 @@ Setting up CLANG
If you don't have libclang already in your system, you can download from the Qt servers::
- wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_140-based-macos-universal.7z
+ wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_18.1.5-based-macos-universal.7z
Extract the files, and leave it on any desired path, and set the environment
variable required::
- 7z x libclang-release_140-based-macos-universal.7z
+ 7z x libclang-release_18.1.5-based-macos-universal.7z
export LLVM_INSTALL_DIR=$PWD/libclang
Getting PySide
@@ -49,32 +49,36 @@ Cloning the official repository can be done by::
git clone https://code.qt.io/pyside/pyside-setup
-Checking out the version that we want to build, for example, 6.5::
+Checking out the version that we want to build, for example, 6.8::
- cd pyside-setup && git checkout 6.5
+ cd pyside-setup && git checkout 6.8
Install the general dependencies::
pip install -r requirements.txt
+For building the documentation::
+
+ pip install -r requirements-doc.txt
+
.. note:: Keep in mind you need to use the same version as your Qt installation
Building PySide
~~~~~~~~~~~~~~~
Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, ``/opt/Qt/6.5.0/gcc_64/bin/qtpaths``.
+for example, ``/opt/Qt/6.8.0/gcc_64/bin/qtpaths``.
Build can take a few minutes, so it is recommended to use more than one CPU core::
- python setup.py build --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+ python setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
Installing PySide
~~~~~~~~~~~~~~~~~
To install on the current directory, just run::
- python setup.py install --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+ python setup.py install --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
Test installation
~~~~~~~~~~~~~~~~~
diff --git a/sources/pyside6/doc/gettingstarted/windows.rst b/sources/pyside6/doc/gettingstarted/windows.rst
index f688acd90..4451bb1fd 100644
--- a/sources/pyside6/doc/gettingstarted/windows.rst
+++ b/sources/pyside6/doc/gettingstarted/windows.rst
@@ -7,7 +7,7 @@ selected when using the online installer.
Requirements
------------
-* `MSVC2022`_ or (MSVC2019) for Python 3 on Windows,
+* `MSVC2022`_ for Python 3 on Windows,
* `OpenSSL`_ (optional for SSL support, Qt must have been configured using the same SSL library).
* ``sphinx`` package for the documentation (optional).
* Check the platform dependencies of `Qt for Windows`_.
@@ -38,7 +38,7 @@ Setting up CLANG
libclang can be downloaded from the
`Qt servers <https://download.qt.io/development_releases/prebuilt/libclang>`_.
-for example, ``libclang-release_140-based-windows-vs2019_64.7z``.
+for example, ``libclang-release_18.1.5-based-windows-vs2019_64.7z``.
Note that from version 12 onwards, the prebuilt Windows binaries from
`LLVM <https://www.llvm.org>`_ no longer contain CMake configuration files; so
@@ -57,21 +57,25 @@ Cloning the official repository can be done by::
git clone https://code.qt.io/pyside/pyside-setup
-Checking out the version that we want to build, for example, 6.5::
+Checking out the version that we want to build, for example, 6.8::
- cd pyside-setup && git checkout 6.5
+ cd pyside-setup && git checkout 6.8
Install the general dependencies::
pip install -r requirements.txt
+For building the documentation::
+
+ pip install -r requirements-doc.txt
+
.. note:: Keep in mind you need to use the same version as your Qt installation
Building PySide
~~~~~~~~~~~~~~~
Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, ``C:\Qt\6.5.0\msvc2019_64\bin\qtpaths.exe``.
+for example, ``C:\Qt\6.8.0\msvc2019_64\bin\qtpaths.exe``.
Build can take a few minutes, so it is recommended to use more than one CPU core::
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp
index 51070b4ad..2c75e39e1 100644
--- a/sources/pyside6/libpyside/globalreceiverv2.cpp
+++ b/sources/pyside6/libpyside/globalreceiverv2.cpp
@@ -274,9 +274,8 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
if (setSenderDynamicProperty)
m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender()));
- const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr;
Shiboken::AutoDecRef callback(m_data->callback());
- SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit);
+ SignalManager::callPythonMetaMethod(slot, args, callback);
if (setSenderDynamicProperty)
m_receiver->setProperty(senderDynamicProperty, QVariant{});
diff --git a/sources/pyside6/libpyside/pyside.cpp b/sources/pyside6/libpyside/pyside.cpp
index d5e815a42..9e12e3cd7 100644
--- a/sources/pyside6/libpyside/pyside.cpp
+++ b/sources/pyside6/libpyside/pyside.cpp
@@ -43,6 +43,7 @@
#include <QtCore/QMutex>
#include <QtCore/QStack>
#include <QtCore/QThread>
+#include <QtCore/private/qobject_p.h>
#include <algorithm>
#include <cstring>
@@ -63,6 +64,20 @@ QT_END_NAMESPACE
Q_LOGGING_CATEGORY(lcPySide, "qt.pyside.libpyside", QtCriticalMsg)
+static QObjectData *qt_object_private(const QObject *o)
+{
+ class FriendlyQObject : public QObject {
+ public:
+ using QObject::d_ptr;
+ };
+ return static_cast<const FriendlyQObject *>(o)->d_ptr.data();
+}
+
+static bool hasDynamicMetaObject(const QObject *o)
+{
+ return qt_object_private(o)->metaObject != nullptr;
+}
+
namespace PySide
{
@@ -675,6 +690,32 @@ static void invalidatePtr(any_t *object)
static const char invalidatePropertyName[] = "_PySideInvalidatePtr";
+// PYSIDE-2749: Skip over internal QML classes and classes
+// with dynamic meta objects when looking for the best matching
+// type to avoid unnessarily triggering the lazy load mechanism
+// for classes that do not have a binding from things like eventFilter().
+static inline bool isInternalObject(const char *name)
+{
+ return std::strstr(name, "QMLTYPE") != nullptr || std::strstr(name, "QQmlPrivate") != nullptr;
+}
+
+static const QMetaObject *metaObjectCandidate(const QObject *o)
+{
+ auto *metaObject = o->metaObject();
+ // Skip QML helper types and Python objects
+ if (hasDynamicMetaObject(o)) {
+ if (auto *super = metaObject->superClass())
+ metaObject = super;
+ }
+ for (auto *candidate = metaObject; candidate != nullptr; candidate = candidate->superClass()) {
+ if (!isInternalObject(candidate->className())) {
+ metaObject = candidate;
+ break;
+ }
+ }
+ return metaObject;
+}
+
// PYSIDE-1214, when creating new wrappers for classes inheriting QObject but
// not exposed to Python, try to find the best-matching (most-derived) Qt
// class by walking up the meta objects.
@@ -682,19 +723,13 @@ static const char *typeName(const QObject *cppSelf)
{
const char *typeName = typeid(*cppSelf).name();
if (!Shiboken::Conversions::getConverter(typeName)) {
- for (auto metaObject = cppSelf->metaObject(); metaObject; metaObject = metaObject->superClass()) {
+ auto *metaObject = metaObjectCandidate(cppSelf);
+ for (; metaObject != nullptr; metaObject = metaObject->superClass()) {
const char *name = metaObject->className();
if (Shiboken::Conversions::getConverter(name)) {
typeName = name;
break;
}
- // PYSIDE-2404: Did not find the name. Load the lazy classes
- // which have this name and try again.
- Shiboken::Module::loadLazyClassesWithName(name);
- if (Shiboken::Conversions::getConverter(name)) {
- typeName = name;
- break;
- }
}
}
return typeName;
@@ -738,7 +773,7 @@ PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *sbk_type)
}
}
- pyOut = Shiboken::Object::newObject(sbk_type, cppSelf, false, false, typeName(cppSelf));
+ pyOut = Shiboken::Object::newObjectWithHeuristics(sbk_type, cppSelf, false, typeName(cppSelf));
return pyOut;
}
diff --git a/sources/pyside6/libpyside/pysideclassinfo.cpp b/sources/pyside6/libpyside/pysideclassinfo.cpp
index 9ab5a7ad0..698cb1c76 100644
--- a/sources/pyside6/libpyside/pysideclassinfo.cpp
+++ b/sources/pyside6/libpyside/pysideclassinfo.cpp
@@ -51,16 +51,12 @@ PyObject *ClassInfoPrivate::tp_call(PyObject *self, PyObject *args, PyObject * /
auto *pData = DecoratorPrivate::get<ClassInfoPrivate>(self);
- if (pData->m_alreadyWrapped) {
- PyErr_SetString(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object");
- return nullptr;
- }
+ if (pData->m_alreadyWrapped)
+ return PyErr_Format(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object");
PyTypeObject *klassType = reinterpret_cast<PyTypeObject *>(klass);
- if (!PySide::ClassInfo::setClassInfo(klassType, pData->m_data)) {
- PyErr_SetString(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject");
- return nullptr;
- }
+ if (!PySide::ClassInfo::setClassInfo(klassType, pData->m_data))
+ return PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject");
pData->m_alreadyWrapped = true;
diff --git a/sources/pyside6/libpyside/pysideproperty.cpp b/sources/pyside6/libpyside/pysideproperty.cpp
index 457415479..3720815db 100644
--- a/sources/pyside6/libpyside/pysideproperty.cpp
+++ b/sources/pyside6/libpyside/pysideproperty.cpp
@@ -475,6 +475,7 @@ static const char *Property_SignatureStrings[] = {
"PySide6.QtCore.Property.read(self,fget:typing.Callable)->PySide6.QtCore.Property",
"PySide6.QtCore.Property.setter(self,fset:typing.Callable)->PySide6.QtCore.Property",
"PySide6.QtCore.Property.write(self,fset:typing.Callable)->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.__call__(self, func:typing.Callable)->PySide6.QtCore.Property",
nullptr}; // Sentinel
void init(PyObject *module)
diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp
index 774837e5b..35a6e7ef9 100644
--- a/sources/pyside6/libpyside/pysidesignal.cpp
+++ b/sources/pyside6/libpyside/pysidesignal.cpp
@@ -484,14 +484,10 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
return nullptr;
PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
- if (!source->d) {
- PyErr_Format(PyExc_RuntimeError, "cannot connect uninitialized SignalInstance");
- return nullptr;
- }
- if (source->deleted) {
- PyErr_Format(PyExc_RuntimeError, "Signal source has been deleted");
- return nullptr;
- }
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot connect uninitialized SignalInstance");
+ if (source->deleted)
+ return PyErr_Format(PyExc_RuntimeError, "Signal source has been deleted");
Shiboken::AutoDecRef pyArgs(PyList_New(0));
@@ -564,10 +560,8 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
PySide::PySideName::qtConnect()));
- if (pyMethod.isNull()) { // PYSIDE-79: check if pyMethod exists.
- PyErr_SetString(PyExc_RuntimeError, "method 'connect' vanished!");
- return nullptr;
- }
+ if (pyMethod.isNull()) // PYSIDE-79: check if pyMethod exists.
+ return PyErr_Format(PyExc_RuntimeError, "method 'connect' vanished!");
PyObject *result = PyObject_CallObject(pyMethod, tupleArgs);
if (connection_Check(result))
return result;
@@ -587,17 +581,13 @@ static int argCountInSignature(const char *signature)
static PyObject *signalInstanceEmit(PyObject *self, PyObject *args)
{
PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
- if (!source->d) {
- PyErr_Format(PyExc_RuntimeError, "cannot emit uninitialized SignalInstance");
- return nullptr;
- }
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot emit uninitialized SignalInstance");
// PYSIDE-2201: Check if the object has vanished meanwhile.
// Tried to revive it without exception, but this gives problems.
- if (source->deleted) {
- PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted");
- return nullptr;
- }
+ if (source->deleted)
+ return PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted");
Shiboken::AutoDecRef pyArgs(PyList_New(0));
int numArgsGiven = PySequence_Fast_GET_SIZE(args);
@@ -657,8 +647,7 @@ static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key)
message += '"' + data->d->signature + '"';
}
- PyErr_SetString(PyExc_IndexError, message.constData());
- return nullptr;
+ return PyErr_Format(PyExc_IndexError, message.constData());
}
static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signature)
@@ -675,10 +664,9 @@ static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signa
static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
{
auto source = reinterpret_cast<PySideSignalInstance *>(self);
- if (!source->d) {
- PyErr_Format(PyExc_RuntimeError, "cannot disconnect uninitialized SignalInstance");
- return nullptr;
- }
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot disconnect uninitialized SignalInstance");
+
Shiboken::AutoDecRef pyArgs(PyList_New(0));
PyObject *slot = Py_None;
@@ -725,8 +713,7 @@ static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
}
warnDisconnectFailed(slot, source->d->signature);
- Py_INCREF(Py_False);
- return Py_False;
+ Py_RETURN_FALSE;
}
// PYSIDE-68: Supply the missing __get__ function
@@ -761,10 +748,8 @@ static PyObject *signalCall(PyObject *self, PyObject *args, PyObject *kw)
// The only way calling a signal can succeed (the Python equivalent of C++'s operator() )
// is when a method with the same name as the signal is attached to an object.
// An example is QProcess::error() (don't check the docs, but the source code of qprocess.h).
- if (!signal->homonymousMethod) {
- PyErr_SetString(PyExc_TypeError, "native Qt signal is not callable");
- return nullptr;
- }
+ if (!signal->homonymousMethod)
+ return PyErr_Format(PyExc_TypeError, "native Qt signal is not callable");
// Check if there exists a method with the same name as the signal, which is also a static
// method in C++ land.
@@ -1202,13 +1187,11 @@ EmitterData getEmitterData(PySideSignalInstance *signal)
return result;
}
-QByteArrayList getArgsFromSignature(const char *signature, bool *isShortCircuit)
+QByteArrayList getArgsFromSignature(const char *signature)
{
QByteArray qsignature = QByteArray(signature).trimmed();
QByteArrayList result;
- if (isShortCircuit)
- *isShortCircuit = !qsignature.contains(u'(');
if (qsignature.contains("()") || qsignature.contains("(void)"))
return result;
if (qsignature.endsWith(')')) {
@@ -1292,24 +1275,21 @@ QByteArray getCallbackSignature(const char *signal, QObject *receiver,
functionName = Shiboken::String::toCString(slotArgs.functionName);
Q_ASSERT(!functionName.isEmpty());
- bool isShortCircuit = false;
-
if (functionName.startsWith('<') && functionName.endsWith('>')) { // fix "<lambda>"
functionName[0] = '_';
functionName[functionName.size() - 1] = '_';
}
QByteArray signature = encodeName ? codeCallbackName(callback, functionName) : functionName;
- QByteArrayList args = getArgsFromSignature(signal, &isShortCircuit);
+ QByteArrayList args = getArgsFromSignature(signal);
- if (!isShortCircuit) {
- signature.append(u'(');
- if (numArgs == -1)
- numArgs = std::numeric_limits<qsizetype>::max();
+ signature.append(u'(');
+ if (numArgs != -1) {
while (!args.isEmpty() && (args.size() > (numArgs - useSelf)))
args.removeLast();
- signature.append(args.join(','));
- signature.append(')');
}
+ signature.append(args.join(','));
+ signature.append(')');
+
return signature;
}
diff --git a/sources/pyside6/libpyside/pysidesignal.h b/sources/pyside6/libpyside/pysidesignal.h
index 589fc2c92..7493f94b5 100644
--- a/sources/pyside6/libpyside/pysidesignal.h
+++ b/sources/pyside6/libpyside/pysidesignal.h
@@ -142,12 +142,10 @@ PYSIDE_API QByteArray getCallbackSignature(const char *signal, QObject *receiver
* This function parses the signature and then returns a list of argument types.
*
* @param signature The signal signature
- * @param isShortCircuit If this is a shortCircuit(python<->python) signal
* @return Return true if this is a Qt Signal, otherwise return false
* @todo replace return type by QList<QByteArray>
**/
-QByteArrayList getArgsFromSignature(const char *signature,
- bool *isShortCircuit = nullptr);
+QByteArrayList getArgsFromSignature(const char *signature);
} // namespace PySide::Signal
diff --git a/sources/pyside6/libpyside/signalmanager.cpp b/sources/pyside6/libpyside/signalmanager.cpp
index 557f130e0..f4c2bbf43 100644
--- a/sources/pyside6/libpyside/signalmanager.cpp
+++ b/sources/pyside6/libpyside/signalmanager.cpp
@@ -43,7 +43,6 @@ using namespace Qt::StringLiterals;
static PyObject *metaObjectAttr = nullptr;
static PyObject *parseArguments(const QMetaMethod &method, void **args);
-static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args);
static bool qAppRunning = false;
@@ -474,16 +473,7 @@ bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *ar
signal++;
int signalIndex = source->metaObject()->indexOfSignal(signal);
- if (signalIndex != -1) {
- // cryptic but works!
- // if the signature doesn't have a '(' it's a shor circuited signal, i.e. std::find
- // returned the string null terminator.
- bool isShortCircuit = !*std::find(signal, signal + std::strlen(signal), '(');
- return isShortCircuit
- ? emitShortCircuitSignal(source, signalIndex, args)
- : MetaFunction::call(source, signalIndex, args);
- }
- return false;
+ return signalIndex != -1 && MetaFunction::call(source, signalIndex, args);
}
// Handle errors from meta calls. Requires GIL and PyErr_Occurred()
@@ -589,7 +579,7 @@ int SignalManager::SignalManagerPrivate::qtMethodMetacall(QObject *object,
PyErr_Format(PyExc_AttributeError, "Slot '%s::%s' not found.",
metaObject->className(), method.methodSignature().constData());
} else {
- SignalManager::callPythonMetaMethod(method, args, pyMethod, false);
+ SignalManager::callPythonMetaMethod(method, args, pyMethod);
}
}
// WARNING Isn't safe to call any metaObject and/or object methods beyond this point
@@ -635,13 +625,12 @@ int SignalManager::qt_metacall(QObject *object, QMetaObject::Call call, int id,
return id;
}
-int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args, PyObject *pyMethod, bool isShortCuit)
+int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args, PyObject *pyMethod)
{
Q_ASSERT(pyMethod);
Shiboken::GilState gil;
- PyObject *pyArguments = isShortCuit
- ? reinterpret_cast<PyObject *>(args[1]) : parseArguments(method, args);
+ PyObject *pyArguments = parseArguments(method, args);
if (pyArguments) {
QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter;
@@ -656,8 +645,7 @@ int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args,
Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments));
- if (!isShortCuit && pyArguments)
- Py_DECREF(pyArguments);
+ Py_DECREF(pyArguments);
if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter)
retConverter->toCpp(retval, args[0]);
@@ -824,11 +812,4 @@ static PyObject *parseArguments(const QMetaMethod &method, void **args)
return preparedArgs;
}
-static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args)
-{
- void *signalArgs[2] = {nullptr, args};
- source->qt_metacall(QMetaObject::InvokeMetaMethod, signalIndex, signalArgs);
- return true;
-}
-
#include "signalmanager.moc"
diff --git a/sources/pyside6/libpyside/signalmanager.h b/sources/pyside6/libpyside/signalmanager.h
index c531d6630..397700df1 100644
--- a/sources/pyside6/libpyside/signalmanager.h
+++ b/sources/pyside6/libpyside/signalmanager.h
@@ -81,7 +81,7 @@ public:
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, bool isShortCuit);
+ static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj);
static void deleteGlobalReceiver(const QObject *globalReceiver);
diff --git a/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp b/sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
index 618d621bd..4ccd459d5 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
@@ -369,7 +392,7 @@ QObject *SingletonQObjectCreationBase::handleReturnValue(PyObject *retVal)
using Shiboken::Conversions::isPythonToCppPointerConvertible;
// Make sure the callback returns something we can convert, else the entire application will crash.
if (retVal == nullptr) {
- PyErr_SetString(PyExc_TypeError, "Callback returns 0 value.");
+ PyErr_Format(PyExc_TypeError, "Callback returns 0 value.");
return nullptr;
}
if (isPythonToCppPointerConvertible(qObjectType(), retVal) == nullptr) {
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/plugins/designer/CMakeLists.txt b/sources/pyside6/plugins/designer/CMakeLists.txt
index c86d328cf..717652314 100644
--- a/sources/pyside6/plugins/designer/CMakeLists.txt
+++ b/sources/pyside6/plugins/designer/CMakeLists.txt
@@ -19,6 +19,9 @@ target_sources(PySidePlugin PRIVATE
)
# See libshiboken/CMakeLists.txt
+
+target_compile_definitions(PySidePlugin PRIVATE -DQT_NO_KEYWORDS=1)
+
if(PYTHON_LIMITED_API)
target_compile_definitions(PySidePlugin PRIVATE "-DPy_LIMITED_API=0x03050000")
endif()
diff --git a/sources/pyside6/tests/QtCore/CMakeLists.txt b/sources/pyside6/tests/QtCore/CMakeLists.txt
index f584bfde6..f0228d943 100644
--- a/sources/pyside6/tests/QtCore/CMakeLists.txt
+++ b/sources/pyside6/tests/QtCore/CMakeLists.txt
@@ -34,6 +34,7 @@ PYSIDE_TEST(bug_1313.py)
PYSIDE_TEST(bug_PYSIDE-41.py)
PYSIDE_TEST(bug_PYSIDE-42.py)
PYSIDE_TEST(bug_PYSIDE-164.py)
+PYSIDE_TEST(bug_PYSIDE-2745.py)
PYSIDE_TEST(blocking_signals_test.py)
PYSIDE_TEST(classinfo_test.py)
PYSIDE_TEST(child_event_test.py)
diff --git a/sources/pyside6/tests/QtCore/bug_927.py b/sources/pyside6/tests/QtCore/bug_927.py
index 1ecea61b2..c15a7014b 100644
--- a/sources/pyside6/tests/QtCore/bug_927.py
+++ b/sources/pyside6/tests/QtCore/bug_927.py
@@ -24,7 +24,7 @@ class thread_function():
class Task(QRunnable):
def run(self):
- QThread.sleep(2) # Sleep 2 seconds
+ QThread.msleep(100)
class QThreadPoolTest(unittest.TestCase):
@@ -34,15 +34,15 @@ class QThreadPoolTest(unittest.TestCase):
for i in range(3):
task = Task()
QThreadPool.globalInstance().start(task)
- time.sleep(1) # Sleep 1 second
+ time.sleep(0.05)
- QThreadPool.globalInstance().waitForDone()
+ self.assertTrue(QThreadPool.globalInstance().waitForDone())
def testCallable(self):
global thread_function_called
tp = QThreadPool.globalInstance()
tp.start(thread_function)
- tp.waitForDone()
+ self.assertTrue(tp.waitForDone())
self.assertTrue(thread_function_called)
diff --git a/sources/pyside6/tests/QtCore/bug_PYSIDE-2745.py b/sources/pyside6/tests/QtCore/bug_PYSIDE-2745.py
new file mode 100644
index 000000000..3d6c603b7
--- /dev/null
+++ b/sources/pyside6/tests/QtCore/bug_PYSIDE-2745.py
@@ -0,0 +1,37 @@
+# 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 PySide6.QtCore import QTimer
+
+from helper.usesqapplication import UsesQApplication
+
+
+class TestBugPYSIDE2745(UsesQApplication):
+
+ def setUp(self):
+ UsesQApplication.setUp(self)
+ self.counter = 0
+
+ def fail(self):
+ self.counter += 1
+ raise Exception()
+
+ def test_fail(self):
+ QTimer.singleShot(0, self.fail)
+ QTimer.singleShot(0, self.fail)
+ QTimer.singleShot(1, self.app.quit)
+ self.app.exec()
+ self.assertEqual(self.counter, 2)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/sources/pyside6/tests/QtGui/qbrush_test.py b/sources/pyside6/tests/QtGui/qbrush_test.py
index 800e6f072..69262328b 100644
--- a/sources/pyside6/tests/QtGui/qbrush_test.py
+++ b/sources/pyside6/tests/QtGui/qbrush_test.py
@@ -13,7 +13,7 @@ from init_paths import init_test_paths
init_test_paths(False)
from PySide6.QtCore import Qt
-from PySide6.QtGui import QColor, QBrush
+from PySide6.QtGui import QColor, QBrush, QConicalGradient
from helper.usesqapplication import UsesQApplication
@@ -30,6 +30,14 @@ class Constructor(UsesQApplication):
obj = QBrush(Qt.blue)
self.assertEqual(obj.color(), Qt.blue)
+ def testGradient(self):
+ """Test type discovery on class hierarchies with non-virtual
+ destructors by specifying a polymorphic-id-expression without
+ polymorphic-name-function."""
+ gradient = QConicalGradient()
+ brush = QBrush(gradient)
+ self.assertEqual(type(brush.gradient()), type(gradient))
+
if __name__ == '__main__':
unittest.main()
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/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/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py b/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
index 02293c33e..db60c8c3f 100644
--- a/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
+++ b/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
@@ -63,6 +63,9 @@ class DeployTestBase(LongSortedOptionTest):
cls.deploy_lib = importlib.import_module("deploy_lib")
cls.deploy = importlib.import_module("deploy")
sys.modules["deploy"] = cls.deploy
+ files_to_ignore = [".cpp.o", ".qsb", ".webp"]
+ cls.dlls_ignore_nuitka = " ".join([f"--noinclude-dlls=*{file}"
+ for file in files_to_ignore])
# required for comparing long strings
cls.maxDiff = None
@@ -107,6 +110,7 @@ class TestPySide6DeployWidgets(DeployTestBase):
f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
f" --noinclude-qt-translations"
f" --include-qt-plugins={plugins_nuitka}"
+ f" {self.dlls_ignore_nuitka}"
)
if sys.platform.startswith("linux"):
self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)} --onefile"
@@ -189,6 +193,7 @@ class TestPySide6DeployQml(DeployTestBase):
self.deployment_files = self.temp_example_qml / "deployment"
self.first_qml_file = "main.qml"
self.second_qml_file = "MovingRectangle.qml"
+
# All the plugins included. This is different from plugins_nuitka, because Nuitka bundles
# some plugins by default
self.all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
@@ -203,6 +208,8 @@ class TestPySide6DeployQml(DeployTestBase):
f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports"
f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
f" --noinclude-qt-translations"
+ f" {self.dlls_ignore_nuitka}"
+ " --noinclude-dlls=*/qml/QtQuickEffectMaker/*"
f" --include-qt-plugins={plugins_nuitka}"
f" --include-data-files={str(self.temp_example_qml / self.first_qml_file)}="
f"./main.qml --include-data-files="
@@ -334,6 +341,8 @@ class TestPySide6DeployWebEngine(DeployTestBase):
f" --noinclude-qt-translations --include-qt-plugins=all"
f" {data_files_cmd}"
f" --include-qt-plugins={plugins_nuitka}"
+ f" {self.dlls_ignore_nuitka}"
+ " --noinclude-dlls=*/qml/QtQuickEffectMaker/*"
)
if sys.platform != "win32":
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
index 89d636964..9831f911b 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
@@ -461,7 +461,7 @@ FileModelItem AbstractMetaBuilderPrivate::buildDom(QByteArrayList arguments,
+ clang::languageLevelOption(level));
}
FileModelItem result = clang::parse(arguments, addCompilerSupportArguments,
- clangFlags, builder)
+ level, clangFlags, builder)
? builder.dom() : FileModelItem();
const clang::BaseVisitor::Diagnostics &diagnostics = builder.diagnostics();
if (const auto diagnosticsCount = diagnostics.size()) {
@@ -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/clangparser/clangparser.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp
index da6930476..6c0cf3ae2 100644
--- a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp
@@ -235,6 +235,7 @@ static QByteArray msgCreateTranslationUnit(const QByteArrayList &clangArgs, unsi
static CXTranslationUnit createTranslationUnit(CXIndex index,
const QByteArrayList &args,
bool addCompilerSupportArguments,
+ LanguageLevel level,
unsigned flags = 0)
{
// courtesy qdoc
@@ -255,7 +256,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index,
QByteArrayList clangArgs;
if (addCompilerSupportArguments) {
- clangArgs += emulatedCompilerOptions();
+ clangArgs += emulatedCompilerOptions(level);
clangArgs += defaultArgs;
}
clangArgs += detectVulkan();
@@ -280,7 +281,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index,
*/
bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments,
- unsigned clangFlags, BaseVisitor &bv)
+ LanguageLevel level, unsigned clangFlags, BaseVisitor &bv)
{
CXIndex index = clang_createIndex(0 /* excludeDeclarationsFromPCH */,
1 /* displayDiagnostics */);
@@ -291,7 +292,7 @@ bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments,
CXTranslationUnit translationUnit =
createTranslationUnit(index, clangArgs, addCompilerSupportArguments,
- clangFlags);
+ level, clangFlags);
if (!translationUnit)
return false;
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.h b/sources/shiboken6/ApiExtractor/clangparser/clangparser.h
index 4a46248e4..22e0a50cd 100644
--- a/sources/shiboken6/ApiExtractor/clangparser/clangparser.h
+++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.h
@@ -14,6 +14,8 @@
#include <string_view>
#include <utility>
+enum class LanguageLevel;
+
namespace clang {
struct Diagnostic;
@@ -79,7 +81,7 @@ private:
bool parse(const QByteArrayList &clangArgs,
bool addCompilerSupportArguments,
- unsigned clangFlags, BaseVisitor &ctx);
+ LanguageLevel level, unsigned clangFlags, BaseVisitor &ctx);
} // namespace clang
diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp
index 4c13b141f..2460ca9d9 100644
--- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp
+++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp
@@ -365,14 +365,15 @@ static void appendClangBuiltinIncludes(HeaderPaths *p)
}
// Returns clang options needed for emulating the host compiler
-QByteArrayList emulatedCompilerOptions()
+QByteArrayList emulatedCompilerOptions(LanguageLevel level)
{
QByteArrayList result;
HeaderPaths headerPaths;
switch (compiler()) {
case Compiler::Msvc:
result.append("-fms-compatibility-version="_ba + msvcCompatVersion());
- result.append(QByteArrayLiteral("-fdelayed-template-parsing"));
+ if (level < LanguageLevel::Cpp20)
+ result.append("-fdelayed-template-parsing"_ba);
result.append(QByteArrayLiteral("-Wno-microsoft-enum-value"));
result.append("/Zc:__cplusplus"_ba);
// Fix yvals_core.h: STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update)
diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h
index 462e8f205..f1d63b7c3 100644
--- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h
+++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h
@@ -33,7 +33,7 @@ enum class Platform {
namespace clang {
QVersionNumber libClangVersion();
-QByteArrayList emulatedCompilerOptions();
+QByteArrayList emulatedCompilerOptions(LanguageLevel level);
LanguageLevel emulatedCompilerLanguageLevel();
const char *languageLevelOption(LanguageLevel l);
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/ApiExtractor/typedatabase.cpp b/sources/shiboken6/ApiExtractor/typedatabase.cpp
index 749c4baa3..61fd22418 100644
--- a/sources/shiboken6/ApiExtractor/typedatabase.cpp
+++ b/sources/shiboken6/ApiExtractor/typedatabase.cpp
@@ -88,7 +88,8 @@ static const PythonTypes &builtinPythonTypes()
{u"PyObject"_s, u"true"_s, TypeSystem::CPythonType::Other},
// shiboken-specific
{u"PyPathLike"_s, u"Shiboken::String::checkPath"_s, TypeSystem::CPythonType::Other},
- {u"PySequence"_s, u"Shiboken::String::checkIterable"_s, TypeSystem::CPythonType::Other},
+ {u"PySequence"_s, u"Shiboken::String::checkIterableArgument"_s,
+ TypeSystem::CPythonType::Other},
{u"PyUnicode"_s, u"PyUnicode_Check"_s, TypeSystem::CPythonType::String},
{u"PyTypeObject"_s, u"PyType_Check"_s, TypeSystem::CPythonType::Other},
{u"str"_s, u"Shiboken::String::check"_s, TypeSystem::CPythonType::String},
diff --git a/sources/shiboken6/doc/shibokenmodule.rst b/sources/shiboken6/doc/shibokenmodule.rst
index 2f1c6d166..3bc4fa6ba 100644
--- a/sources/shiboken6/doc/shibokenmodule.rst
+++ b/sources/shiboken6/doc/shibokenmodule.rst
@@ -125,6 +125,11 @@ To import the module:
Dumps the map of wrappers existing in libshiboken to standard error.
+.. function:: dumpConverters()
+
+ Dumps the map of named converters existing in libshiboken to standard
+ error.
+
.. py:class:: VoidPtr(address, size = -1, writeable = 0)
:param address: (PyBuffer, SbkObject, int, VoidPtr)
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/generator.cpp b/sources/shiboken6/generator/generator.cpp
index b224858c5..a01326530 100644
--- a/sources/shiboken6/generator/generator.cpp
+++ b/sources/shiboken6/generator/generator.cpp
@@ -231,10 +231,9 @@ QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartP
const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType();
smartPointerType.typeEntry()->qualifiedCppName();
QString fileName = smartPointerType.typeEntry()->qualifiedCppName().toLower();
- fileName.replace(u"::"_s, u"_"_s);
- fileName.append(u"_"_s);
+ fileName.append(u'_');
fileName.append(innerType.name().toLower());
-
+ fileName.replace(u"::"_s, u"_"_s); // std::shared_ptr<std::string>
return fileName;
}
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
index 84d3c93d3..0eeeec6f9 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -1337,7 +1337,8 @@ void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s,
s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent
<< "// An error happened in python code!\n"
- << "Shiboken::Errors::storeErrorOrPrint();\n"
+ << "Shiboken::Errors::storePythonOverrideErrorOrPrint(\""
+ << func->ownerClass()->name() << "\", funcName);\n"
<< returnStatement.statement << "\n" << outdent
<< "}\n";
@@ -1590,6 +1591,33 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta
s << '\n';
}
+static void writePointerToPythonConverter(TextStream &c,
+ const AbstractMetaClassCPtr &metaClass,
+ const QString &typeName,
+ const QString &cpythonType)
+{
+ c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n"
+ << "if (pyOut) {\n" << indent
+ << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent
+ << "}\n";
+
+ const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction();
+ if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) {
+ c << "return Shiboken::Object::newObjectWithHeuristics("
+ << cpythonType << ", const_cast<void *>(cppIn), false);\n";
+ return;
+ }
+
+ c << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn);
+const char *typeName = )";
+ if (nameFunc.isEmpty())
+ c << "typeid(*tCppIn).name();\n";
+ else
+ c << nameFunc << "(tCppIn);\n";
+ c << "return Shiboken::Object::newObjectForPointer("
+ << cpythonType << ", const_cast<void *>(cppIn), false, typeName);\n";
+}
+
void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass,
const GeneratorContext &classContext) const
{
@@ -1635,26 +1663,7 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas
c << "return PySide::getWrapperForQObject(reinterpret_cast<"
<< typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n";
} else {
- c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n"
- << "if (pyOut) {\n" << indent
- << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent
- << "}\n"
- << "bool exactType = false;\n"
- << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn);
-const char *typeName = )";
-
- const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction();
- if (nameFunc.isEmpty())
- c << "typeid(*tCppIn).name();\n";
- else
- c << nameFunc << "(tCppIn);\n";
- c << R"(auto *sbkType = Shiboken::ObjectType::typeForTypeName(typeName);
-if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType))
- exactType = true;
-)"
- << "PyObject *result = Shiboken::Object::newObject(" << cpythonType
- << R"(, const_cast<void *>(cppIn), false, exactType, typeName);
-return result;)";
+ writePointerToPythonConverter(c, metaClass, typeName, cpythonType);
}
std::swap(targetTypeName, sourceTypeName);
writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName);
@@ -5114,10 +5123,15 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const
const AbstractMetaFunctionCList conversions =
api().implicitConversions(metaType);
for (const auto &f : conversions) {
- if (f->isConstructor() && !f->arguments().isEmpty())
- signatures << f->arguments().constFirst().type().pythonSignature();
- else if (f->isConversionOperator())
+ if (f->isConstructor() && !f->arguments().isEmpty()) {
+ // PYSIDE-2712: modified types from converting constructors are not always correct
+ // candidates if they are modified by the type system reference
+ if (!f->arguments().constFirst().isTypeModified()) {
+ signatures << f->arguments().constFirst().type().pythonSignature();
+ }
+ } else if (f->isConversionOperator()) {
signatures << f->ownerClass()->fullName();
+ }
}
const qsizetype size = signatures.size();
@@ -5826,7 +5840,7 @@ void CppGenerator::writeTypeDiscoveryFunction(TextStream &s,
} else if (metaClass->isPolymorphic()) {
const auto &ancestors = metaClass->allTypeSystemAncestors();
for (const auto &ancestor : ancestors) {
- if (ancestor->baseClass())
+ if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase())
continue;
if (ancestor->isPolymorphic()) {
s << "if (instanceType == Shiboken::SbkType< " << m_gsp
@@ -6046,18 +6060,14 @@ void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr,
} else if (hasParent) {
const QString &enclosingName = enclosingEntry->name();
const auto parts = QStringView{enclosingName}.split(u"::", Qt::SkipEmptyParts);
+ const QString namePathPrefix = enclosingEntry->name().replace("::"_L1, "."_L1);
callStr << "Shiboken::Module::AddTypeCreationFunction("
- << "module, \"" << pythonName << "\", " << functionName << ", \"";
- for (qsizetype i = 0; i < parts.size(); ++i) {
- if (i > 0)
- callStr << "\", \"";
- callStr << parts.at(i);
- }
- callStr << "\");\n";
+ << "module, \"" << parts[0] << "\", "
+ << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n";
} else {
callStr << "Shiboken::Module::AddTypeCreationFunction("
<< "module, \"" << pythonName << "\", "
- << "init_" << initFunctionName << ");\n";
+ << functionName << ");\n";
}
}
@@ -6403,9 +6413,14 @@ bool CppGenerator::finishGeneration()
s << "{nullptr, nullptr}\n" << outdent << "};\n"
<< "// The new global structure consisting of (type, name) pairs.\n"
- << cppApiVariableName() << " = cppApi;\n"
- << "// The backward compatible alias with upper case indexes.\n"
- << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n\n";
+ << cppApiVariableName() << " = cppApi;\n";
+ if (usePySideExtensions())
+ s << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n";
+ s << "// The backward compatible alias with upper case indexes.\n"
+ << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n";
+ if (usePySideExtensions())
+ s << "QT_WARNING_POP\n";
+ s << '\n';
}
s << "// Create an array of primitive type converters for the current module.\n"
diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp
index 1f574b47c..35d0d114f 100644
--- a/sources/shiboken6/generator/shiboken/headergenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp
@@ -706,8 +706,12 @@ bool HeaderGenerator::finishGeneration()
macrosStream << ti;
macrosStream << "};\n\n";
+ // FIXME: Remove backwards compatible variable in PySide 7.
macrosStream << "// This variable stores all Python types exported by this module.\n";
macrosStream << "extern Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << ";\n\n";
+ macrosStream << "// This variable stores all Python types exported by this module ";
+ macrosStream << "in a backwards compatible way with identical indexing.\n";
+ macrosStream << "[[deprecated]] extern PyTypeObject **" << cppApiVariableNameOld() << ";\n\n";
macrosStream << "// This variable stores the Python module object exported by this module.\n";
macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n";
macrosStream << "// This variable stores all type converters exported by this module.\n";
diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp
index 15c79479e..0ce80d0c6 100644
--- a/sources/shiboken6/libshiboken/basewrapper.cpp
+++ b/sources/shiboken6/libshiboken/basewrapper.cpp
@@ -27,7 +27,9 @@
#include "signature_p.h"
#include "voidptr.h"
+#include <string>
#include <iostream>
+#include <sstream>
#if defined(__APPLE__)
#include <dlfcn.h>
@@ -37,7 +39,73 @@ namespace {
void _destroyParentInfo(SbkObject *obj, bool keepReference);
}
-static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEntries &dts)
+namespace Shiboken
+{
+// Walk through the first level of non-user-type Sbk base classes relevant for
+// C++ object allocation. Return true from the predicate to terminate.
+template <class Predicate>
+bool walkThroughBases(PyTypeObject *currentType, Predicate predicate)
+{
+ PyObject *bases = currentType->tp_bases;
+ const Py_ssize_t numBases = PyTuple_Size(bases);
+ bool result = false;
+ for (Py_ssize_t i = 0; !result && i < numBases; ++i) {
+ auto type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, i));
+ if (PyType_IsSubtype(type, SbkObject_TypeF()) != 0) {
+ result = PepType_SOTP(type)->is_user_type
+ ? walkThroughBases(type, predicate) : predicate(type);
+ }
+ }
+ return result;
+}
+
+int getTypeIndexOnHierarchy(PyTypeObject *baseType, PyTypeObject *desiredType)
+{
+ int index = -1;
+ walkThroughBases(baseType, [&index, desiredType](PyTypeObject *node) {
+ ++index;
+ return PyType_IsSubtype(node, desiredType) != 0;
+ });
+ return index;
+}
+
+int getNumberOfCppBaseClasses(PyTypeObject *baseType)
+{
+ int count = 0;
+ walkThroughBases(baseType, [&count](PyTypeObject *) {
+ ++count;
+ return false;
+ });
+ return count;
+}
+
+std::vector<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType)
+{
+ std::vector<PyTypeObject *> cppBaseClasses;
+ walkThroughBases(baseType, [&cppBaseClasses](PyTypeObject *node) {
+ cppBaseClasses.push_back(node);
+ return false;
+ });
+ return cppBaseClasses;
+}
+
+using DestructorEntries = std::vector<DestructorEntry>;
+
+DestructorEntries getDestructorEntries(SbkObject *o)
+{
+ DestructorEntries result;
+ void **cptrs = o->d->cptr;
+ walkThroughBases(Py_TYPE(o), [&result, cptrs](PyTypeObject *node) {
+ auto *sotp = PepType_SOTP(node);
+ auto index = result.size();
+ result.push_back(DestructorEntry{sotp->cpp_dtor,
+ cptrs[index]});
+ return false;
+ });
+ return result;
+}
+
+static void callDestructor(const DestructorEntries &dts)
{
for (const auto &e : dts) {
Shiboken::ThreadStateSaver threadSaver;
@@ -46,6 +114,8 @@ static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEnt
}
}
+} // namespace Shiboken
+
extern "C"
{
@@ -129,31 +199,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 +236,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)
@@ -370,9 +410,8 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
if (sotp->delete_in_main_thread && Shiboken::currentThreadId() != Shiboken::mainThreadId()) {
auto &bindingManager = Shiboken::BindingManager::instance();
if (sotp->is_multicpp) {
- Shiboken::DtorAccumulatorVisitor visitor(sbkObj);
- Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor);
- for (const auto &e : visitor.entries())
+ const auto entries = Shiboken::getDestructorEntries(sbkObj);
+ for (const auto &e : entries)
bindingManager.addToDeletionInMainThread(e);
} else {
Shiboken::DestructorEntry e{sotp->cpp_dtor, sbkObj->d->cptr[0]};
@@ -390,10 +429,9 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
if (canDelete) {
if (sotp->is_multicpp) {
- Shiboken::DtorAccumulatorVisitor visitor(sbkObj);
- Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor);
+ const auto entries = Shiboken::getDestructorEntries(sbkObj);
Shiboken::Object::deallocData(sbkObj, true);
- callDestructor(visitor.entries());
+ callDestructor(entries);
} else {
void *cptr = sbkObj->d->cptr[0];
Shiboken::Object::deallocData(sbkObj, true);
@@ -730,54 +768,24 @@ void _destroyParentInfo(SbkObject *obj, bool keepReference)
namespace Shiboken
{
-bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor)
-{
- PyObject *bases = currentType->tp_bases;
- Py_ssize_t numBases = PyTuple_GET_SIZE(bases);
- bool result = false;
- for (int i = 0; !result && i < numBases; ++i) {
- auto type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i));
- if (PyType_IsSubtype(type, reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) {
- result = PepType_SOTP(type)->is_user_type
- ? walkThroughClassHierarchy(type, visitor) : visitor->visit(type);
- }
- }
- return result;
-}
// Wrapper metatype and base type ----------------------------------------------------------
-HierarchyVisitor::HierarchyVisitor() = default;
-HierarchyVisitor::~HierarchyVisitor() = default;
-
-bool BaseCountVisitor::visit(PyTypeObject *)
-{
- m_count++;
- return false;
-}
-
-bool BaseAccumulatorVisitor::visit(PyTypeObject *node)
-{
- m_bases.push_back(node);
- return false;
-}
-
-bool GetIndexVisitor::visit(PyTypeObject *node)
-{
- m_index++;
- return PyType_IsSubtype(node, m_desiredType);
-}
+void _initMainThreadId(); // helper.cpp
-bool DtorAccumulatorVisitor::visit(PyTypeObject *node)
+static std::string msgFailedToInitializeType(const char *description)
{
- auto *sotp = PepType_SOTP(node);
- m_entries.push_back(DestructorEntry{sotp->cpp_dtor,
- m_pyObject->d->cptr[m_entries.size()]});
- return false;
+ std::ostringstream stream;
+ stream << "[libshiboken] Failed to initialize " << description;
+ if (auto *error = PepErr_GetRaisedException()) {
+ if (auto *str = PyObject_Str(error))
+ stream << ": " << Shiboken::String::toCString(str);
+ Py_DECREF(error);
+ }
+ stream << '.';
+ return stream.str();
}
-void _initMainThreadId(); // helper.cpp
-
namespace Conversions { void init(); }
void init()
@@ -793,11 +801,13 @@ void init()
//Init private data
Pep384_Init();
- if (PyType_Ready(SbkObjectType_TypeF()) < 0)
- Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapperType metatype.");
+ auto *type = SbkObjectType_TypeF();
+ if (type == nullptr || PyType_Ready(type) < 0)
+ Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapperType metatype").c_str());
- if (PyType_Ready(SbkObject_TypeF()) < 0)
- Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapper type.");
+ type = SbkObject_TypeF();
+ if (type == nullptr || PyType_Ready(type) < 0)
+ Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapper type").c_str());
VoidPtr::init();
@@ -871,20 +881,6 @@ PyObject *checkInvalidArgumentCount(Py_ssize_t numArgs, Py_ssize_t minArgs, Py_s
return result;
}
-class FindBaseTypeVisitor : public HierarchyVisitor
-{
-public:
- explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {}
-
- bool visit(PyTypeObject *node) override
- {
- return node == m_typeToFind;
- }
-
-private:
- PyTypeObject *m_typeToFind;
-};
-
std::vector<SbkObject *> splitPyObject(PyObject *pyObj)
{
std::vector<SbkObject *> result;
@@ -925,8 +921,8 @@ bool isUserType(PyTypeObject *type)
bool canCallConstructor(PyTypeObject *myType, PyTypeObject *ctorType)
{
- FindBaseTypeVisitor visitor(ctorType);
- if (!walkThroughClassHierarchy(myType, &visitor)) {
+ auto findBasePred = [ctorType](PyTypeObject *type) { return type == ctorType; };
+ if (!walkThroughBases(myType, findBasePred)) {
PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", ctorType->tp_name, myType->tp_name);
return false;
}
@@ -1001,8 +997,7 @@ introduceWrapperType(PyObject *enclosingObject,
PyObject *bases,
unsigned wrapperFlags)
{
- const auto basesSize = PySequence_Fast_GET_SIZE(bases);
- assert(basesSize > 0);
+ assert(PySequence_Fast_GET_SIZE(bases) > 0);
typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0);
auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF());
@@ -1076,6 +1071,26 @@ bool hasSpecialCastFunction(PyTypeObject *sbkType)
return d != nullptr && d->mi_specialcast != nullptr;
}
+// Find whether base is a direct single line base class of type
+// (no multiple inheritance), that is, a C++ pointer cast can safely be done.
+static bool isDirectAncestor(PyTypeObject *type, PyTypeObject *base)
+{
+ if (type == base)
+ return true;
+ if (PyTuple_Size(type->tp_bases) == 0)
+ return false;
+ auto *sbkObjectType = SbkObject_TypeF();
+ auto *firstBase = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, 0));
+ return firstBase != sbkObjectType
+ && PyType_IsSubtype(type, sbkObjectType) != 0
+ && isDirectAncestor(firstBase, base);
+}
+
+bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType)
+{
+ return isDirectAncestor(targetType, baseType);
+}
+
} // namespace ObjectType
@@ -1164,9 +1179,7 @@ void callCppDestructors(SbkObject *pyObj)
PyTypeObject *type = Py_TYPE(pyObj);
auto *sotp = PepType_SOTP(type);
if (sotp->is_multicpp) {
- Shiboken::DtorAccumulatorVisitor visitor(pyObj);
- Shiboken::walkThroughClassHierarchy(type, &visitor);
- callDestructor(visitor.entries());
+ callDestructor(getDestructorEntries(pyObj));
} else {
Shiboken::ThreadStateSaver threadSaver;
threadSaver.save();
@@ -1447,25 +1460,67 @@ SbkObject *findColocatedChild(SbkObject *wrapper,
return nullptr;
}
+// Legacy, for compatibility only.
PyObject *newObject(PyTypeObject *instanceType,
void *cptr,
bool hasOwnership,
bool isExactType,
const char *typeName)
{
- // Try to find the exact type of cptr.
- if (!isExactType) {
- if (PyTypeObject *exactType = ObjectType::typeForTypeName(typeName)) {
- instanceType = exactType;
- } else {
- auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType);
- if (resolved.first != nullptr) {
- instanceType = resolved.first;
- cptr = resolved.second;
- }
+ return isExactType
+ ? newObjectForType(instanceType, cptr, hasOwnership)
+ : newObjectWithHeuristics(instanceType, cptr, hasOwnership, typeName);
+}
+
+static PyObject *newObjectWithHeuristicsHelper(PyTypeObject *instanceType,
+ PyTypeObject *exactType,
+ void *cptr,
+ bool hasOwnership)
+{
+ // Try to find the exact type of cptr. For hierarchies with
+ // non-virtual destructors, typeid() will return the base name.
+ // Try type discovery in these cases.
+ if (exactType == nullptr || exactType == instanceType) {
+ auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType);
+ if (resolved.first != nullptr
+ && Shiboken::ObjectType::canDowncastTo(instanceType, resolved.first)) {
+ exactType = resolved.first;
+ cptr = resolved.second;
}
}
+ return newObjectForType(exactType != nullptr ? exactType : instanceType,
+ cptr, hasOwnership);
+}
+
+PyObject *newObjectForPointer(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership,
+ const char *typeName)
+{
+ // Try to find the exact type of cptr.
+ PyTypeObject *exactType = ObjectType::typeForTypeName(typeName);
+ // PYSIDE-868: In case of multiple inheritance, (for example,
+ // a function returning a QPaintDevice * from a QWidget *),
+ // use instance type to avoid pointer offset errors.
+ return exactType != nullptr && !Shiboken::ObjectType::canDowncastTo(instanceType, exactType)
+ ? newObjectForType(instanceType, cptr, hasOwnership)
+ : newObjectWithHeuristicsHelper(instanceType, exactType, cptr, hasOwnership);
+}
+
+
+PyObject *newObjectWithHeuristics(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership,
+ const char *typeName)
+{
+ return newObjectWithHeuristicsHelper(instanceType,
+ ObjectType::typeForTypeName(typeName),
+ cptr, hasOwnership);
+}
+
+PyObject *newObjectForType(PyTypeObject *instanceType, void *cptr, bool hasOwnership)
+{
bool shouldCreate = true;
bool shouldRegister = true;
SbkObject *self = nullptr;
diff --git a/sources/shiboken6/libshiboken/basewrapper.h b/sources/shiboken6/libshiboken/basewrapper.h
index 4835c4810..ec5545aea 100644
--- a/sources/shiboken6/libshiboken/basewrapper.h
+++ b/sources/shiboken6/libshiboken/basewrapper.h
@@ -265,6 +265,14 @@ LIBSHIBOKEN_API PyTypeObject *typeForTypeName(const char *typeName);
* \since 5.12
*/
LIBSHIBOKEN_API bool hasSpecialCastFunction(PyTypeObject *sbkType);
+
+/// Returns whether a C++ pointer of \p baseType can be safely downcast
+/// to \p targetType (base is a direct, single line base class of targetType).
+/// (is a direct, single-line inheritance)
+/// \param baseType Python type of base class
+/// \param targetType Python type of derived class
+/// \since 6.8
+LIBSHIBOKEN_API bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType);
}
namespace Object {
@@ -297,7 +305,8 @@ LIBSHIBOKEN_API SbkObject *findColocatedChild(SbkObject *wrapper,
const PyTypeObject *instanceType);
/**
- * Bind a C++ object to Python.
+ * Bind a C++ object to Python. Forwards to
+ * newObjectWithHeuristics(), newObjectForType() depending on \p isExactType.
* \param instanceType equivalent Python type for the C++ object.
* \param hasOwnership if true, Python will try to delete the underlying C++ object when there's no more refs.
* \param isExactType if false, Shiboken will use some heuristics to detect the correct Python type of this C++
@@ -311,6 +320,40 @@ LIBSHIBOKEN_API PyObject *newObject(PyTypeObject *instanceType,
bool isExactType = false,
const char *typeName = nullptr);
+/// Bind a C++ object to Python for polymorphic pointers. Calls
+/// newObjectWithHeuristics() with an additional check for multiple
+/// inheritance, in which case it will fall back to instanceType.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying C++ object
+/// when there's no more refs.
+/// \param typeName If non-null, this will be used as helper to find the correct
+/// Python type for this object (obtained by typeid().name().
+LIBSHIBOKEN_API PyObject *newObjectForPointer(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership = true,
+ const char *typeName = nullptr);
+
+/// Bind a C++ object to Python using some heuristics to detect the correct
+/// Python type of this C++ object. In any case \p instanceType must be provided;
+/// it'll be used as search starting point and as fallback.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying C++ object
+/// C++ object when there are no more references.
+/// when there's no more refs.
+/// \param typeName If non-null, this will be used as helper to find the correct
+/// Python type for this object (obtained by typeid().name().
+LIBSHIBOKEN_API PyObject *newObjectWithHeuristics(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership = true,
+ const char *typeName = nullptr);
+
+/// Bind a C++ object to Python using the given type.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying
+/// C++ object when there are no more references.
+LIBSHIBOKEN_API PyObject *newObjectForType(PyTypeObject *instanceType,
+ void *cptr, bool hasOwnership = true);
+
/**
* Changes the valid flag of a PyObject, invalid objects will raise an exception when someone tries to access it.
*/
diff --git a/sources/shiboken6/libshiboken/basewrapper_p.h b/sources/shiboken6/libshiboken/basewrapper_p.h
index 729c6a5e6..fb9140793 100644
--- a/sources/shiboken6/libshiboken/basewrapper_p.h
+++ b/sources/shiboken6/libshiboken/basewrapper_p.h
@@ -143,107 +143,7 @@ struct DestructorEntry
**/
std::vector<SbkObject *> splitPyObject(PyObject *pyObj);
-/**
-* Visitor class used by walkOnClassHierarchy function.
-*/
-class HierarchyVisitor
-{
-public:
- HierarchyVisitor(const HierarchyVisitor &) = delete;
- HierarchyVisitor(HierarchyVisitor &&) = delete;
- HierarchyVisitor &operator=(const HierarchyVisitor &) = delete;
- HierarchyVisitor &operator=(HierarchyVisitor &&) = delete;
-
- HierarchyVisitor();
- virtual ~HierarchyVisitor();
-
- virtual bool visit(PyTypeObject *node) = 0; // return true to terminate
-};
-
-class BaseCountVisitor : public HierarchyVisitor
-{
-public:
- bool visit(PyTypeObject *) override;
-
- int count() const { return m_count; }
-
-private:
- int m_count = 0;
-};
-
-class BaseAccumulatorVisitor : public HierarchyVisitor
-{
-public:
- using Result = std::vector<PyTypeObject *>;
-
- bool visit(PyTypeObject *node) override;
-
- Result bases() const { return m_bases; }
-
-private:
- Result m_bases;
-};
-
-class GetIndexVisitor : public HierarchyVisitor
-{
-public:
- explicit GetIndexVisitor(PyTypeObject *desiredType) : m_desiredType(desiredType) {}
-
- bool visit(PyTypeObject *node) override;
-
- int index() const { return m_index; }
-
-private:
- int m_index = -1;
- PyTypeObject *m_desiredType;
-};
-
-/// Collect destructors and C++ instances of each C++ object held by a Python
-/// object
-class DtorAccumulatorVisitor : public HierarchyVisitor
-{
-public:
- explicit DtorAccumulatorVisitor(SbkObject *pyObj) : m_pyObject(pyObj) {}
-
- bool visit(PyTypeObject *node) override;
-
- using DestructorEntries = std::vector<DestructorEntry>;
-
- const DestructorEntries &entries() const { return m_entries; }
-
-private:
- DestructorEntries m_entries;
- SbkObject *m_pyObject;
-};
-
-/// \internal Internal function used to walk on classes inheritance trees.
-/**
-* Walk on class hierarchy using a DFS algorithm.
-* For each pure Shiboken type found, HierarchyVisitor::visit is called and the algorithm
-* considers all children of this type as visited.
-*/
-bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor);
-
-inline int getTypeIndexOnHierarchy(PyTypeObject *baseType, PyTypeObject *desiredType)
-{
- GetIndexVisitor visitor(desiredType);
- walkThroughClassHierarchy(baseType, &visitor);
- return visitor.index();
-}
-
-inline int getNumberOfCppBaseClasses(PyTypeObject *baseType)
-{
- BaseCountVisitor visitor;
- walkThroughClassHierarchy(baseType, &visitor);
- return visitor.count();
-}
-
-inline std::vector<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType)
-{
- BaseAccumulatorVisitor visitor;
- walkThroughClassHierarchy(baseType, &visitor);
- return visitor.bases();
-}
+int getNumberOfCppBaseClasses(PyTypeObject *baseType);
namespace Object
{
diff --git a/sources/shiboken6/libshiboken/pep384impl.cpp b/sources/shiboken6/libshiboken/pep384impl.cpp
index 4b3759456..5310207a3 100644
--- a/sources/shiboken6/libshiboken/pep384impl.cpp
+++ b/sources/shiboken6/libshiboken/pep384impl.cpp
@@ -105,13 +105,13 @@ static PyType_Spec typeprobe_spec = {
static void
check_PyTypeObject_valid()
{
- auto *obtype = reinterpret_cast<PyObject *>(&PyType_Type);
- auto *probe_tp_base = reinterpret_cast<PyTypeObject *>(
- PyObject_GetAttr(obtype, Shiboken::PyMagicName::base()));
+ auto *typetype = &PyType_Type;
+ auto *obtype = reinterpret_cast<PyObject *>(typetype);
+ auto *probe_tp_base_obj = PyObject_GetAttr(obtype, Shiboken::PyMagicName::base());
+ auto *probe_tp_base = reinterpret_cast<PyTypeObject *>(probe_tp_base_obj);
auto *probe_tp_bases = PyObject_GetAttr(obtype, Shiboken::PyMagicName::bases());
- auto *check = reinterpret_cast<PyTypeObject *>(
- PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases));
- auto *typetype = reinterpret_cast<PyTypeObject *>(obtype);
+ auto *checkObj = PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases);
+ auto *check = reinterpret_cast<PyTypeObject *>(checkObj);
PyObject *w = PyObject_GetAttr(obtype, Shiboken::PyMagicName::weakrefoffset());
long probe_tp_weakrefoffset = PyLong_AsLong(w);
PyObject *d = PyObject_GetAttr(obtype, Shiboken::PyMagicName::dictoffset());
@@ -149,8 +149,8 @@ check_PyTypeObject_valid()
|| probe_tp_mro != typetype->tp_mro
|| Py_TPFLAGS_DEFAULT != (check->tp_flags & Py_TPFLAGS_DEFAULT))
Py_FatalError("The structure of type objects has changed!");
- Py_DECREF(check);
- Py_DECREF(probe_tp_base);
+ Py_DECREF(checkObj);
+ Py_DECREF(probe_tp_base_obj);
Py_DECREF(w);
Py_DECREF(d);
Py_DECREF(probe_tp_bases);
@@ -482,6 +482,47 @@ Pep_GetVerboseFlag()
}
#endif // Py_LIMITED_API
+// Support for pyerrors.h
+
+#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000
+// Emulate PyErr_GetRaisedException() using the deprecated PyErr_Fetch()/PyErr_Store()
+PyObject *PepErr_GetRaisedException()
+{
+ PyObject *type{};
+ PyObject *value{};
+ PyObject *traceback{};
+ PyErr_Fetch(&type, &value, &traceback);
+ Py_XINCREF(value);
+ PyErr_Restore(type, value, traceback);
+ return value;
+}
+
+struct PepException_HEAD
+{
+ PyObject_HEAD
+ PyObject *x1; // dict
+ PyObject *args;
+};
+
+// PyException_GetArgs/PyException_SetArgs were added to the stable API in 3.12
+PyObject *PepException_GetArgs(PyObject *ex)
+{
+ auto *h = reinterpret_cast<PepException_HEAD *>(ex);
+ Py_XINCREF(h->args);
+ return h->args;
+}
+
+LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args)
+{
+ auto *h = reinterpret_cast<PepException_HEAD *>(ex);
+ Py_XINCREF(args);
+ auto *old = h->args; // Py_XSETREF()
+ h->args = args;
+ Py_XDECREF(old);
+
+}
+#endif // Limited or < 3.12
+
/*****************************************************************************
*
* Support for code.h
@@ -722,11 +763,8 @@ PyTypeObject *PepStaticMethod_TypePtr = nullptr;
static PyTypeObject *
getStaticMethodType(void)
{
- // this works for Python 3, only
- // "StaticMethodType = type(str.__dict__['maketrans'])\n";
static const char prog[] =
- "from xxsubtype import spamlist\n"
- "result = type(spamlist.__dict__['staticmeth'])\n";
+ "result = type(str.__dict__['maketrans'])\n";
return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog));
}
@@ -1009,9 +1047,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 +1102,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/pep384impl.h b/sources/shiboken6/libshiboken/pep384impl.h
index ec58aac81..7188366e2 100644
--- a/sources/shiboken6/libshiboken/pep384impl.h
+++ b/sources/shiboken6/libshiboken/pep384impl.h
@@ -188,6 +188,17 @@ LIBSHIBOKEN_API int Pep_GetFlag(const char *name);
LIBSHIBOKEN_API int Pep_GetVerboseFlag(void);
#endif
+// pyerrors.h
+#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000
+LIBSHIBOKEN_API PyObject *PepErr_GetRaisedException();
+LIBSHIBOKEN_API PyObject *PepException_GetArgs(PyObject *ex);
+LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args);
+#else
+# define PepErr_GetRaisedException PyErr_GetRaisedException
+# define PepException_GetArgs PyException_GetArgs
+# define PepException_SetArgs PyException_SetArgs
+#endif
+
/*****************************************************************************
*
* RESOLVED: unicodeobject.h
diff --git a/sources/shiboken6/libshiboken/sbkcontainer.h b/sources/shiboken6/libshiboken/sbkcontainer.h
index 240c772a9..8ad5aadc6 100644
--- a/sources/shiboken6/libshiboken/sbkcontainer.h
+++ b/sources/shiboken6/libshiboken/sbkcontainer.h
@@ -74,10 +74,9 @@ public:
static PyObject *tpNewInvalid(PyTypeObject * /* subtype */, PyObject * /* args */, PyObject * /* kwds */)
{
- PyErr_Format(PyExc_NotImplementedError,
+ return PyErr_Format(PyExc_NotImplementedError,
"Opaque containers of type '%s' cannot be instantiated.",
typeid(SequenceContainer).name());
- return nullptr;
}
static int tpInit(PyObject * /* self */, PyObject * /* args */, PyObject * /* kwds */)
@@ -105,10 +104,8 @@ public:
static PyObject *sqGetItem(PyObject *self, Py_ssize_t i)
{
auto *d = get(self);
- if (i < 0 || i >= Py_ssize_t(d->m_list->size())) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return nullptr;
- }
+ if (i < 0 || i >= Py_ssize_t(d->m_list->size()))
+ return PyErr_Format(PyExc_IndexError, "index out of bounds");
auto it = std::cbegin(*d->m_list);
std::advance(it, i);
return ShibokenContainerValueConverter<value_type>::convertValueToPython(*it);
@@ -133,14 +130,10 @@ public:
static PyObject *push_back(PyObject *self, PyObject *pyArg)
{
auto *d = get(self);
- if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) {
- PyErr_SetString(PyExc_TypeError, "wrong type passed to append.");
- return nullptr;
- }
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg))
+ return PyErr_Format(PyExc_TypeError, "wrong type passed to append.");
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg);
if (!value.has_value())
@@ -152,14 +145,10 @@ public:
static PyObject *push_front(PyObject *self, PyObject *pyArg)
{
auto *d = get(self);
- if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) {
- PyErr_SetString(PyExc_TypeError, "wrong type passed to append.");
- return nullptr;
- }
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg))
+ return PyErr_Format(PyExc_TypeError, "wrong type passed to append.");
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg);
if (!value.has_value())
@@ -171,10 +160,8 @@ public:
static PyObject *clear(PyObject *self)
{
auto *d = get(self);
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
d->m_list->clear();
Py_RETURN_NONE;
@@ -183,10 +170,8 @@ public:
static PyObject *pop_back(PyObject *self)
{
auto *d = get(self);
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
d->m_list->pop_back();
Py_RETURN_NONE;
@@ -195,10 +180,8 @@ public:
static PyObject *pop_front(PyObject *self)
{
auto *d = get(self);
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
d->m_list->pop_front();
Py_RETURN_NONE;
@@ -208,21 +191,16 @@ public:
static PyObject *reserve(PyObject *self, PyObject *pyArg)
{
auto *d = get(self);
- if (PyLong_Check(pyArg) == 0) {
- PyErr_SetString(PyExc_TypeError, "wrong type passed to reserve().");
- return nullptr;
- }
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (PyLong_Check(pyArg) == 0)
+ return PyErr_Format(PyExc_TypeError, "wrong type passed to reserve().");
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
if constexpr (ShibokenContainerHasReserve<SequenceContainer>::value) {
const Py_ssize_t size = PyLong_AsSsize_t(pyArg);
d->m_list->reserve(size);
} else {
- PyErr_SetString(PyExc_TypeError, "Container does not support reserve().");
- return nullptr;
+ return PyErr_Format(PyExc_TypeError, "Container does not support reserve().");
}
Py_RETURN_NONE;
diff --git a/sources/shiboken6/libshiboken/sbkconverter.cpp b/sources/shiboken6/libshiboken/sbkconverter.cpp
index 358827aa8..9ab674415 100644
--- a/sources/shiboken6/libshiboken/sbkconverter.cpp
+++ b/sources/shiboken6/libshiboken/sbkconverter.cpp
@@ -12,7 +12,12 @@
#include "voidptr.h"
#include <string>
+#include <cstring>
+#include <iostream>
#include <unordered_map>
+#include <unordered_set>
+#include <map>
+#include <set>
static SbkConverter **PrimitiveTypeConverters;
@@ -72,6 +77,103 @@ void init()
initArrayConverters();
}
+static void dumpPyTypeObject(std::ostream &str, PyTypeObject *t)
+{
+ str << "\nPython type ";
+ if (t == nullptr) {
+ str << "<None>";
+ return;
+ }
+ str << '"' << t->tp_name << '"';
+ if (t->tp_base != nullptr && t->tp_base != &PyBaseObject_Type)
+ str << '(' << t->tp_base->tp_name << ')';
+}
+
+static void dumpSbkConverter(std::ostream &str, const SbkConverter *c)
+{
+ str << "SbkConverter " << static_cast<const void *>(c) << ": ";
+ if (c->pointerToPython != nullptr)
+ str << ", C++ pointer->Python";
+ if (c->copyToPython != nullptr)
+ str << ", copy->Python";
+ if (c->toCppPointerConversion.second != nullptr)
+ str << ", Python->C++ pointer";
+ if (!c->toCppConversions.empty())
+ str << ", " << c->toCppConversions.size() << " Python->C++ conversions";
+}
+
+// Less than operator for a PyTypeObject for dumping the converter map
+static bool pyTypeObjectLessThan(const PyTypeObject *t1, const PyTypeObject *t2)
+{
+ const bool isNull1 = t1 == nullptr;
+ const bool isNull2 = t2 == nullptr;
+ if (isNull1 || isNull2)
+ return isNull1 && !isNull2;
+ // Internal types (lower case) first
+ const bool isInternal1 = std::islower(t1->tp_name[0]);
+ const bool isInternal2 = std::islower(t2->tp_name[0]);
+ if (isInternal1 != isInternal2)
+ return !isInternal2;
+ return std::strcmp(t1->tp_name, t2->tp_name) < 0;
+}
+
+void dumpConverters()
+{
+ struct PyTypeObjectLess {
+
+ bool operator()(const PyTypeObject *t1, const PyTypeObject *t2) const {
+ return pyTypeObjectLessThan(t1, t2);
+ }
+ };
+
+ using StringSet = std::set<std::string>;
+ using SbkConverterNamesMap = std::unordered_map<SbkConverter *, StringSet>;
+ using PyTypeObjectConverterMap = std::map<PyTypeObject *, SbkConverterNamesMap,
+ PyTypeObjectLess>;
+
+ auto &str = std::cerr;
+
+ // Sort the entries by the associated PyTypeObjects and converters
+ PyTypeObjectConverterMap pyTypeObjectConverterMap;
+ for (const auto &converter : converters) {
+ auto *sbkConverter = converter.second;
+ if (sbkConverter == nullptr) {
+ str << "Non-existent: \"" << converter.first << "\"\n";
+ continue;
+ }
+ auto *typeObject = sbkConverter->pythonType;
+ auto typeIt = pyTypeObjectConverterMap.find(typeObject);
+ if (typeIt == pyTypeObjectConverterMap.end())
+ typeIt = pyTypeObjectConverterMap.insert(std::make_pair(typeObject,
+ SbkConverterNamesMap{})).first;
+ SbkConverterNamesMap &sbkConverterMap = typeIt->second;
+ auto convIt = sbkConverterMap.find(sbkConverter);
+ if (convIt == sbkConverterMap.end())
+ convIt = sbkConverterMap.insert(std::make_pair(sbkConverter,
+ StringSet{})).first;
+ convIt->second.insert(converter.first);
+ }
+
+ for (const auto &tc : pyTypeObjectConverterMap) {
+ dumpPyTypeObject(str, tc.first);
+ str << ", " << tc.second.size() << " converter(s):\n";
+ for (const auto &cn : tc.second) {
+ str << " ";
+ dumpSbkConverter(str, cn.first);
+ str << ", " << cn.second.size() << " alias(es):";
+ int i = 0;
+ for (const auto &name : cn.second) {
+ if ((i++ % 5) == 0)
+ str << "\n ";
+ str << " \"" << name << '"';
+ }
+ str << '\n';
+ }
+ }
+
+ str << '\n';
+}
+
SbkConverter *createConverterObject(PyTypeObject *type,
PythonToCppFunc toCppPointerConvFunc,
IsConvertibleToCppFunc toCppPointerCheckFunc,
@@ -422,18 +524,36 @@ void registerConverterName(SbkConverter *converter, const char *typeName)
converters.insert(std::make_pair(typeName, converter));
}
-static std::string getRealTypeName(const char *name)
+static std::string getRealTypeName(const std::string &typeName)
{
- std::string typeName(name);
auto size = typeName.size();
if (std::isalnum(typeName[size - 1]) == 0)
return typeName.substr(0, size - 1);
return typeName;
}
-SbkConverter *getConverter(const char *typeName)
+// PYSIDE-2404: Build a negative cache of already failed lookups.
+// The resulting list must be reset after each new import,
+// because that can change results. Also clear the cache after
+// reaching some threashold.
+static std::unordered_set<std::string> nonExistingTypeNames{};
+
+// Arbitrary size limit to prevent random name overflows.
+static constexpr std::size_t negativeCacheLimit = 50;
+
+static void rememberAsNonexistent(const std::string &typeName)
+{
+ if (nonExistingTypeNames.size() > negativeCacheLimit)
+ clearNegativeLazyCache();
+ converters.insert(std::make_pair(typeName, nullptr));
+ nonExistingTypeNames.insert(typeName);
+}
+
+SbkConverter *getConverter(const char *typeNameC)
{
+ std::string typeName = typeNameC;
auto it = converters.find(typeName);
+ // PYSIDE-2404: This can also contain explicit nullptr as a negative cache.
if (it != converters.end())
return it->second;
// PYSIDE-2404: Did not find the name. Load the lazy classes
@@ -442,6 +562,9 @@ SbkConverter *getConverter(const char *typeName)
it = converters.find(typeName);
if (it != converters.end())
return it->second;
+ // Cache the negative result. Don't forget to clear the cache for new modules.
+ rememberAsNonexistent(typeName);
+
if (Shiboken::pyVerbose() > 0) {
const std::string message =
std::string("Can't find type resolver for type '") + typeName + "'.";
@@ -450,6 +573,15 @@ SbkConverter *getConverter(const char *typeName)
return nullptr;
}
+void clearNegativeLazyCache()
+{
+ for (const auto &typeName : nonExistingTypeNames) {
+ auto it = converters.find(typeName);
+ converters.erase(it);
+ }
+ nonExistingTypeNames.clear();
+}
+
SbkConverter *primitiveTypeConverter(int index)
{
return PrimitiveTypeConverters[index];
@@ -704,14 +836,7 @@ PyTypeObject *getPythonTypeObject(const SbkConverter *converter)
PyTypeObject *getPythonTypeObject(const char *typeName)
{
- auto *type = getPythonTypeObject(getConverter(typeName));
- if (type == nullptr) {
- // PYSIDE-2404: Did not find the name. Load the lazy classes
- // which have this name and try again.
- Shiboken::Module::loadLazyClassesWithName(getRealTypeName(typeName).c_str());
- type = getPythonTypeObject(getConverter(typeName));
- }
- return type;
+ return getPythonTypeObject(getConverter(typeName));
}
bool pythonTypeIsValueType(const SbkConverter *converter)
diff --git a/sources/shiboken6/libshiboken/sbkconverter_p.h b/sources/shiboken6/libshiboken/sbkconverter_p.h
index c886c9b9f..08fc4c8e1 100644
--- a/sources/shiboken6/libshiboken/sbkconverter_p.h
+++ b/sources/shiboken6/libshiboken/sbkconverter_p.h
@@ -531,6 +531,12 @@ SbkConverter *createConverterObject(PyTypeObject *type,
IsConvertibleToCppFunc toCppPointerCheckFunc,
CppToPythonFunc pointerToPythonFunc,
CppToPythonFunc copyToPythonFunc);
+
+LIBSHIBOKEN_API void dumpConverters();
+
+/// Interface for sbkmodule which must reset cache when new module is loaded.
+LIBSHIBOKEN_API void clearNegativeLazyCache();
+
} // namespace Shiboken::Conversions
#endif // SBK_CONVERTER_P_H
diff --git a/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp b/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp
index 44e900f01..7637efa70 100644
--- a/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp
+++ b/sources/shiboken6/libshiboken/sbkcpptonumpy.cpp
@@ -49,17 +49,17 @@ PyObject *createByteArray1(Py_ssize_t, const uint8_t *)
PyObject *createDoubleArray1(Py_ssize_t, const double *)
{
- return Py_None;
+ Py_RETURN_NONE;
}
PyObject *createFloatArray1(Py_ssize_t, const float *)
{
- return Py_None;
+ Py_RETURN_NONE;
}
PyObject *createIntArray1(Py_ssize_t, const int *)
{
- return Py_None;
+ Py_RETURN_NONE;
}
#endif // !HAVE_NUMPY
diff --git a/sources/shiboken6/libshiboken/sbkerrors.cpp b/sources/shiboken6/libshiboken/sbkerrors.cpp
index 1832624d5..84c080f8d 100644
--- a/sources/shiboken6/libshiboken/sbkerrors.cpp
+++ b/sources/shiboken6/libshiboken/sbkerrors.cpp
@@ -6,6 +6,11 @@
#include "helper.h"
#include "gilstate.h"
+#include <cstdio>
+#include <string>
+
+using namespace std::literals::string_literals;
+
namespace Shiboken
{
@@ -93,6 +98,21 @@ void setWrongContainerType()
PyErr_SetString(PyExc_TypeError, "Wrong type passed to container conversion.");
}
+// Prepend something to an exception message provided it is a single string
+// argument.
+static bool prependToExceptionMessage(PyObject *exc, const char *context)
+{
+ Shiboken::AutoDecRef args(PepException_GetArgs(exc));
+ if (args.isNull() || PyTuple_Check(args.object()) == 0 || PyTuple_Size(args) != 1)
+ return false;
+ auto *oldMessage = PyTuple_GetItem(args, 0);
+ if (oldMessage == nullptr || PyUnicode_CheckExact(oldMessage) == 0)
+ return false;
+ auto *newMessage = PyUnicode_FromFormat("%s%U", context, oldMessage);
+ PepException_SetArgs(exc, PyTuple_Pack(1, newMessage));
+ return true;
+}
+
struct ErrorStore {
PyObject *type;
PyObject *exc;
@@ -101,17 +121,42 @@ struct ErrorStore {
static thread_local ErrorStore savedError{};
+static bool hasPythonContext()
+{
+ return _pythonContextStack & 1;
+}
+
void storeErrorOrPrint()
{
// This error happened in a function with no way to return an error state.
// Therefore, we handle the error when we are error checking, anyway.
// But we do that only when we know that an error handler can pick it up.
- if (_pythonContextStack & 1)
+ if (hasPythonContext())
PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback);
else
PyErr_Print();
}
+// Like storeErrorOrPrint() with additional context info that is prepended
+// to the exception message or printed.
+static void storeErrorOrPrintWithContext(const char *context)
+{
+ if (hasPythonContext()) {
+ PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback);
+ prependToExceptionMessage(savedError.exc, context);
+ } else {
+ std::fputs(context, stderr);
+ PyErr_Print();
+ }
+}
+
+void storePythonOverrideErrorOrPrint(const char *className, const char *funcName)
+{
+ const std::string context = "Error calling Python override of "s
+ + className + "::"s + funcName + "(): "s;
+ storeErrorOrPrintWithContext(context.c_str());
+}
+
PyObject *occurred()
{
if (savedError.type) {
diff --git a/sources/shiboken6/libshiboken/sbkerrors.h b/sources/shiboken6/libshiboken/sbkerrors.h
index 6ff85f8e1..18ce701e7 100644
--- a/sources/shiboken6/libshiboken/sbkerrors.h
+++ b/sources/shiboken6/libshiboken/sbkerrors.h
@@ -50,6 +50,11 @@ LIBSHIBOKEN_API void setWrongContainerType();
/// This replaces `PyErr_Print`, which cannot report errors as exception.
/// To be used in contexts where raising errors is impossible.
LIBSHIBOKEN_API void storeErrorOrPrint();
+
+/// Call storeErrorOrPrint() and print the context to report
+/// errors when calling Python overrides of virtual functions.
+LIBSHIBOKEN_API void storePythonOverrideErrorOrPrint(const char *className, const char *funcName);
+
/// Handle an error as in PyErr_Occurred(), but also check for errors which
/// were captured by `storeErrorOrPrint`.
/// To be used in normal error checks.
diff --git a/sources/shiboken6/libshiboken/sbkmodule.cpp b/sources/shiboken6/libshiboken/sbkmodule.cpp
index 4153df27f..76087fbb5 100644
--- a/sources/shiboken6/libshiboken/sbkmodule.cpp
+++ b/sources/shiboken6/libshiboken/sbkmodule.cpp
@@ -7,21 +7,27 @@
#include "bindingmanager.h"
#include "sbkstring.h"
#include "sbkcppstring.h"
+#include "sbkconverter_p.h"
#include <unordered_map>
#include <unordered_set>
+#include <vector>
#include <cstring>
+/// This hash maps module objects to arrays of converters.
+using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **> ;
+
/// This hash maps module objects to arrays of Python types.
using ModuleTypesMap = std::unordered_map<PyObject *, Shiboken::Module::TypeInitStruct *> ;
-/// This hash maps module objects to arrays of converters.
-using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **>;
+struct TypeCreationStruct
+{
+ Shiboken::Module::TypeCreationFunction func;
+ std::vector<std::string> subtypeNames;
+};
-/// This hash maps type names to type creation functions.
-using TypeCreationFunctionModulePair =
- std::pair<Shiboken::Module::TypeCreationFunction, PyObject *>;
-using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationFunctionModulePair>;
+/// This hash maps type names to type creation structs.
+using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationStruct> ;
/// This hash maps module objects to maps of names to functions.
using ModuleToFuncsMap = std::unordered_map<PyObject *, NameToTypeFunctionMap> ;
@@ -56,8 +62,8 @@ LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct)
AutoDecRef modName(String::fromCppStringView(names.substr(0, dotPos)));
auto *modOrType = PyDict_GetItem(sysModules, modName);
if (modOrType == nullptr) {
- PyErr_Format(PyExc_SystemError, "Module %s should already be in sys.modules",
- PyModule_GetName(modOrType));
+ PyErr_Format(PyExc_SystemError, "Module \"%U\" should already be in sys.modules",
+ modName.object());
return nullptr;
}
@@ -74,6 +80,39 @@ LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct)
return typeStruct.type;
}
+static void incarnateHelper(PyObject *module, const std::string_view names,
+ const NameToTypeFunctionMap &nameToFunc)
+{
+ auto dotPos = names.find('.');
+ std::string::size_type startPos = 0;
+ auto *modOrType{module};
+ while (dotPos != std::string::npos) {
+ auto typeName = names.substr(startPos, dotPos - startPos);
+ AutoDecRef obTypeName(String::fromCppStringView(typeName));
+ modOrType = PyObject_GetAttr(modOrType, obTypeName);
+ startPos = dotPos + 1;
+ dotPos = names.find('.', startPos);
+ }
+ // now we have the type to create.
+ auto funcIter = nameToFunc.find(std::string(names));
+ // - call this function that returns a PyTypeObject
+ auto tcStruct = funcIter->second;
+ auto initFunc = tcStruct.func;
+ PyTypeObject *type = initFunc(modOrType);
+ auto name = names.substr(startPos);
+ PyObject_SetAttrString(modOrType, name.data(), reinterpret_cast<PyObject *>(type));
+}
+
+static void incarnateSubtypes(PyObject *module,
+ const std::vector<std::string> &nameList,
+ NameToTypeFunctionMap &nameToFunc)
+{
+ for (auto const & tableIter : nameList) {
+ std::string_view names(tableIter);
+ incarnateHelper(module, names, nameToFunc);
+ }
+}
+
static PyTypeObject *incarnateType(PyObject *module, const char *name,
NameToTypeFunctionMap &nameToFunc)
{
@@ -85,13 +124,15 @@ static PyTypeObject *incarnateType(PyObject *module, const char *name,
return nullptr;
}
// - call this function that returns a PyTypeObject
- auto pair = funcIter->second;
- auto initFunc = pair.first;
- auto *modOrType = pair.second;
+ auto tcStruct = funcIter->second;
+ auto initFunc = tcStruct.func;
+ auto *modOrType{module};
// PYSIDE-2404: Make sure that no switching happens during type creation.
auto saveFeature = initSelectableFeature(nullptr);
PyTypeObject *type = initFunc(modOrType);
+ if (!tcStruct.subtypeNames.empty())
+ incarnateSubtypes(module, tcStruct.subtypeNames, nameToFunc);
initSelectableFeature(saveFeature);
// - assign this object to the name in the module
@@ -164,7 +205,7 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name)
// - locate the name and retrieve the generating function
const char *attrNameStr = Shiboken::String::toCString(name);
auto &nameToFunc = tableIter->second;
- // - create the real type (incarnateType checks this)
+ // - create the real type and handle subtypes
auto *type = incarnateType(module, attrNameStr, nameToFunc);
auto *ret = reinterpret_cast<PyObject *>(type);
// - if attribute does really not exist use the original
@@ -172,7 +213,6 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name)
PyErr_Clear();
return origModuleGetattro(module, name);
}
-
return ret;
}
@@ -292,25 +332,12 @@ static bool shouldLazyLoad(PyObject *module)
return std::strncmp(modName, "PySide6.", 8) == 0;
}
-void AddTypeCreationFunction(PyObject *module,
- const char *name,
- TypeCreationFunction func)
+void checkIfShouldLoadImmediately(PyObject *module, const std::string &name,
+ const NameToTypeFunctionMap &nameToFunc)
{
static const char *flag = getenv("PYSIDE6_OPTION_LAZY");
static const int value = flag != nullptr ? std::atoi(flag) : 1;
- // - locate the module in the moduleTofuncs mapping
- auto tableIter = moduleToFuncs.find(module);
- assert(tableIter != moduleToFuncs.end());
- // - Assign the name/generating function pair.
- auto &nameToFunc = tableIter->second;
- TypeCreationFunctionModulePair pair{func, module};
- auto nit = nameToFunc.find(name);
- if (nit == nameToFunc.end())
- nameToFunc.insert(std::make_pair(name, pair));
- else
- nit->second = pair;
-
// PYSIDE-2404: Lazy Loading
//
// Options:
@@ -319,56 +346,56 @@ void AddTypeCreationFunction(PyObject *module,
// 3 - lazy loading for any module.
//
// By default we lazy load all known modules (option = 1).
-
if (value == 0 // completely disabled
|| canNotLazyLoad(module) // for some reason we cannot lazy load
|| (value == 1 && !shouldLazyLoad(module)) // not a known module
) {
- PyTypeObject *type = func(module);
- PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(type)); // steals reference
+ incarnateHelper(module, name, nameToFunc);
}
}
void AddTypeCreationFunction(PyObject *module,
const char *name,
- TypeCreationFunction func,
- const char *containerName)
+ TypeCreationFunction func)
{
- // This version could be delayed as well, but for the few cases
- // we simply fetch the container type and insert directly.
- AutoDecRef obContainerType(PyObject_GetAttrString(module, containerName));
- PyTypeObject *type = func(obContainerType);
- PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
-}
+ // - locate the module in the moduleTofuncs mapping
+ auto tableIter = moduleToFuncs.find(module);
+ assert(tableIter != moduleToFuncs.end());
+ // - Assign the name/generating function tcStruct.
+ auto &nameToFunc = tableIter->second;
+ TypeCreationStruct tcStruct{func, {}};
+ auto nit = nameToFunc.find(name);
+ if (nit == nameToFunc.end())
+ nameToFunc.insert(std::make_pair(name, tcStruct));
+ else
+ nit->second = tcStruct;
-void AddTypeCreationFunction(PyObject *module,
- const char *name,
- TypeCreationFunction func,
- const char *outerContainerName,
- const char *innerContainerName)
-{
- // This version has even more indirection. It is very rare, and
- // we handle it directly.
- AutoDecRef obOuterType(PyObject_GetAttrString(module, outerContainerName));
- AutoDecRef obInnerType(PyObject_GetAttrString(obOuterType, innerContainerName));
- PyTypeObject *type = func(obInnerType);
- PyObject_SetAttrString(obInnerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
+ checkIfShouldLoadImmediately(module, name, nameToFunc);
}
void AddTypeCreationFunction(PyObject *module,
- const char *name,
+ const char *containerName,
TypeCreationFunction func,
- const char *containerName3,
- const char *containerName2,
- const char *containerName)
+ const char *namePath)
{
- // This version has even mode indirection. It is very rare, and
- // we handle it directly.
- AutoDecRef obContainerType3(PyObject_GetAttrString(module, containerName3));
- AutoDecRef obContainerType2(PyObject_GetAttrString(obContainerType3, containerName2));
- AutoDecRef obContainerType(PyObject_GetAttrString(obContainerType2, containerName));
- PyTypeObject *type = func(obContainerType);
- PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
+ // - locate the module in the moduleTofuncs mapping
+ auto tableIter = moduleToFuncs.find(module);
+ assert(tableIter != moduleToFuncs.end());
+ // - Assign the name/generating function tcStruct.
+ auto &nameToFunc = tableIter->second;
+ auto nit = nameToFunc.find(containerName);
+
+ // - insert namePath into the subtype vector of the main type.
+ nit->second.subtypeNames.push_back(namePath);
+ // - insert it also as its own entry.
+ nit = nameToFunc.find(namePath);
+ TypeCreationStruct tcStruct{func, {}};
+ if (nit == nameToFunc.end())
+ nameToFunc.insert(std::make_pair(namePath, tcStruct));
+ else
+ nit->second = tcStruct;
+
+ checkIfShouldLoadImmediately(module, namePath, nameToFunc);
}
PyObject *import(const char *moduleName)
@@ -455,6 +482,8 @@ PyObject *create(const char * /* modName */, void *moduleData)
// into `sys.modules`. This can cause a race condition.
// Insert the module early into the module dict to prevend recursion.
PyDict_SetItemString(sysModules, PyModule_GetName(module), module);
+ // Clear the non-existing name cache because we have a new module.
+ Shiboken::Conversions::clearNegativeLazyCache();
return module;
}
diff --git a/sources/shiboken6/libshiboken/sbkmodule.h b/sources/shiboken6/libshiboken/sbkmodule.h
index 1b3de33b7..2c407e09d 100644
--- a/sources/shiboken6/libshiboken/sbkmodule.h
+++ b/sources/shiboken6/libshiboken/sbkmodule.h
@@ -56,18 +56,6 @@ LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
TypeCreationFunction func,
const char *containerName);
-LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
- const char *name,
- TypeCreationFunction func,
- const char *outerContainerName,
- const char *innerContainerName);
-
-LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
- const char *name,
- TypeCreationFunction func,
- const char *containerName3,
- const char *containerName2,
- const char *containerName);
/**
* Registers the list of types created by \p module.
* \param module Module where the types were created.
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
diff --git a/sources/shiboken6/libshiboken/sbkstring.cpp b/sources/shiboken6/libshiboken/sbkstring.cpp
index 1471cd7fe..b5e87ca5a 100644
--- a/sources/shiboken6/libshiboken/sbkstring.cpp
+++ b/sources/shiboken6/libshiboken/sbkstring.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "sbkstring.h"
+#include "sbkenum.h"
#include "sbkstaticstrings_p.h"
#include "autodecref.h"
@@ -14,6 +15,11 @@ bool checkIterable(PyObject *obj)
return PyObject_HasAttr(obj, Shiboken::PyMagicName::iter());
}
+bool checkIterableArgument(PyObject *obj)
+{
+ return checkIterable(obj) && !Shiboken::Enum::check(obj);
+}
+
static PyObject *initPathLike()
{
PyObject *PathLike{};
diff --git a/sources/shiboken6/libshiboken/sbkstring.h b/sources/shiboken6/libshiboken/sbkstring.h
index f91847c11..ebc5428c7 100644
--- a/sources/shiboken6/libshiboken/sbkstring.h
+++ b/sources/shiboken6/libshiboken/sbkstring.h
@@ -13,6 +13,8 @@ namespace String
{
LIBSHIBOKEN_API bool check(PyObject *obj);
LIBSHIBOKEN_API bool checkIterable(PyObject *obj);
+ /// Check for iterable function arguments (excluding enumerations)
+ LIBSHIBOKEN_API bool checkIterableArgument(PyObject *obj);
LIBSHIBOKEN_API bool checkPath(PyObject *path);
LIBSHIBOKEN_API bool checkType(PyTypeObject *obj);
LIBSHIBOKEN_API bool checkChar(PyObject *obj);
diff --git a/sources/shiboken6/libshiboken/voidptr.cpp b/sources/shiboken6/libshiboken/voidptr.cpp
index 7045b08b1..8bb3f6ac8 100644
--- a/sources/shiboken6/libshiboken/voidptr.cpp
+++ b/sources/shiboken6/libshiboken/voidptr.cpp
@@ -156,10 +156,9 @@ PyObject *SbkVoidPtrObject_int(PyObject *v)
PyObject *toBytes(PyObject *self, PyObject * /* args */)
{
auto *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(self);
- if (sbkObject->size < 0) {
- PyErr_SetString(PyExc_IndexError, "VoidPtr does not have a size set.");
- return nullptr;
- }
+ if (sbkObject->size < 0)
+ return PyErr_Format(PyExc_IndexError, "VoidPtr does not have a size set.");
+
PyObject *bytes = PyBytes_FromStringAndSize(reinterpret_cast<const char *>(sbkObject->cptr),
sbkObject->size);
Py_XINCREF(bytes);
diff --git a/sources/shiboken6/shibokenmodule/shibokenmodule.cpp b/sources/shiboken6/shibokenmodule/shibokenmodule.cpp
index b3adfe78b..5c6219885 100644
--- a/sources/shiboken6/shibokenmodule/shibokenmodule.cpp
+++ b/sources/shiboken6/shibokenmodule/shibokenmodule.cpp
@@ -100,6 +100,10 @@ const bool ok = Shiboken::BindingManager::instance().dumpTypeGraph(%1);
Shiboken::BindingManager::instance().dumpWrapperMap();
// @snippet dumpwrappermap
+// @snippet dumpconverters
+Shiboken::Conversions::dumpConverters();
+// @snippet dumpconverters
+
// @snippet init
// Add __version__ and __version_info__ attributes to the module
PyObject* version = PyTuple_New(5);
diff --git a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml
index aa08a8bbf..acb522ecc 100644
--- a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml
+++ b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml
@@ -57,9 +57,14 @@
<inject-code file="shibokenmodule.cpp" snippet="dumpwrappermap"/>
</add-function>
+ <add-function signature="dumpConverters()">
+ <inject-code file="shibokenmodule.cpp" snippet="dumpconverters"/>
+ </add-function>
+
<extra-includes>
<include file-name="sbkversion.h" location="local"/>
<include file-name="voidptr.h" location="local"/>
+ <include file-name="sbkconverter_p.h" location="local"/>
</extra-includes>
<inject-code position="end" file="shibokenmodule.cpp" snippet="init"/>
</typesystem>
diff --git a/sources/shiboken6/tests/libother/othermultiplederived.h b/sources/shiboken6/tests/libother/othermultiplederived.h
index a8e265388..cd9910687 100644
--- a/sources/shiboken6/tests/libother/othermultiplederived.h
+++ b/sources/shiboken6/tests/libother/othermultiplederived.h
@@ -10,7 +10,7 @@
class ObjectType;
-class LIBOTHER_API OtherMultipleDerived : public MDerived1
+class LIBOTHER_API OtherMultipleDerived : public OtherBase, public MDerived1
{
public:
// this will use CppCopier from other module (bug#142)
diff --git a/sources/shiboken6/tests/libsample/derived.h b/sources/shiboken6/tests/libsample/derived.h
index b7736c37a..cf95cb601 100644
--- a/sources/shiboken6/tests/libsample/derived.h
+++ b/sources/shiboken6/tests/libsample/derived.h
@@ -26,7 +26,7 @@ public:
public:
void uselessMethod() {}
SomeInnerClass operator+(const SomeInnerClass &other) { return other; }
- bool operator==(const SomeInnerClass &) { return true; }
+ bool operator==(const SomeInnerClass &) const { return true; }
};
explicit Derived(int id = -1) noexcept;
diff --git a/sources/shiboken6/tests/libsample/point.cpp b/sources/shiboken6/tests/libsample/point.cpp
index b8630eb1e..0a28e877f 100644
--- a/sources/shiboken6/tests/libsample/point.cpp
+++ b/sources/shiboken6/tests/libsample/point.cpp
@@ -34,7 +34,7 @@ void Point::show() const
std::cout << "(x: " << m_x << ", y: " << m_y << ")";
}
-bool Point::operator==(const Point &other)
+bool Point::operator==(const Point &other) const
{
return m_x == other.m_x && m_y == other.m_y;
}
diff --git a/sources/shiboken6/tests/libsample/point.h b/sources/shiboken6/tests/libsample/point.h
index 59e0236d5..7e5d128ab 100644
--- a/sources/shiboken6/tests/libsample/point.h
+++ b/sources/shiboken6/tests/libsample/point.h
@@ -38,7 +38,7 @@ public:
// The != operator is not implemented for the purpose of testing
// for the absense of the __ne__ method in the Python binding.
- bool operator==(const Point &other);
+ bool operator==(const Point &other) const;
Point operator+(const Point &other);
Point operator-(const Point &other);
diff --git a/sources/shiboken6/tests/libsample/pointf.cpp b/sources/shiboken6/tests/libsample/pointf.cpp
index 6b39f73a9..736a5c6b5 100644
--- a/sources/shiboken6/tests/libsample/pointf.cpp
+++ b/sources/shiboken6/tests/libsample/pointf.cpp
@@ -26,7 +26,7 @@ void PointF::show() const
std::cout << "(x: " << m_x << ", y: " << m_y << ")";
}
-bool PointF::operator==(const PointF &other)
+bool PointF::operator==(const PointF &other) const
{
return m_x == other.m_x && m_y == other.m_y;
}
diff --git a/sources/shiboken6/tests/libsample/pointf.h b/sources/shiboken6/tests/libsample/pointf.h
index bb50b5c6d..49e009467 100644
--- a/sources/shiboken6/tests/libsample/pointf.h
+++ b/sources/shiboken6/tests/libsample/pointf.h
@@ -31,7 +31,7 @@ public:
// The != operator is not implemented for the purpose of testing
// for the absence of the __ne__ method in the Python binding.
- bool operator==(const PointF &other);
+ bool operator==(const PointF &other) const;
PointF operator+(const PointF &other);
PointF operator-(const PointF &other);
diff --git a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp
index a7b73cc81..1be93db66 100644
--- a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp
+++ b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp
@@ -51,6 +51,26 @@ void StdSharedPtrTestBench::printInt(const std::shared_ptr<int> &p)
std::cerr << '\n';
}
+std::shared_ptr<std::string> StdSharedPtrTestBench::createString(const char *text)
+{
+ return std::make_shared<std::string>(text);
+}
+
+std::shared_ptr<std::string> StdSharedPtrTestBench::createNullString()
+{
+ return {};
+}
+
+void StdSharedPtrTestBench::printString(const std::shared_ptr<std::string> &p)
+{
+ std::cerr << __FUNCTION__ << ' ';
+ if (p.get())
+ std::cerr << '"' << *p << '"';
+ else
+ std::cerr << "nullptr";
+ std::cerr << '\n';
+}
+
StdSharedPtrVirtualMethodTester::StdSharedPtrVirtualMethodTester() = default;
StdSharedPtrVirtualMethodTester::~StdSharedPtrVirtualMethodTester() = default;
diff --git a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h
index 8991cded6..04d75d5ef 100644
--- a/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h
+++ b/sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h
@@ -7,6 +7,7 @@
#include "libsmartmacros.h"
#include <memory>
+#include <string>
class Integer;
@@ -23,6 +24,10 @@ public:
static std::shared_ptr<int> createInt(int v = 42);
static std::shared_ptr<int> createNullInt();
static void printInt(const std::shared_ptr<int> &);
+
+ static std::shared_ptr<std::string> createString(const char *text);
+ static std::shared_ptr<std::string> createNullString();
+ static void printString(const std::shared_ptr<std::string> &);
};
class LIB_SMART_API StdSharedPtrVirtualMethodTester
diff --git a/sources/shiboken6/tests/otherbinding/typediscovery_test.py b/sources/shiboken6/tests/otherbinding/typediscovery_test.py
index 791d3bdce..39dc5cf0f 100644
--- a/sources/shiboken6/tests/otherbinding/typediscovery_test.py
+++ b/sources/shiboken6/tests/otherbinding/typediscovery_test.py
@@ -13,7 +13,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from shiboken_paths import init_paths
init_paths()
-from sample import Abstract, Base1, Derived
+from sample import (Abstract, Base1, Derived,
+ MDerived1, SonOfMDerived1, MDerived3)
from other import OtherMultipleDerived
@@ -32,14 +33,18 @@ class TypeDiscoveryTest(unittest.TestCase):
def testMultipleInheritance(self):
obj = OtherMultipleDerived.createObject("Base1")
self.assertEqual(type(obj), Base1)
- # PYSIDE-868: In case of multiple inheritance, a factory
- # function will return the base class wrapper.
+ # PYSIDE-868: In case of single line direct inheritance,
+ # a factory function will return the class wrapper
+ # of the derived class.
obj = OtherMultipleDerived.createObject("MDerived1")
- self.assertEqual(type(obj), Base1)
+ self.assertEqual(type(obj), MDerived1)
obj = OtherMultipleDerived.createObject("SonOfMDerived1")
- self.assertEqual(type(obj), Base1)
+ self.assertEqual(type(obj), SonOfMDerived1)
obj = OtherMultipleDerived.createObject("MDerived3")
- self.assertEqual(type(obj), Base1)
+ self.assertEqual(type(obj), MDerived3)
+ # PYSIDE-868: OtherMultipleDerived inherits
+ # OtherBase, Base1. In this case, a factory
+ # function will return the base class wrapper.
obj = OtherMultipleDerived.createObject("OtherMultipleDerived")
self.assertEqual(type(obj), Base1)
diff --git a/sources/shiboken6/tests/smartbinding/CMakeLists.txt b/sources/shiboken6/tests/smartbinding/CMakeLists.txt
index 2e729321e..c10d7ab6b 100644
--- a/sources/shiboken6/tests/smartbinding/CMakeLists.txt
+++ b/sources/shiboken6/tests/smartbinding/CMakeLists.txt
@@ -20,6 +20,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrtestbench_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrvirtualmethodtester_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_integer_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_int_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_std_string_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_optional_int_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_optional_integer_wrapper.cpp
diff --git a/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py b/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py
index 2e6aea3d9..c87a6f3e2 100644
--- a/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py
+++ b/sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py
@@ -50,6 +50,13 @@ class StdSharedPtrTests(unittest.TestCase):
p = StdSharedPtrTestBench.createInt()
StdSharedPtrTestBench.printInt(p)
+ def testString(self):
+ np = StdSharedPtrTestBench.createNullString()
+ StdSharedPtrTestBench.printString(np)
+ self.assertFalse(np)
+ p = StdSharedPtrTestBench.createString("bla")
+ StdSharedPtrTestBench.printString(p)
+
def testVirtuals(self):
"""Test whether code generating virtual function overrides is generated
correctly."""
diff --git a/sources/shiboken6/tests/smartbinding/typesystem_smart.xml b/sources/shiboken6/tests/smartbinding/typesystem_smart.xml
index 261d5f15d..8831629f8 100644
--- a/sources/shiboken6/tests/smartbinding/typesystem_smart.xml
+++ b/sources/shiboken6/tests/smartbinding/typesystem_smart.xml
@@ -50,7 +50,7 @@
value-check-method="operator bool"
ref-count-method="use_count"
reset-method="reset"
- instantiations="Integer,int">
+ instantiations="Integer,int,std::string">
<include file-name="memory" location="global"/>
</smart-pointer-type>
diff --git a/tools/cross_compile_android/main.py b/tools/cross_compile_android/main.py
index bda438cca..b68fd5031 100644
--- a/tools/cross_compile_android/main.py
+++ b/tools/cross_compile_android/main.py
@@ -86,7 +86,8 @@ if __name__ == "__main__":
parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
dest="loglevel", const=logging.INFO)
- parser.add_argument("--api-level", type=str, default="33", help="Android API level to use")
+ parser.add_argument("--api-level", type=str, default="26",
+ help="Minimum Android API level to use")
parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred r25c)")
# sdk path is needed to compile all the Qt Java Acitivity files into Qt6AndroidBindings.jar
parser.add_argument("--sdk-path", type=str, help="Path to Android SDK")
@@ -184,8 +185,8 @@ if __name__ == "__main__":
platform_data = PlatformData("x86_64", api_level, "x86_64", "x86_64", "x86-64", "64")
# python path is valid, if Python for android installation exists in python_path
- python_path = (pyside6_deploy_cache / f"Python-{platform_data.plat_name}-linux-android"
- / "_install")
+ python_path = (pyside6_deploy_cache
+ / f"Python-{platform_data.plat_name}-linux-android" / "_install")
valid_python_path = python_path.exists()
if Path(python_path).exists():
expected_dirs = ["lib", "include"]
@@ -214,6 +215,10 @@ if __name__ == "__main__":
)
if not python_ccompile_script.exists():
+ host_system_config_name = run_command("./config.guess", cwd=cpython_dir,
+ dry_run=dry_run, show_stdout=True,
+ capture_stdout=True).strip()
+
# use jinja2 to create cross_compile.sh script
template = environment.get_template("cross_compile.tmpl.sh")
content = template.render(
@@ -221,7 +226,10 @@ if __name__ == "__main__":
ndk_path=ndk_path,
api_level=platform_data.api_level,
android_py_install_path_prefix=pyside6_deploy_cache,
- host_python_path=sys.executable
+ host_python_path=sys.executable,
+ python_version=PYTHON_VERSION,
+ host_system_name=host_system_config_name,
+ host_platform_name=sys.platform
)
logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
@@ -240,13 +248,6 @@ if __name__ == "__main__":
run_command([f"./{python_ccompile_script.name}"], cwd=cpython_dir, dry_run=dry_run,
show_stdout=True)
- # run patchelf to change the SONAME of libpython from libpython3.x.so.1.0 to
- # libpython3.x.so, to match with python_for_android's Python library. Otherwise,
- # the Qfp binaries won't be able to link to Python
- run_command(["patchelf", "--set-soname", f"libpython{PYTHON_VERSION}.so",
- f"libpython{PYTHON_VERSION}.so.1.0"], cwd=Path(python_path) / "lib",
- dry_run=dry_run)
-
logging.info(
f"Cross compile Python for Android platform {platform_data.plat_name}. "
f"Final installation in {python_path}"
@@ -286,15 +287,22 @@ if __name__ == "__main__":
# give run permission to cross compile script
qfp_toolchain.chmod(qfp_toolchain.stat().st_mode | stat.S_IEXEC)
+ if sys.platform == "linux":
+ host_qt_install_suffix = "gcc_64"
+ elif sys.platform == "darwin":
+ host_qt_install_suffix = "macos"
+ else:
+ raise RuntimeError("Qt for Python cross compilation not supported on this platform")
+
# run the cross compile script
logging.info(f"Running Qt for Python cross-compile for platform {platform_data.plat_name}")
qfp_ccompile_cmd = [sys.executable, "setup.py", "bdist_wheel", "--parallel=9",
"--standalone",
f"--cmake-toolchain-file={str(qfp_toolchain.resolve())}",
- f"--qt-host-path={qt_install_path}/gcc_64",
+ f"--qt-host-path={qt_install_path}/{host_qt_install_suffix}",
f"--plat-name=android_{platform_data.plat_name}",
f"--python-target-path={python_path}",
(f"--qt-target-path={qt_install_path}/"
- f"android_{platform_data.qt_plat_name}"),
+ f"android_{platform_data.qt_plat_name}"),
"--no-qt-tools"]
run_command(qfp_ccompile_cmd, cwd=pyside_setup_dir, dry_run=dry_run, show_stdout=True)
diff --git a/tools/cross_compile_android/templates/cross_compile.tmpl.sh b/tools/cross_compile_android/templates/cross_compile.tmpl.sh
index a68907591..784e822ca 100644
--- a/tools/cross_compile_android/templates/cross_compile.tmpl.sh
+++ b/tools/cross_compile_android/templates/cross_compile.tmpl.sh
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
set -x -e
export HOST_ARCH={{ plat_name }}-linux-android
-export TOOLCHAIN={{ ndk_path }}/toolchains/llvm/prebuilt/linux-x86_64/bin
+export TOOLCHAIN={{ ndk_path }}/toolchains/llvm/prebuilt/{{ host_platform_name }}-x86_64/bin
export TOOL_PREFIX=$TOOLCHAIN/$HOST_ARCH
export PLATFORM_API={{ api_level }}
{% if plat_name == "armv7a" -%}
@@ -20,10 +20,10 @@ export RANLIB=$TOOLCHAIN/llvm-ranlib
export LD=$TOOLCHAIN/ld
export READELF=$TOOLCHAIN/llvm-readelf
export CFLAGS='-fPIC -DANDROID'
-./configure --host=$HOST_ARCH --target=$HOST_ARCH --build=x86_64-pc-linux-gnu \
+./configure --host=$HOST_ARCH --target=$HOST_ARCH --build={{ host_system_name }} \
--with-build-python={{ host_python_path }} --enable-shared \
--enable-ipv6 ac_cv_file__dev_ptmx=yes ac_cv_file__dev_ptc=no --without-ensurepip \
ac_cv_little_endian_double=yes
-make BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- CROSS_COMPILE_TARGET=yes
-make install BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- \
-CROSS_COMPILE_TARGET=yes prefix={{ android_py_install_path_prefix }}/Python-$HOST_ARCH/_install
+make BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- CROSS_COMPILE_TARGET=yes \
+INSTSONAME=libpython{{ python_version }}.so
+make install prefix={{ android_py_install_path_prefix }}/Python-$HOST_ARCH/_install
diff --git a/tools/snippets_translate/module_classes.py b/tools/snippets_translate/module_classes.py
index 4992e170b..df4c7557c 100644
--- a/tools/snippets_translate/module_classes.py
+++ b/tools/snippets_translate/module_classes.py
@@ -532,6 +532,7 @@ module_classes = {
"QAccessibleEvent",
"QAccessibleInterface",
"QAccessibleObject",
+ "QAccessibleSelectionInterface",
"QAccessibleStateChangeEvent",
"QAccessibleTableCellInterface",
"QAccessibleTableModelChangeEvent",