diff options
Diffstat (limited to 'sources/pyside6')
| -rw-r--r-- | sources/pyside6/libpyside/pysideproperty.cpp | 98 | ||||
| -rw-r--r-- | sources/pyside6/tests/pysidetest/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | sources/pyside6/tests/pysidetest/snake_case_imported.py | 25 | ||||
| -rw-r--r-- | sources/pyside6/tests/pysidetest/snake_case_imported_no_snake_case.py (renamed from sources/pyside6/tests/pysidetest/snake_case_sub.py) | 2 | ||||
| -rw-r--r-- | sources/pyside6/tests/pysidetest/snake_case_test.py | 18 |
5 files changed, 86 insertions, 58 deletions
diff --git a/sources/pyside6/libpyside/pysideproperty.cpp b/sources/pyside6/libpyside/pysideproperty.cpp index ff3c7cc9b..0207a7320 100644 --- a/sources/pyside6/libpyside/pysideproperty.cpp +++ b/sources/pyside6/libpyside/pysideproperty.cpp @@ -319,6 +319,9 @@ static int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds) { auto *pData = propertyPrivate(self); + if (!pData->typeName().isEmpty()) // Cloned copy, already initialized + return 0; + static const char *kwlist[] = {"type", "fget", "fset", "freset", "fdel", "doc", "notify", "designable", "scriptable", "stored", "user", "constant", "final", dataCapsuleKeyName, nullptr}; @@ -402,80 +405,73 @@ static void qpropertyDeAlloc(PyObject *self) } // Create a copy of the property to prevent the @property.setter from modifying -// the property in place and avoid strange side effects in derived classes -// (cf https://bugs.python.org/issue1620). -static PyObject * -_property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *reset, PyObject *del) +// the property in place and avoid strange side effects when modifying the +// property in derived classes (cf https://bugs.python.org/issue1620, +// pysidetest/property_python_test.py). +static PyObject *copyProperty(PyObject *old) { - auto *pData = propertyPrivate(old); - AutoDecRef type(PyObject_Type(old)); - QByteArray doc{}; - if (type.isNull()) - return nullptr; - - if (get == nullptr || get == Py_None) { - Py_XDECREF(get); - get = pData->fget ? pData->fget : Py_None; - } - if (set == nullptr || set == Py_None) { - Py_XDECREF(set); - set = pData->fset ? pData->fset : Py_None; - } - if (reset == nullptr || reset == Py_None) { - Py_XDECREF(reset); - reset = pData->freset ? pData->freset : Py_None; - } - if (del == nullptr || del == Py_None) { - Py_XDECREF(del); - del = pData->fdel ? pData->fdel : Py_None; - } - // make _init use __doc__ from getter - if ((pData->getter_doc && get != Py_None) || pData->doc().isEmpty()) - doc.clear(); - else - doc = pData->doc(); - - auto *notify = pData->notify() ? pData->notify() : Py_None; - - const auto flags = pData->flags(); - PyObject *obNew = - PyObject_CallFunction(type, "OOOOOsO" "bbb" "bbb", - pData->pyTypeObject(), get, set, reset, del, doc.data(), notify, - flags.testFlag(PySide::Property::PropertyFlag::Designable), - flags.testFlag(PySide::Property::PropertyFlag::Scriptable), - flags.testFlag(PySide::Property::PropertyFlag::Stored), - flags.testFlag(PySide::Property::PropertyFlag::User), - flags.testFlag(PySide::Property::PropertyFlag::Constant), - flags.testFlag(PySide::Property::PropertyFlag::Final)); - - return obNew; + Shiboken::AutoDecRef kwds(PyDict_New()); + addDataCapsuleToKwArgs(kwds, propertyPrivate(old)->clone()); + Shiboken::AutoDecRef args(PyTuple_New(0)); + return PyObject_Call(type.object(), args.object(), kwds.object()); } static PyObject *qPropertyGetter(PyObject *self, PyObject *getter) { - return _property_copy(self, getter, nullptr, nullptr, nullptr); + PyObject *result = copyProperty(self); + if (result != nullptr) { + auto *data = propertyPrivate(result); + auto *old = std::exchange(data->fget, getter); + Py_XINCREF(data->fget); + Py_XDECREF(old); + data->setFlag(PySide::Property::PropertyFlag::Readable); + } + return result; } static PyObject *qPropertySetter(PyObject *self, PyObject *setter) { - return _property_copy(self, nullptr, setter, nullptr, nullptr); + PyObject *result = copyProperty(self); + if (result != nullptr) { + auto *data = propertyPrivate(result); + auto *old = std::exchange(data->fset, setter); + Py_XINCREF(data->fset); + Py_XDECREF(old); + data->setFlag(PySide::Property::PropertyFlag::Writable); + } + return result; } static PyObject *qPropertyResetter(PyObject *self, PyObject *resetter) { - return _property_copy(self, nullptr, nullptr, resetter, nullptr); + PyObject *result = copyProperty(self); + if (result != nullptr) { + auto *data = propertyPrivate(result); + auto *old = std::exchange(data->freset, resetter); + Py_XINCREF(data->freset); + Py_XDECREF(old); + data->setFlag(PySide::Property::PropertyFlag::Resettable); + } + return result; } static PyObject *qPropertyDeleter(PyObject *self, PyObject *deleter) { - return _property_copy(self, nullptr, nullptr, nullptr, deleter); + PyObject *result = copyProperty(self); + if (result != nullptr) { + auto *data = propertyPrivate(result); + auto *old = std::exchange(data->fdel, deleter); + Py_XINCREF(data->fdel); + Py_XDECREF(old); + } + return result; } static PyObject *qPropertyCall(PyObject *self, PyObject *args, PyObject * /* kw */) { PyObject *getter = PyTuple_GetItem(args, 0); - return _property_copy(self, getter, nullptr, nullptr, nullptr); + return qPropertyGetter(self, getter); } // PYSIDE-1019: Provide the same getters as Pythons `PyProperty`. diff --git a/sources/pyside6/tests/pysidetest/CMakeLists.txt b/sources/pyside6/tests/pysidetest/CMakeLists.txt index 4a7e2e1d1..8b4de5d8e 100644 --- a/sources/pyside6/tests/pysidetest/CMakeLists.txt +++ b/sources/pyside6/tests/pysidetest/CMakeLists.txt @@ -156,7 +156,6 @@ 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(snake_case_sub.py) PYSIDE_TEST(snake_case_test.py) PYSIDE_TEST(true_property_test.py) PYSIDE_TEST(qapp_like_a_macro_test.py) diff --git a/sources/pyside6/tests/pysidetest/snake_case_imported.py b/sources/pyside6/tests/pysidetest/snake_case_imported.py new file mode 100644 index 000000000..c79966e1e --- /dev/null +++ b/sources/pyside6/tests/pysidetest/snake_case_imported.py @@ -0,0 +1,25 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +from __future__ import annotations + +import os +import sys + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from init_paths import init_test_paths # noqa: E402 +init_test_paths(False) + +from __feature__ import snake_case # noqa + +""" +PYSIDE-3250: Tests that snake_case works when used in several files +""" + +from PySide6.QtWidgets import QWidget # noqa: E402 + + +def test(): + print(__name__) + widget = QWidget() + return widget.size_hint() diff --git a/sources/pyside6/tests/pysidetest/snake_case_sub.py b/sources/pyside6/tests/pysidetest/snake_case_imported_no_snake_case.py index e423542a6..a5ce14694 100644 --- a/sources/pyside6/tests/pysidetest/snake_case_sub.py +++ b/sources/pyside6/tests/pysidetest/snake_case_imported_no_snake_case.py @@ -20,4 +20,4 @@ from PySide6.QtWidgets import QWidget # noqa: E402 def test_no_snake_case(): print(__name__) widget = QWidget() - check = widget.sizeHint # noqa + return widget.sizeHint() diff --git a/sources/pyside6/tests/pysidetest/snake_case_test.py b/sources/pyside6/tests/pysidetest/snake_case_test.py index bdcd996c4..4667e584a 100644 --- a/sources/pyside6/tests/pysidetest/snake_case_test.py +++ b/sources/pyside6/tests/pysidetest/snake_case_test.py @@ -21,18 +21,26 @@ if not is_pypy: from __feature__ import snake_case # noqa from helper.usesqapplication import UsesQApplication -import snake_case_sub +import snake_case_imported +import snake_case_imported_no_snake_case @unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy") class SnakeCaseNoPropagateTest(UsesQApplication): - def testSnakeCase(self): - # this worked + def testSnakeCaseImport(self): + """PYSIDE-3250: Test that snake case works when using it in imported modules.""" widget = QWidget() - check = widget.size_hint # noqa + r1 = widget.size_hint() + r2 = snake_case_imported.test() + self.assertEqual(r1, r2) - snake_case_sub.test_no_snake_case() + def testSnakeCaseImportNoSnakeCase(self): + """PYSIDE-2029: Tests that snake_case is isolated from imported modules.""" + widget = QWidget() + r1 = widget.size_hint() + r2 = snake_case_imported_no_snake_case.test_no_snake_case() + self.assertEqual(r1, r2) if __name__ == '__main__': |
