From 2b14cba81265bbeb31b35dc4e04ccd3f6e29c4ee Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Wed, 16 Nov 2022 14:58:28 +0100 Subject: __feature__: Fix true_property inheritance The wrapping process creates wrapper functions for all C functions, also for those which are meant as virtual functions promoting an inherited function. Because properties appear as such additional functions, we need to convert not only according to the property strings, but also use the mro to reach the extra functions indirectly. [ChangeLog][PySide6] true_property was fixed to work with inherited properties as well. Change-Id: I176a30df77f550504f3aaf71e0c20de3e0707792 Fixes: PYSIDE-2042 Pick-to: 6.4 Reviewed-by: Friedemann Kleint --- sources/pyside6/libpyside/feature_select.cpp | 30 ++++++++++++++--- sources/pyside6/tests/pysidetest/CMakeLists.txt | 1 + .../pyside6/tests/pysidetest/true_property_test.py | 38 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 sources/pyside6/tests/pysidetest/true_property_test.py diff --git a/sources/pyside6/libpyside/feature_select.cpp b/sources/pyside6/libpyside/feature_select.cpp index 567ae79e0..cf55ae7cf 100644 --- a/sources/pyside6/libpyside/feature_select.cpp +++ b/sources/pyside6/libpyside/feature_select.cpp @@ -614,6 +614,28 @@ PyObject *adjustPropertyName(PyObject *dict, PyObject *name) return name; } +static QByteArrayList GetPropertyStringsMro(PyTypeObject *type) +{ + /* + * PYSIDE-2042: There are possibly more methods which should become properties, + * because the wrapping process does not obey inheritance. + * Therefore, we need to walk the mro to find property strings. + */ + auto res = QByteArrayList(); + + PyObject *mro = type->tp_mro; + Py_ssize_t idx, n = PyTuple_GET_SIZE(mro); + // We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2". + for (idx = 0; idx < n - 2; idx++) { + auto *subType = reinterpret_cast(PyTuple_GET_ITEM(mro, idx)); + auto props = SbkObjectType_GetPropertyStrings(subType); + if (props != nullptr) + for (; *props != nullptr; ++props) + res << QByteArray(*props); + } + return res; +} + static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, int id) { /* @@ -643,12 +665,12 @@ static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, in } // We then replace methods by properties. bool lower = (id & 0x01) != 0; - auto props = SbkObjectType_GetPropertyStrings(type); - if (props == nullptr || *props == nullptr) + auto props = GetPropertyStringsMro(type); + if (props.isEmpty()) return true; - for (; *props != nullptr; ++props) { + + for (const auto &propStr : std::as_const(props)) { bool isStdWrite; - auto propStr = *props; auto fields = parseFields(propStr, &isStdWrite); bool haveWrite = fields.size() == 3; PyObject *name = make_snake_case(fields[0], lower); diff --git a/sources/pyside6/tests/pysidetest/CMakeLists.txt b/sources/pyside6/tests/pysidetest/CMakeLists.txt index e88b866c4..d0932509e 100644 --- a/sources/pyside6/tests/pysidetest/CMakeLists.txt +++ b/sources/pyside6/tests/pysidetest/CMakeLists.txt @@ -149,6 +149,7 @@ PYSIDE_TEST(new_inherited_functions_test.py) PYSIDE_TEST(notify_id.py) PYSIDE_TEST(properties_test.py) PYSIDE_TEST(property_python_test.py) +PYSIDE_TEST(true_property_test.py) PYSIDE_TEST(qapp_like_a_macro_test.py) PYSIDE_TEST(qvariant_test.py) PYSIDE_TEST(repr_test.py) diff --git a/sources/pyside6/tests/pysidetest/true_property_test.py b/sources/pyside6/tests/pysidetest/true_property_test.py new file mode 100644 index 000000000..671cc4571 --- /dev/null +++ b/sources/pyside6/tests/pysidetest/true_property_test.py @@ -0,0 +1,38 @@ +# Copyright (C) 2022 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) + +""" +PYSIDE-2042: Tests true_property with inheritance +""" + +from PySide6.QtCore import QSize +from PySide6.QtWidgets import QWidget, QSpinBox +from __feature__ import true_property +from helper.usesqapplication import UsesQApplication + + +class TruePropertyInheritanceTest(UsesQApplication): + + def testTrueProperty(self): + # this worked + widget = QWidget() + check = widget.sizeHint + self.assertEqual(type(check), QSize) + + # PYSIDE-2042: inheritance did not work + spin_box = QSpinBox() + check = spin_box.sizeHint + self.assertEqual(type(check), QSize) + + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.3