aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6')
-rw-r--r--sources/pyside6/libpyside/pysideproperty.cpp98
-rw-r--r--sources/pyside6/tests/pysidetest/CMakeLists.txt1
-rw-r--r--sources/pyside6/tests/pysidetest/snake_case_imported.py25
-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.py18
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__':