diff options
author | Christian Tismer <tismer@stackless.com> | 2021-07-20 10:55:10 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2021-07-28 19:55:36 +0000 |
commit | 3833b0aacc84924bd41cb03ab11cab0f18cd0dc9 (patch) | |
tree | 0fa4e9f17013d5777f6e271da9c72c6a8feb47a9 | |
parent | 49cd96eba9d0dbe0334b9e2f1f1fa4fa3499c033 (diff) |
feature: fix the UIC switching problem
The BindingManager::getOverride function computes the current
switch state from information of a type object. But the type object
must first be updated in case a switch has happened.
The solution was an extra update call at the beginning of the
function.
This solution _always_ works, with or without inheritance, for
Python >= 3.7.
[ChangeLog][shiboken6] Coexistence of different feature
selections works now, especially for UIC files and inheritance.
Fixes: PYSIDE-1626
Change-Id: I577331cfb2d7511110d1e16e729bed80985340a0
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
(cherry picked from commit 9b5fa60d1fed5025e97c393ba1bab80f81ba833a)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | build_history/blacklist.txt | 3 | ||||
-rw-r--r-- | sources/pyside6/doc/feature-why.rst | 9 | ||||
-rw-r--r-- | sources/pyside6/libpyside/feature_select.cpp | 3 | ||||
-rw-r--r-- | sources/pyside6/tests/QtCore/CMakeLists.txt | 1 | ||||
-rw-r--r-- | sources/pyside6/tests/QtCore/feature_with_uic/window.py | 90 | ||||
-rw-r--r-- | sources/pyside6/tests/QtCore/feature_with_uic/window.ui | 62 | ||||
-rw-r--r-- | sources/pyside6/tests/QtCore/feature_with_uic_test.py | 88 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/basewrapper.cpp | 7 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/basewrapper.h | 7 | ||||
-rw-r--r-- | sources/shiboken6/libshiboken/bindingmanager.cpp | 3 |
10 files changed, 271 insertions, 2 deletions
diff --git a/build_history/blacklist.txt b/build_history/blacklist.txt index 7786fe989..e8c17cde9 100644 --- a/build_history/blacklist.txt +++ b/build_history/blacklist.txt @@ -47,6 +47,9 @@ darwin ci [QtWidgets::qpicture_test] darwin ci +# Incomplehensible effect with feature switching on 3.6, qApp.process_events() +[QtCore::feature_with_uic_test] + py3.6 # Unsolved Refcounting leaks in debug mode [pysidetest::property_python_test] debug diff --git a/sources/pyside6/doc/feature-why.rst b/sources/pyside6/doc/feature-why.rst index b95cb77ac..a2f3e146b 100644 --- a/sources/pyside6/doc/feature-why.rst +++ b/sources/pyside6/doc/feature-why.rst @@ -256,6 +256,15 @@ When using ``__feature__`` often with common IDEs, you may want to provide a feature-aware version of ``.pyi`` files to get a correct display. The simplest way to change them all in-place is the command + +Using __feature__ with UIC files +-------------------------------- + +Features can be freely used together with generated UIC files. The UIC files +are _not_ converted, intentionally. Mixing them with feature selections in other +Python modules should always work, because switching will happen as needed, selected +by the currently active module. (Please report to us if this fails for an example) + .. code-block:: python pyside6-genpyi all --feature snake_case true_property diff --git a/sources/pyside6/libpyside/feature_select.cpp b/sources/pyside6/libpyside/feature_select.cpp index 7d95034f0..45af38bd7 100644 --- a/sources/pyside6/libpyside/feature_select.cpp +++ b/sources/pyside6/libpyside/feature_select.cpp @@ -253,6 +253,7 @@ static bool replaceClassDict(PyTypeObject *type) // Replace `__dict__` which usually has refcount 1 (but see cyclic_test.py) Py_DECREF(type->tp_dict); type->tp_dict = new_dict; + setCurrentSelectId(type, select_id.object()); return true; } @@ -273,6 +274,7 @@ static bool addNewDict(PyTypeObject *type, PyObject *select_id) setNextDict(dict, new_dict); setNextDict(new_dict, next_dict); type->tp_dict = new_dict; + setCurrentSelectId(type, select_id); return true; } @@ -295,6 +297,7 @@ static bool moveToFeatureSet(PyTypeObject *type, PyObject *select_id) } } while (dict != initial_dict); type->tp_dict = initial_dict; + setCurrentSelectId(type, getSelectId(initial_dict)); return false; } diff --git a/sources/pyside6/tests/QtCore/CMakeLists.txt b/sources/pyside6/tests/QtCore/CMakeLists.txt index e9717d9d0..89c34da58 100644 --- a/sources/pyside6/tests/QtCore/CMakeLists.txt +++ b/sources/pyside6/tests/QtCore/CMakeLists.txt @@ -37,6 +37,7 @@ PYSIDE_TEST(destroysignal_test.py) PYSIDE_TEST(duck_punching_test.py) PYSIDE_TEST(emoji_string_test.py) PYSIDE_TEST(errormessages_with_features_test.py) +PYSIDE_TEST(feature_with_uic_test.py) PYSIDE_TEST(hash_test.py) PYSIDE_TEST(inherits_test.py) PYSIDE_TEST(max_signals.py) diff --git a/sources/pyside6/tests/QtCore/feature_with_uic/window.py b/sources/pyside6/tests/QtCore/feature_with_uic/window.py new file mode 100644 index 000000000..6da881c6c --- /dev/null +++ b/sources/pyside6/tests/QtCore/feature_with_uic/window.py @@ -0,0 +1,90 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QHBoxLayout, QMainWindow, QMenuBar, + QPushButton, QSizePolicy, QStatusBar, QVBoxLayout, + QWidget) + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + if not MainWindow.objectName(): + MainWindow.setObjectName(u"MainWindow") + MainWindow.resize(263, 196) + self.centralwidget = QWidget(MainWindow) + self.centralwidget.setObjectName(u"centralwidget") + self.horizontalLayout = QHBoxLayout(self.centralwidget) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.verticalLayout = QVBoxLayout() + self.verticalLayout.setObjectName(u"verticalLayout") + self.pushButton = QPushButton(self.centralwidget) + self.pushButton.setObjectName(u"pushButton") + + self.verticalLayout.addWidget(self.pushButton) + + + self.horizontalLayout.addLayout(self.verticalLayout) + + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QMenuBar(MainWindow) + self.menubar.setObjectName(u"menubar") + self.menubar.setGeometry(QRect(0, 0, 263, 23)) + MainWindow.setMenuBar(self.menubar) + self.statusbar = QStatusBar(MainWindow) + self.statusbar.setObjectName(u"statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + self.pushButton.clicked.connect(MainWindow.close) + + QMetaObject.connectSlotsByName(MainWindow) + # setupUi + + def retranslateUi(self, MainWindow): + MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None)) + self.pushButton.setText(QCoreApplication.translate("MainWindow", u"PushButton", None)) + # retranslateUi + diff --git a/sources/pyside6/tests/QtCore/feature_with_uic/window.ui b/sources/pyside6/tests/QtCore/feature_with_uic/window.ui new file mode 100644 index 000000000..0b85824ea --- /dev/null +++ b/sources/pyside6/tests/QtCore/feature_with_uic/window.ui @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>263</width> + <height>196</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="pushButton"> + <property name="text"> + <string>PushButton</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>263</width> + <height>23</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections> + <connection> + <sender>pushButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel"> + <x>131</x> + <y>97</y> + </hint> + <hint type="destinationlabel"> + <x>131</x> + <y>97</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/sources/pyside6/tests/QtCore/feature_with_uic_test.py b/sources/pyside6/tests/QtCore/feature_with_uic_test.py new file mode 100644 index 000000000..001ab36f6 --- /dev/null +++ b/sources/pyside6/tests/QtCore/feature_with_uic_test.py @@ -0,0 +1,88 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +""" +feature_with_uic_test.py +------------------------ + +Check if feature switching works with a normal UIC file. +This crashed due to callbacks into QApplication. + +PYSIDE-1626: Switch early in `BindingManager::getOverride`. +""" + +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 QLibraryInfo, qVersion +from PySide6.QtWidgets import QApplication, QMainWindow + +from __feature__ import snake_case + +from feature_with_uic.window import Ui_MainWindow + + +class MainWindow(QMainWindow, Ui_MainWindow): + + def __init__(self) -> None: + super().__init__() + self.setupUi(self) + + +class FeatureTest(UsesQApplication): + + def testFeaturesWorkWithUIC(self): + window = MainWindow() + window.set_window_title(qVersion()) + window.show() + while not window.window_handle().is_exposed(): + qApp.process_events() + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index 2120ae7e2..e9bdc15af 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -582,6 +582,13 @@ void SbkObjectType_SetPropertyStrings(PyTypeObject *type, const char **strings) ptr->propertyStrings = strings; } +// PYSIDE-1626: Enforcing a context switch without further action. +void SbkObjectType_UpdateFeature(PyTypeObject *type) +{ + if (SelectFeatureSet != nullptr) + type->tp_dict = SelectFeatureSet(type); +} + // ////////////////////////////////////////////////////////////////////////////// diff --git a/sources/shiboken6/libshiboken/basewrapper.h b/sources/shiboken6/libshiboken/basewrapper.h index 38f0d93eb..b0e627b06 100644 --- a/sources/shiboken6/libshiboken/basewrapper.h +++ b/sources/shiboken6/libshiboken/basewrapper.h @@ -98,11 +98,14 @@ typedef void (*SubTypeInitHook)(SbkObjectType *, PyObject *, PyObject *); typedef PyObject *(*SelectableFeatureHook)(PyTypeObject *); LIBSHIBOKEN_API SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func); -// PYSIDE-1019: Get access to PySide reserved bits. +/// PYSIDE-1019: Get access to PySide reserved bits. LIBSHIBOKEN_API int SbkObjectType_GetReserved(PyTypeObject *type); LIBSHIBOKEN_API void SbkObjectType_SetReserved(PyTypeObject *type, int value); -// PYSIDE-1019: Get access to PySide property strings. +/// PYSIDE-1626: Enforcing a context switch without further action. +LIBSHIBOKEN_API void SbkObjectType_UpdateFeature(PyTypeObject *type); + +/// PYSIDE-1019: Get access to PySide property strings. LIBSHIBOKEN_API const char **SbkObjectType_GetPropertyStrings(PyTypeObject *type); LIBSHIBOKEN_API void SbkObjectType_SetPropertyStrings(PyTypeObject *type, const char **strings); diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp index bf448ac4a..1c7bec5f5 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.cpp +++ b/sources/shiboken6/libshiboken/bindingmanager.cpp @@ -291,6 +291,9 @@ PyObject *BindingManager::getOverride(const void *cptr, if (!wrapper || reinterpret_cast<const PyObject *>(wrapper)->ob_refcnt == 0) return nullptr; + // PYSIDE-1626: Touch the type to initiate switching early. + SbkObjectType_UpdateFeature(Py_TYPE(wrapper)); + int flag = currentSelectId(Py_TYPE(wrapper)); int propFlag = isdigit(methodName[0]) ? methodName[0] - '0' : 0; if ((flag & 0x02) != 0 && (propFlag & 3) != 0) { |