diff options
author | Christian Tismer <tismer@stackless.com> | 2021-07-20 10:55:10 +0200 |
---|---|---|
committer | Christian Tismer <tismer@stackless.com> | 2021-08-05 14:41:14 +0200 |
commit | 0c75c823b0c8e48f03fa0bb166b195bfa7d5f2e9 (patch) | |
tree | b5b67aeb1ce883dad8331dad7b955df95123826e /sources | |
parent | 1f4c770c07b64d33e0f5a73dd82a0e25ac23c7f3 (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: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'sources')
-rw-r--r-- | sources/pyside2/libpyside/feature_select.cpp | 20 | ||||
-rw-r--r-- | sources/pyside2/tests/QtCore/CMakeLists.txt | 1 | ||||
-rw-r--r-- | sources/pyside2/tests/QtCore/feature_with_uic/__init__.py | 29 | ||||
-rw-r--r-- | sources/pyside2/tests/QtCore/feature_with_uic/window.py | 81 | ||||
-rw-r--r-- | sources/pyside2/tests/QtCore/feature_with_uic/window.ui | 62 | ||||
-rw-r--r-- | sources/pyside2/tests/QtCore/feature_with_uic_test.py | 76 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/basewrapper.cpp | 7 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/basewrapper.h | 7 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/bindingmanager.cpp | 3 |
9 files changed, 283 insertions, 3 deletions
diff --git a/sources/pyside2/libpyside/feature_select.cpp b/sources/pyside2/libpyside/feature_select.cpp index 1e1eb9357..dd8a11ee4 100644 --- a/sources/pyside2/libpyside/feature_select.cpp +++ b/sources/pyside2/libpyside/feature_select.cpp @@ -237,6 +237,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; } @@ -257,6 +258,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; } @@ -279,6 +281,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; } @@ -400,6 +403,13 @@ void Select(PyObject *obj) type->tp_dict = SelectFeatureSet(type); } +PyObject *Select(PyTypeObject *type) +{ + if (featurePointer != nullptr) + type->tp_dict = SelectFeatureSet(type); + return type->tp_dict; +} + static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, int id); static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, int id); static bool feature_04_addDummyNames(PyTypeObject *type, PyObject *prev_dict, int id); @@ -428,11 +438,11 @@ void finalize() } static bool patch_property_impl(); +static bool is_initialized = false; void init() { // This function can be called multiple times. - static bool is_initialized = false; if (!is_initialized) { fast_id_array = &_fast_id_array[1]; for (int idx = -1; idx < 256; ++idx) @@ -448,6 +458,14 @@ void init() cached_globals = nullptr; } +void Enable(bool enable) +{ + if (!is_initialized) + return; + featurePointer = enable ? featureProcArray : nullptr; + initSelectableFeature(enable ? SelectFeatureSet : nullptr); +} + ////////////////////////////////////////////////////////////////////////////// // // PYSIDE-1019: Support switchable extensions diff --git a/sources/pyside2/tests/QtCore/CMakeLists.txt b/sources/pyside2/tests/QtCore/CMakeLists.txt index 9d268e079..c7e50d640 100644 --- a/sources/pyside2/tests/QtCore/CMakeLists.txt +++ b/sources/pyside2/tests/QtCore/CMakeLists.txt @@ -37,6 +37,7 @@ PYSIDE_TEST(deletelater_test.py) PYSIDE_TEST(destroysignal_test.py) PYSIDE_TEST(duck_punching_test.py) PYSIDE_TEST(emoji_string_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/pyside2/tests/QtCore/feature_with_uic/__init__.py b/sources/pyside2/tests/QtCore/feature_with_uic/__init__.py new file mode 100644 index 000000000..396f82fb1 --- /dev/null +++ b/sources/pyside2/tests/QtCore/feature_with_uic/__init__.py @@ -0,0 +1,29 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +# this file intentionally left blank diff --git a/sources/pyside2/tests/QtCore/feature_with_uic/window.py b/sources/pyside2/tests/QtCore/feature_with_uic/window.py new file mode 100644 index 000000000..db0fbd033 --- /dev/null +++ b/sources/pyside2/tests/QtCore/feature_with_uic/window.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +################################################################################ +## Form generated from reading UI file 'window.ui' +## +## Created by: Qt User Interface Compiler version 5.15.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide2.QtCore import * +from PySide2.QtGui import * +from PySide2.QtWidgets import * + + +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/pyside2/tests/QtCore/feature_with_uic/window.ui b/sources/pyside2/tests/QtCore/feature_with_uic/window.ui new file mode 100644 index 000000000..0b85824ea --- /dev/null +++ b/sources/pyside2/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/pyside2/tests/QtCore/feature_with_uic_test.py b/sources/pyside2/tests/QtCore/feature_with_uic_test.py new file mode 100644 index 000000000..5b61a4afb --- /dev/null +++ b/sources/pyside2/tests/QtCore/feature_with_uic_test.py @@ -0,0 +1,76 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $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 + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from init_paths import init_test_paths +init_test_paths(False) + +from helper.usesqapplication import UsesQApplication + +from PySide2.QtCore import QLibraryInfo, qVersion +from PySide2.QtWidgets import QApplication, QMainWindow + +if sys.version_info[0] >= 3: + from __feature__ import snake_case + +from feature_with_uic.window import Ui_MainWindow + + +class MainWindow(QMainWindow, Ui_MainWindow): + + def __init__(self): + 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(): + QCoreApplication.process_events() + + +if __name__ == '__main__' and sys.version_info[0] >= 3: + unittest.main() diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index 48be4ea04..ab637354e 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -659,6 +659,13 @@ void SbkObjectType_SetPropertyStrings(PyTypeObject *type, const char **strings) PepType_SOTP(reinterpret_cast<SbkObjectType *>(type))->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/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h index 322581353..c6fc1af2a 100644 --- a/sources/shiboken2/libshiboken/basewrapper.h +++ b/sources/shiboken2/libshiboken/basewrapper.h @@ -80,11 +80,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/shiboken2/libshiboken/bindingmanager.cpp b/sources/shiboken2/libshiboken/bindingmanager.cpp index 6c1300188..7111197bf 100644 --- a/sources/shiboken2/libshiboken/bindingmanager.cpp +++ b/sources/shiboken2/libshiboken/bindingmanager.cpp @@ -274,6 +274,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) { |