diff options
17 files changed, 261 insertions, 16 deletions
diff --git a/build_scripts/platforms/windows_desktop.py b/build_scripts/platforms/windows_desktop.py index f8a776b87..112bd436f 100644 --- a/build_scripts/platforms/windows_desktop.py +++ b/build_scripts/platforms/windows_desktop.py @@ -239,6 +239,7 @@ def prepare_packages_win32(self, vars): if config.is_internal_pyside_build() or config.is_internal_shiboken_generator_build(): copy_qt_artifacts(self, copy_pdbs, vars) + copy_msvc_redist_files(vars, "{build_dir}/msvc_redist".format(**vars)) def copy_msvc_redist_files(vars, redist_target_path): @@ -251,7 +252,10 @@ def copy_msvc_redist_files(vars, redist_target_path): "vccorlib140.dll", "vcomp140.dll", "vcruntime140.dll", - "vcruntime140_1.dll" + "vcruntime140_1.dll", + "msvcp140_1.dll", + "msvcp140_2.dll", + "msvcp140_codecvt_ids.dll" ] # Make a directory where the files should be extracted. diff --git a/dist/changes-5.15.1 b/dist/changes-5.15.1 new file mode 100644 index 000000000..8627d3e92 --- /dev/null +++ b/dist/changes-5.15.1 @@ -0,0 +1,67 @@ +Qt for Python 5.15.1 is a bug-fix release. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qtforpython/ + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* PySide2 * +**************************************************************************** + + + - [PYSIDE-74] Implement default __ne__ and __eq__ for all PySide types + - [PYSIDE-198] Add compatibility with Nuitka + - [PYSIDE-829] signature: Avoid non-existent attributes in compiled code (Nuitka) + - [PYSIDE-841] doc: Add widget styling tutorial + - [PYSIDE-841] add quick painteditem example + - [PYSIDE-904] Add QObject.findChildren(QRegularExpression) + - [PYSIDE-904] qp5_tool.py: Add an configuration key for the CMake generator + - [PYSIDE-904] Port some examples from QRegExp to QRegularExpression + - [PYSIDE-904] Fix libsample/photon test for Qt 6 / Windows + - [PYSIDE-904] Enable adding operators ==, != as functions without code injection + - [PYSIDE-957] Implement the QEnum/QFlag decorator, V2 + - [PYSIDE-981] Return QVariantList when using list as Signal argument + - [PYSIDE-1019] feature-select: Implement a selectable feature framework + - [PYSIDE-1019] feature-select: allow snake_case instead of camelCase for methods + - [PYSIDE-1019] feature-select: delay the feature switching + - [PYSIDE-1223] Use pyside2-uic instead of uic for the loadUiType + - [PYSIDE-1257] Potential fix for deploying with cx_freeze using zip_include_packages + - [PYSIDE-1282] pthreads: Try to abandon the GIL in case a thread was terminated + - [PYSIDE-1292] Doc: Enable doc builds using the offline template + - [PYSIDE-1313] basewrapper.cpp: add PyErr_Fetch/Restore in SbkDeallocWrapperCommon() + - [PYSIDE-1317] Add QSocketDescriptor class + - [PYSIDE-1321] Fix leaking reference in PySide2 property getter + - [PYSIDE-1321] Fix some leaks in enumeration creation + - [PYSIDE-1323] Add missing Win runtime dll into win wheels + - [PYSIDE-1323] Update vcredist binaries for MSVC 2019 + - [PYSIDE-1332] Fix crashes in QThread::wait(), QWaitCondition::wait() + - [PYSIDE-1349] QQmlComponent: allow thread in constructors + - [PYSIDE-1353] doc: fix Property indentation + - [PYSIDE-1355] Add Qt3DExtras.QNormalDiffuseMapAlphaMaterial + - [PYSIDE-1368] __feature__: ignore if `__name__` does not exist in a module + - [PYSIDE-1372] QDomElement: remove unnecesary setAttribute overloads + - [PYSIDE-1374] Add the QWidget *-based overloads of the QtWinExtras functions + +**************************************************************************** +* Shiboken2 * +**************************************************************************** + + * shiboken now accepts multiple headers on the command line. + * A number of error and warning messages have been prefixed by file name + and line for better tooling. + - [PYSIDE-841] Add custom widget shiboken example + - [PYSIDE-904] shiboken2: Handle virtual methods returning a reference + - [PYSIDE-1019] shiboken2: Re-add support for parsing Q_PROPERTY + - [PYSIDE-1177] shiboken: Fix __doc__ setter for derived types + - [PYSIDE-1325] shiboken: Fix default-initialized function arguments + - [PYSIDE-1327] shiboken: Resolve typedef'ed base classes + diff --git a/sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt b/sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt index 3593394c3..6c877dfe4 100644 --- a/sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt @@ -18,21 +18,27 @@ ${QtWinExtras_GEN_DIR}/qwinthumbnailtoolbutton_wrapper.cpp ${QtWinExtras_GEN_DIR}/qtwinextras_module_wrapper.cpp ) +configure_file("${QtWinExtras_SOURCE_DIR}/QtWinExtras_global.pre.h.in" + "${QtWinExtras_BINARY_DIR}/QtWinExtras_global.pre.h" @ONLY) + set(QtWinExtras_include_dirs ${QtWinExtras_SOURCE_DIR} ${QtWinExtras_BINARY_DIR} ${Qt${QT_MAJOR_VERSION}WinExtras_INCLUDE_DIRS} ${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS} ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS} + ${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS} ${QtCore_GEN_DIR} ${QtGui_GEN_DIR} + ${QtWidgets_GEN_DIR} ${libpyside_SOURCE_DIR}) set(QtWinExtras_libraries pyside2 ${Qt${QT_MAJOR_VERSION}WinExtras_LIBRARIES} ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES} - ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}) + ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES} + ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}) -set(QtWinExtras_deps QtCore QtGui) +set(QtWinExtras_deps QtCore QtGui QtWidgets) create_pyside_module(NAME QtWinExtras INCLUDE_DIRS QtWinExtras_include_dirs diff --git a/sources/pyside2/PySide2/QtWinExtras/QtWinExtras_global.pre.h.in b/sources/pyside2/PySide2/QtWinExtras/QtWinExtras_global.pre.h.in new file mode 100644 index 000000000..21285e301 --- /dev/null +++ b/sources/pyside2/PySide2/QtWinExtras/QtWinExtras_global.pre.h.in @@ -0,0 +1,5 @@ +// QT_WIDGETS_LIB must be defined for the widget functions to become visible. + +#if @Qt6Widgets_FOUND@ +# define QT_WIDGETS_LIB +#endif diff --git a/sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml b/sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml index db7416a24..69c90dd34 100644 --- a/sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml +++ b/sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml @@ -42,6 +42,7 @@ <typesystem package="PySide2.QtWinExtras"> <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/> <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/> + <load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/> <namespace-type name="QtWin"> <enum-type name="HBitmapFormat"/> diff --git a/sources/pyside2/PySide2/QtXml/typesystem_xml.xml b/sources/pyside2/PySide2/QtXml/typesystem_xml.xml index e1750fb80..96d12ea4e 100644 --- a/sources/pyside2/PySide2/QtXml/typesystem_xml.xml +++ b/sources/pyside2/PySide2/QtXml/typesystem_xml.xml @@ -231,7 +231,20 @@ <object-type name="QXmlNamespaceSupport"/> - <value-type name="QDomElement"/> + <value-type name="QDomElement"> + <!-- PYSIDE-1372 + We will leave only one for int, and one for float since Python + doesn't have other variations on the primitive types. + Only 'qlonglong' and 'double' will be available from the Qt API. + TODO: This will require a better review of the shiboken primitive + types converters, since this situation might happen on + different signatures. + --> + <modify-function signature="setAttribute(const QString&, uint)" remove="all"/> + <modify-function signature="setAttribute(const QString&, float)" remove="all"/> + <modify-function signature="setAttribute(const QString&, int)" remove="all"/> + <modify-function signature="setAttribute(const QString&, qulonglong)" remove="all"/> + </value-type> <object-type name="QXmlContentHandler"> <modify-function signature="setDocumentLocator(QXmlLocator*)"> diff --git a/sources/pyside2/doc/considerations.rst b/sources/pyside2/doc/considerations.rst index f05d929b5..b5eae7d86 100644 --- a/sources/pyside2/doc/considerations.rst +++ b/sources/pyside2/doc/considerations.rst @@ -133,3 +133,17 @@ Abandoned Alternative We also tried an alternative implementation with a ``qApp()`` function that was more *pythonic* and problem free, but many people liked the ``qApp`` macro better for its brevity, so here it is. + + +Rich Comparison +~~~~~~~~~~~~~~~ + +There was a long-standing bug in the ``tp_richcompare`` implementation of PySide classes. + +* When a class did not implement it, the default implementation of ``object`` is used. + This implements ``==`` and ``!=`` like the ``is`` operator. + +* When a class implements only a single function like ``<``, then the default implementation + was disabled, and expressions like ``obj in sequence`` failed with ``NotImplemented``. + +This oversight was fixed in version 5.15.1 . diff --git a/sources/pyside2/tests/QtWidgets/CMakeLists.txt b/sources/pyside2/tests/QtWidgets/CMakeLists.txt index 33bff6fed..afa5eae0a 100644 --- a/sources/pyside2/tests/QtWidgets/CMakeLists.txt +++ b/sources/pyside2/tests/QtWidgets/CMakeLists.txt @@ -122,6 +122,7 @@ PYSIDE_TEST(qtextedit_test.py) PYSIDE_TEST(qtextedit_signal_test.py) PYSIDE_TEST(qtreeview_test.py) PYSIDE_TEST(qtreewidget_test.py) +PYSIDE_TEST(qtreewidgetitem_test.py) PYSIDE_TEST(qtoolbar_test.py) PYSIDE_TEST(qtoolbox_test.py) PYSIDE_TEST(qvariant_test.py) diff --git a/sources/pyside2/tests/QtWidgets/qtreewidgetitem_test.py b/sources/pyside2/tests/QtWidgets/qtreewidgetitem_test.py new file mode 100644 index 000000000..115bce4b7 --- /dev/null +++ b/sources/pyside2/tests/QtWidgets/qtreewidgetitem_test.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of PySide2. +## +## $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$ +## +############################################################################# + +""" +Unit tests for QTreeWidgetItem +------------------------------ + +This test is actually meant for all types which provide `tp_richcompare` +but actually define something without providing `==` or `!=` operators. +QTreeWidgetItem for instance defines `<` only. + +PYSIDE-74: We redirect to type `object`s handling which is anyway the default + when `tp_richcompare` is undefined. +""" + +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 PySide2 import QtCore, QtWidgets + + +class QTreeWidgetItemTest(unittest.TestCase): + def testClass(self): + app = QtWidgets.QApplication([]) + treewidget = QtWidgets.QTreeWidget() + item = QtWidgets.QTreeWidgetItem(["Words and stuff"]) + item2 = QtWidgets.QTreeWidgetItem(["More words!"]) + treewidget.insertTopLevelItem(0, item) + + dummy_list = ["Numbers", "Symbols", "Spam"] + self.assertFalse(item in dummy_list) + self.assertTrue(item not in dummy_list) + self.assertFalse(item == item2) + self.assertTrue(item != item2) + treewidget.show() + QtCore.QTimer.singleShot(500, app.quit) + app.exec_() + + +if __name__ == "__main__": + unittest.main() + diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index c8565d44a..33596e235 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -2797,7 +2797,7 @@ void AbstractMetaBuilderPrivate::parseQ_Properties(AbstractMetaClass *metaClass, } QPropertySpec *AbstractMetaBuilderPrivate::parseQ_Property(AbstractMetaClass *metaClass, - const QString &declaration, + const QString &declarationIn, const QStringList &scopes, QString *errorMessage) { @@ -2805,6 +2805,7 @@ QPropertySpec *AbstractMetaBuilderPrivate::parseQ_Property(AbstractMetaClass *me // Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged) + const QString declaration = declarationIn.simplified(); auto propertyTokens = QStringView{declaration}.split(QLatin1Char(' '), Qt::SkipEmptyParts); if (propertyTokens.size() < 4) { @@ -2812,27 +2813,40 @@ QPropertySpec *AbstractMetaBuilderPrivate::parseQ_Property(AbstractMetaClass *me return nullptr; } - const QString typeName = propertyTokens.takeFirst().toString(); - const QString name = propertyTokens.takeFirst().toString(); + QString fullTypeName = propertyTokens.takeFirst().toString(); + QString name = propertyTokens.takeFirst().toString(); + // Fix errors like "Q_PROPERTY(QXYSeries *series .." to be of type "QXYSeries*" + while (name.startsWith(QLatin1Char('*'))) { + fullTypeName += name.at(0); + name.remove(0, 1); + } + + int indirections = 0; + QString typeName = fullTypeName; + for (; typeName.endsWith(QLatin1Char('*')); ++indirections) + typeName.chop(1); QScopedPointer<AbstractMetaType> type; + QString typeError; for (int j = scopes.size(); j >= 0 && type.isNull(); --j) { QStringList qualifiedName = scopes.mid(0, j); qualifiedName.append(typeName); TypeInfo info; + info.setIndirections(indirections); info.setQualifiedName(qualifiedName); - type.reset(translateType(info, metaClass)); + type.reset(translateType(info, metaClass, {}, &typeError)); } if (!type) { QTextStream str(errorMessage); str << "Unable to decide type of property: \"" << name << "\" (" - << typeName << ')'; + << typeName << "): " << typeError; return nullptr; } QScopedPointer<QPropertySpec> spec(new QPropertySpec(type->typeEntry())); spec->setName(name); + spec->setIndirections(indirections); for (int pos = 0; pos + 1 < propertyTokens.size(); pos += 2) { if (propertyTokens.at(pos) == QLatin1String("READ")) diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 8b24a49e1..cd419837d 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -2782,14 +2782,16 @@ bool QPropertySpec::isValid() const #ifndef QT_NO_DEBUG_STREAM void QPropertySpec::formatDebug(QDebug &d) const { - d << '#' << m_index << " \"" << m_name << "\" (" << m_type->qualifiedCppName() - << "), read=" << m_read; + d << '#' << m_index << " \"" << m_name << "\" (" << m_type->qualifiedCppName(); + for (int i = 0; i < m_indirections; ++i) + d << '*'; + d << "), read=" << m_read; if (!m_write.isEmpty()) d << ", write=" << m_write; if (!m_reset.isEmpty()) d << ", reset=" << m_reset; if (!m_designable.isEmpty()) - d << ", esignable=" << m_designable; + d << ", designable=" << m_designable; } QDebug operator<<(QDebug d, const QPropertySpec &p) diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index de7a1e3a5..9ceae14df 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -1743,6 +1743,9 @@ public: return m_type; } + int indirections() const { return m_indirections; } + void setIndirections(int indirections) { m_indirections = indirections; } + QString name() const { return m_name; @@ -1814,6 +1817,7 @@ private: QString m_designable; QString m_reset; const TypeEntry *m_type; + int m_indirections = 0; int m_index = -1; }; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index f0c6398ee..6927229d9 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -4596,6 +4596,8 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, const GeneratorConte s << INDENT << "default:\n"; { Indentation indent(INDENT); + s << INDENT << "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n"; + s << INDENT << "return FallbackRichCompare(self, " << PYTHON_ARG << ", op);\n"; s << INDENT << "goto " << baseName << "_RichComparison_TypeError;\n"; } } diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index 61c4ae984..05f8a4fb8 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -809,6 +809,33 @@ PyObject *SbkType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) return type; } +// PYSIDE-74: Fallback used in all types now. +PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op) +{ + // This is a very simple implementation that supplies a simple identity. + static const char * const opstrings[] = {"<", "<=", "==", "!=", ">", ">="}; + PyObject *res; + + switch (op) { + + case Py_EQ: + res = (self == other) ? Py_True : Py_False; + break; + case Py_NE: + res = (self != other) ? Py_True : Py_False; + break; + default: + PyErr_Format(PyExc_TypeError, + "'%s' not supported between instances of '%.100s' and '%.100s'", + opstrings[op], + self->ob_type->tp_name, + other->ob_type->tp_name); + return NULL; + } + Py_INCREF(res); + return res; +} + } //extern "C" diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h index b233c02b4..267759daa 100644 --- a/sources/shiboken2/libshiboken/basewrapper.h +++ b/sources/shiboken2/libshiboken/basewrapper.h @@ -135,6 +135,9 @@ LIBSHIBOKEN_API PyObject *SbkDummyNew(PyTypeObject *type, PyObject *, PyObject * LIBSHIBOKEN_API PyObject *SbkType_FromSpec(PyType_Spec *); LIBSHIBOKEN_API PyObject *SbkType_FromSpecWithBases(PyType_Spec *, PyObject *); +/// PYSIDE-74: Fallback used in all types now. +LIBSHIBOKEN_API PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op); + } // extern "C" namespace Shiboken diff --git a/sources/shiboken2/tests/samplebinding/point_test.py b/sources/shiboken2/tests/samplebinding/point_test.py index 6d254128f..c2159e7d5 100644 --- a/sources/shiboken2/tests/samplebinding/point_test.py +++ b/sources/shiboken2/tests/samplebinding/point_test.py @@ -70,7 +70,10 @@ class PointTest(unittest.TestCase): '''Test Point class != operator.''' pt1 = Point(5.0, 2.3) pt2 = Point(5.0, 2.3) - self.assertRaises(NotImplementedError, pt1.__ne__, pt2) + # This test no longer makes sense because we always supply default `==`, `!=`. + #self.assertRaises(NotImplementedError, pt1.__ne__, pt2) + # Since we use the default identity comparison, this results in `!=` . + self.assertTrue(pt1 != pt2) def testReturnNewCopy(self): '''Point returns a copy of itself.''' diff --git a/sources/shiboken2/tests/shiboken_paths.py b/sources/shiboken2/tests/shiboken_paths.py index c9d1d129a..1072aa501 100644 --- a/sources/shiboken2/tests/shiboken_paths.py +++ b/sources/shiboken2/tests/shiboken_paths.py @@ -76,10 +76,15 @@ def _prepend_path_var(var_name, paths): def add_python_dirs(python_dirs): - """Add directories to the Python path unless present.""" + """Add directories to the Python path unless present. + Care is taken that the added directories come before + site-packages.""" + new_paths = [] for python_dir in python_dirs: - if python_dir not in sys.path: - sys.path.append(python_dir) + new_paths.append(python_dir) + if python_dir in sys.path: + sys.path.remove(python_dir) + sys.path[:0] = new_paths def add_lib_dirs(lib_dirs): |