diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-06-19 08:12:33 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-06-19 08:12:33 +0200 |
commit | d8bd1449361835a3fe67f8ad33e82cec78940f1c (patch) | |
tree | 279e019b83fb2aa9e0673d00b5df4c432315ea51 | |
parent | b4098737b13c91ca85b69362426f0f30768c49b1 (diff) | |
parent | 7dba09d7c4007e96fb0a39bf6f6e740cd207324a (diff) |
Merge remote-tracking branch 'origin/5.12' into 5.13
Change-Id: I42955abfeb1fe8b7c6443a9b334154984286b9cc
26 files changed, 398 insertions, 138 deletions
diff --git a/.gitignore b/.gitignore index b49ed94bc..b8977159c 100644 --- a/.gitignore +++ b/.gitignore @@ -65,7 +65,7 @@ __pycache__/ .Python build/ develop-eggs/ -dist/ +#dist/ # we place the changelogs here downloads/ eggs/ .eggs/ diff --git a/dist/changes-5.12.4 b/dist/changes-5.12.4 new file mode 100644 index 000000000..d6d1679bc --- /dev/null +++ b/dist/changes-5.12.4 @@ -0,0 +1,43 @@ +Qt for Python 5.12.4 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-571] correct bool(qApp) on Python 2 + - [PYSIDE-735] generate_pyi: Simplify, Enhance and Get Rid Of Any Import Problems + - [PYSIDE-865] opengl/contextinfo.py: Fix exception on exit + - [PYSIDE-865] opengl/contextinfo.py: Polish the example a bit + - [PYSIDE-908] Complete the help() support for Types + - [PYSIDE-955] Add PySide2: Add OpenGL version functions as separate module + - [PYSIDE-980] CMake: Change the default behavior CMAKE_BUILD_TYPE + - [PYSIDE-996] Fix PyInstaller after removal of COIN glitches + - [PYSIDE-1003] Fix Shiboken.ObjectType.__new__ for Python 2.7 V2 + - [PYSIDE-1004] Make the __signature__ attribute writable by default + - [PYSIDE-1008] Fix propagation of existing LD env vars when calling generate_pyi + - [PYSIDE-1009] Signature: Try to recognize unknown modules in errorhandler.py + - [PYSIDE-1012] Fix build on centOS/conda forge + - [PYSIDE-1019] PySide: Allow any existing attribute in the constructor + - [PYSIDE-1022] TabbedBrowser Example: Fix "Open in new tab" + - [PYSIDE-1027] Fix negative refcount on QSocketNotifier + +**************************************************************************** +* Shiboken2 * +**************************************************************************** + + - [PYSIDE-955] shiboken/Generators: Cache class information lists per class + - [PYSIDE-1016] shiboken: Fix handling shared pointers passed by const-ref, take 2 + - [PYSIDE-1017] shiboken: Enable specifying names for a parameters of added functions diff --git a/examples/webenginewidgets/tabbedbrowser/webengineview.py b/examples/webenginewidgets/tabbedbrowser/webengineview.py index 7b5775bc8..4fdbf1801 100644 --- a/examples/webenginewidgets/tabbedbrowser/webengineview.py +++ b/examples/webenginewidgets/tabbedbrowser/webengineview.py @@ -79,7 +79,7 @@ class WebEngineView(QWebEngineView): def is_web_action_enabled(self, web_action): return self.page().action(web_action).isEnabled() - def create_window(self, window_type): + def createWindow(self, window_type): if window_type == QWebEnginePage.WebBrowserTab or window_type == QWebEnginePage.WebBrowserBackgroundTab: return self._tab_factory_func() return self._window_factory_func() diff --git a/examples/widgets/mainwindows/application/application.py b/examples/widgets/mainwindows/application/application.py index e0a46bdad..624fb47d5 100644 --- a/examples/widgets/mainwindows/application/application.py +++ b/examples/widgets/mainwindows/application/application.py @@ -229,19 +229,22 @@ class MainWindow(QtWidgets.QMainWindow): self.statusBar().showMessage("File loaded", 2000) def saveFile(self, fileName): - file = QtCore.QFile(fileName) - if not file.open(QtCore.QFile.WriteOnly | QtCore.QFile.Text): - QtWidgets.QMessageBox.warning(self, "Application", - "Cannot write file %s:\n%s." % (fileName, file.errorString())) - return False - - outf = QtCore.QTextStream(file) + error = None QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) - - # FIXME: Once file is out of scope, the file is empty, instead of having text. - outf << self.textEdit.toPlainText() + file = QtCore.QSaveFile(fileName) + if file.open(QtCore.QFile.WriteOnly | QtCore.QFile.Text): + outf = QtCore.QTextStream(file) + outf << self.textEdit.toPlainText() + if not file.commit(): + error = "Cannot write file %s:\n%s." % (fileName, file.errorString()) + else: + error = "Cannot open file %s:\n%s." % (fileName, file.errorString()) QtWidgets.QApplication.restoreOverrideCursor() + if error: + QtWidgets.QMessageBox.warning(self, "Application", error) + return False + self.setCurrentFile(fileName) self.statusBar().showMessage("File saved", 2000) return True diff --git a/examples/widgets/mainwindows/mdi/mdi.py b/examples/widgets/mainwindows/mdi/mdi.py index b3c1c1eb9..9daca826d 100644 --- a/examples/widgets/mainwindows/mdi/mdi.py +++ b/examples/widgets/mainwindows/mdi/mdi.py @@ -43,7 +43,7 @@ """PySide2 port of the widgets/draganddrop/draggabletext example from Qt v5.x, originating from PyQt""" from PySide2.QtCore import (QFile, QFileInfo, QPoint, QSettings, QSignalMapper, - QSize, QTextStream, Qt) + QSaveFile, QSize, QTextStream, Qt) from PySide2.QtGui import QIcon, QKeySequence from PySide2.QtWidgets import (QAction, QApplication, QFileDialog, QMainWindow, QMdiArea, QMessageBox, QTextEdit, QWidget) @@ -100,18 +100,22 @@ class MdiChild(QTextEdit): return self.saveFile(fileName) def saveFile(self, fileName): - file = QFile(fileName) - - if not file.open(QFile.WriteOnly | QFile.Text): - QMessageBox.warning(self, "MDI", - "Cannot write file %s:\n%s." % (fileName, file.errorString())) - return False - - outstr = QTextStream(file) + error = None QApplication.setOverrideCursor(Qt.WaitCursor) - outstr << self.toPlainText() + file = QSaveFile(fileName) + if file.open(QFile.WriteOnly | QFile.Text): + outstr = QTextStream(file) + outstr << self.toPlainText() + if not file.commit(): + error = "Cannot write file %s:\n%s." % (fileName, file.errorString()) + else: + error = "Cannot open file %s:\n%s." % (fileName, file.errorString()) QApplication.restoreOverrideCursor() + if error: + QMessageBox.warning(self, "MDI", error) + return False + self.setCurrentFile(fileName) return True diff --git a/sources/pyside2/PySide2/glue/qtcore.cpp b/sources/pyside2/PySide2/glue/qtcore.cpp index fde016548..9db4e2e82 100644 --- a/sources/pyside2/PySide2/glue/qtcore.cpp +++ b/sources/pyside2/PySide2/glue/qtcore.cpp @@ -1211,8 +1211,8 @@ QByteArray ba(1 + int(%2), char(0)); // @snippet qcryptographichash-adddata // @snippet qsocketnotifier -Shiboken::AutoDecRef socket(%PYARG_1); -if (!socket.isNull()) { +PyObject *socket = %PYARG_1; +if (socket != nullptr) { // We use qintptr as PyLong, but we check for int // since it is currently an alias to be Python2 compatible. // Internally, ints are qlonglongs. @@ -1340,18 +1340,17 @@ if (!PyTuple_SetItem(empty, 0, PyList_New(0))) { // @snippet qcoreapplication-2 // @snippet qcoreapplication-instance -QCoreApplication *app = QCoreApplication::instance(); PyObject *pyApp = Py_None; -if (app) { +if (qApp) { pyApp = reinterpret_cast<PyObject*>( - Shiboken::BindingManager::instance().retrieveWrapper(app)); + Shiboken::BindingManager::instance().retrieveWrapper(qApp)); if (!pyApp) - pyApp = %CONVERTTOPYTHON[QCoreApplication*](app); + pyApp = %CONVERTTOPYTHON[QCoreApplication*](qApp); // this will keep app live after python exit (extra ref) } // PYSIDE-571: make sure that we return the singleton "None" if (pyApp == Py_None) - Py_DECREF(MakeSingletonQAppWrapper(0)); // here qApp and instance() diverge + Py_DECREF(MakeSingletonQAppWrapper(nullptr)); // here qApp and instance() diverge %PYARG_0 = pyApp; Py_XINCREF(%PYARG_0); // @snippet qcoreapplication-instance diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp index 6e4a3efd4..fff28a9e7 100644 --- a/sources/pyside2/libpyside/pyside.cpp +++ b/sources/pyside2/libpyside/pyside.cpp @@ -94,6 +94,31 @@ void init(PyObject *module) SignalManager::instance(); } +static bool _setProperty(PyObject* qObj, PyObject *name, PyObject *value, bool *accept) +{ + QByteArray propName(Shiboken::String::toCString(name)); + propName[0] = std::toupper(propName[0]); + propName.prepend("set"); + + Shiboken::AutoDecRef propSetter(PyObject_GetAttrString(qObj, propName.constData())); + if (!propSetter.isNull()) { + *accept = true; + Shiboken::AutoDecRef args(PyTuple_Pack(1, value)); + Shiboken::AutoDecRef retval(PyObject_CallObject(propSetter, args)); + if (retval.isNull()) + return false; + } else { + PyErr_Clear(); + Shiboken::AutoDecRef attr(PyObject_GenericGetAttr(qObj, name)); + if (PySide::Property::checkType(attr)) { + *accept = true; + if (PySide::Property::setValue(reinterpret_cast<PySideProperty*>(attr.object()), qObj, value) < 0) + return false; + } + } + return true; +} + bool fillQtProperties(PyObject* qObj, const QMetaObject* metaObj, PyObject* kwds, const char** blackList, unsigned int blackListSize) { @@ -103,28 +128,27 @@ bool fillQtProperties(PyObject* qObj, const QMetaObject* metaObj, PyObject* kwds while (PyDict_Next(kwds, &pos, &key, &value)) { if (!blackListSize || !std::binary_search(blackList, blackList + blackListSize, std::string(Shiboken::String::toCString(key)))) { QByteArray propName(Shiboken::String::toCString(key)); + bool accept = false; if (metaObj->indexOfProperty(propName) != -1) { - propName[0] = std::toupper(propName[0]); - propName.prepend("set"); - - Shiboken::AutoDecRef propSetter(PyObject_GetAttrString(qObj, propName.constData())); - if (!propSetter.isNull()) { - Shiboken::AutoDecRef args(PyTuple_Pack(1, value)); - Shiboken::AutoDecRef retval(PyObject_CallObject(propSetter, args)); - } else { - PyObject* attr = PyObject_GenericGetAttr(qObj, key); - if (PySide::Property::checkType(attr)) - PySide::Property::setValue(reinterpret_cast<PySideProperty*>(attr), qObj, value); - } + if (!_setProperty(qObj, key, value, &accept)) + return false; } else { propName.append("()"); if (metaObj->indexOfSignal(propName) != -1) { + accept = true; propName.prepend('2'); - PySide::Signal::connect(qObj, propName, value); - } else { - PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal", propName.constData()); + if (!PySide::Signal::connect(qObj, propName, value)) + return false; + } + } + if (!accept) { + // PYSIDE-1019: Allow any existing attribute in the constructor. + if (!_setProperty(qObj, key, value, &accept)) return false; - }; + } + if (!accept) { + PyErr_Format(PyExc_AttributeError, "'%S' is not a Qt property or a signal", key); + return false; } } } diff --git a/sources/pyside2/tests/QtWidgets/application_test.py b/sources/pyside2/tests/QtWidgets/application_test.py index bd0f94125..0b8f73cd6 100644 --- a/sources/pyside2/tests/QtWidgets/application_test.py +++ b/sources/pyside2/tests/QtWidgets/application_test.py @@ -31,19 +31,25 @@ import unittest from testbinding import TestObject from PySide2.QtWidgets import QApplication +from PySide2 import __all__ as all class QApplicationInstance(unittest.TestCase): def appDestroyed(self): - sefl.assertTrue(False) + self.assertTrue(False) def testInstanceObject(self): + self.assertEqual(type(qApp), type(None)) TestObject.createApp() app1 = QApplication.instance() app2 = QApplication.instance() app1.setObjectName("MyApp") self.assertEqual(app1, app2) self.assertEqual(app2.objectName(), app1.objectName()) + if len(all) > 3: + # an import triggers qApp initialization + __import__("PySide2." + all[-1]) + self.assertEqual(app1, qApp) app1.destroyed.connect(self.appDestroyed) if __name__ == '__main__': diff --git a/sources/pyside2/tests/pysidetest/CMakeLists.txt b/sources/pyside2/tests/pysidetest/CMakeLists.txt index cb8ba04cf..3c993cf4e 100644 --- a/sources/pyside2/tests/pysidetest/CMakeLists.txt +++ b/sources/pyside2/tests/pysidetest/CMakeLists.txt @@ -118,6 +118,7 @@ target_link_libraries(testbinding add_dependencies(testbinding pyside2 QtCore QtGui QtWidgets pysidetest) create_generator_target(testbinding) +PYSIDE_TEST(constructor_properties_test.py) PYSIDE_TEST(decoratedslot_test.py) # Will always crash when built against Qt 5.6, no point in running it. if (Qt5Core_VERSION VERSION_GREATER 5.7.0) diff --git a/sources/pyside2/tests/pysidetest/constructor_properties_test.py b/sources/pyside2/tests/pysidetest/constructor_properties_test.py new file mode 100644 index 000000000..48e2a7aae --- /dev/null +++ b/sources/pyside2/tests/pysidetest/constructor_properties_test.py @@ -0,0 +1,64 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## 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-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import unittest + +from helper import UsesQApplication +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QApplication, QLabel, QFrame + + +class ConstructorPropertiesTest(UsesQApplication): + + def testCallConstructor(self): + label = QLabel( + frameStyle=QFrame.Panel | QFrame.Sunken, + text="first line\nsecond line", + alignment=Qt.AlignBottom | Qt.AlignRight + ) + self.assertRaises(AttributeError, lambda: QLabel( + somethingelse=42, + text="first line\nsecond line", + alignment=Qt.AlignBottom | Qt.AlignRight + )) + + +if __name__ == '__main__': + unittest.main() + diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index c55dba973..6cc8cd583 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -33,8 +33,7 @@ add_library(apiextractor STATIC ${apiextractor_SRC}) target_include_directories(apiextractor PRIVATE ${CLANG_EXTRA_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/parser - ${CMAKE_CURRENT_SOURCE_DIR}/parser/rpp) + ${CMAKE_CURRENT_SOURCE_DIR}/parser) target_link_libraries(apiextractor PUBLIC Qt5::Core) target_link_libraries(apiextractor PRIVATE ${CLANG_EXTRA_LIBRARIES}) diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 67489b151..170a8e1d4 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -1615,11 +1615,11 @@ AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFu metaFunction->setType(translateType(addedFunc->returnType())); - QVector<AddedFunction::TypeInfo> args = addedFunc->arguments(); + const auto &args = addedFunc->arguments(); AbstractMetaArgumentList metaArguments; for (int i = 0; i < args.count(); ++i) { - AddedFunction::TypeInfo& typeInfo = args[i]; + const AddedFunction::TypeInfo& typeInfo = args.at(i).typeInfo; AbstractMetaArgument *metaArg = new AbstractMetaArgument; AbstractMetaType *type = translateType(typeInfo); if (Q_UNLIKELY(!type)) { @@ -1630,6 +1630,8 @@ AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFu return nullptr; } type->decideUsagePattern(); + if (!args.at(i).name.isEmpty()) + metaArg->setName(args.at(i).name); metaArg->setType(type); metaArg->setArgumentIndex(i); metaArg->setDefaultValueExpression(typeInfo.defaultValue); diff --git a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp index 8c443527e..c50084b8e 100644 --- a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp @@ -46,17 +46,21 @@ void TestAddFunction::testParsingFuncNameAndConstness() QCOMPARE(retval.isReference, false); // test with a ugly template as argument and other ugly stuff - const char sig2[] = " _fu__nc_ ( type1, const type2, const Abc<int& , C<char*> * > * *, const type3* const ) const "; + const char sig2[] = " _fu__nc_ ( type1, const type2, const Abc<int& , C<char*> * > * *@my_name@, const type3* const ) const "; AddedFunction f2(QLatin1String(sig2), QLatin1String("const Abc<int& , C<char*> * > * *")); QCOMPARE(f2.name(), QLatin1String("_fu__nc_")); - QVector< AddedFunction::TypeInfo > args = f2.arguments(); + const auto &args = f2.arguments(); QCOMPARE(args.count(), 4); retval = f2.returnType(); QCOMPARE(retval.name, QLatin1String("Abc<int& , C<char*> * >")); QCOMPARE(retval.indirections, 2); QCOMPARE(retval.isConstant, true); QCOMPARE(retval.isReference, false); - retval = args[2]; + retval = args.at(2).typeInfo; + QVERIFY(args.at(0).name.isEmpty()); + QVERIFY(args.at(1).name.isEmpty()); + QCOMPARE(args.at(2).name, QLatin1String("my_name")); + QVERIFY(args.at(3).name.isEmpty()); QCOMPARE(retval.name, QLatin1String("Abc<int& , C<char*> * >")); QCOMPARE(retval.indirections, 2); QCOMPARE(retval.isConstant, true); diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 434134be9..344313e87 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -3279,7 +3279,9 @@ QString FunctionModification::toString() const return str; } -static AddedFunction::TypeInfo parseType(const QString& signature, int startPos = 0, int* endPos = 0) +static AddedFunction::TypeInfo parseType(const QString& signature, + int startPos = 0, int *endPos = nullptr, + QString *argumentName = nullptr) { AddedFunction::TypeInfo result; static const QRegularExpression regex(QLatin1String("\\w")); @@ -3330,6 +3332,19 @@ static AddedFunction::TypeInfo parseType(const QString& signature, int startPos paramString.remove(0, sizeof("const")/sizeof(char)); paramString = paramString.trimmed(); } + + // Extract argument name from "T<bla,blub>* @foo@" + const int nameStartPos = paramString.indexOf(QLatin1Char('@')); + if (nameStartPos != -1) { + const int nameEndPos = paramString.indexOf(QLatin1Char('@'), nameStartPos + 1); + if (nameEndPos > nameStartPos) { + if (argumentName) + *argumentName = paramString.mid(nameStartPos + 1, nameEndPos - nameStartPos - 1); + paramString.remove(nameStartPos, nameEndPos - nameStartPos + 1); + paramString = paramString.trimmed(); + } + } + // check reference if (paramString.endsWith(QLatin1Char('&'))) { result.isReference = true; @@ -3364,9 +3379,10 @@ AddedFunction::AddedFunction(QString signature, const QString &returnType) : m_name = signature.left(endPos).trimmed(); int signatureLength = signature.length(); while (endPos < signatureLength) { - TypeInfo arg = parseType(signature, endPos, &endPos); + QString argumentName; + TypeInfo arg = parseType(signature, endPos, &endPos, &argumentName); if (!arg.name.isEmpty()) - m_arguments.append(arg); + m_arguments.append({argumentName, arg}); // end of parameters... if (signature[endPos] == QLatin1Char(')')) break; @@ -3512,6 +3528,19 @@ QDebug operator<<(QDebug d, const AddedFunction::TypeInfo &ti) return d; } +QDebug operator<<(QDebug d, const AddedFunction::Argument &a) +{ + QDebugStateSaver saver(d); + d.noquote(); + d.nospace(); + d << "Argument("; + d << a.typeInfo; + if (!a.name.isEmpty()) + d << ' ' << a.name; + d << ')'; + return d; +} + QDebug operator<<(QDebug d, const AddedFunction &af) { QDebugStateSaver saver(d); diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h index 2e4578a1d..82a698107 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.h +++ b/sources/shiboken2/ApiExtractor/typesystem.h @@ -424,6 +424,12 @@ struct AddedFunction bool isReference = false; }; + struct Argument + { + QString name; + TypeInfo typeInfo; + }; + /// Creates a new AddedFunction with a signature and a return type. explicit AddedFunction(QString signature, const QString &returnType); AddedFunction() = default; @@ -453,7 +459,7 @@ struct AddedFunction } /// Returns a list of argument type infos. - QVector<TypeInfo> arguments() const + const QVector<Argument> &arguments() const { return m_arguments; } @@ -480,7 +486,7 @@ struct AddedFunction private: QString m_name; - QVector<TypeInfo> m_arguments; + QVector<Argument> m_arguments; TypeInfo m_returnType; Access m_access = Protected; bool m_isConst = false; @@ -489,6 +495,7 @@ private: #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const AddedFunction::TypeInfo &ti); +QDebug operator<<(QDebug d, const AddedFunction::Argument &a); QDebug operator<<(QDebug d, const AddedFunction &af); #endif diff --git a/sources/shiboken2/doc/typesystem_manipulating_objects.rst b/sources/shiboken2/doc/typesystem_manipulating_objects.rst index 12b866ad7..c04a4fa27 100644 --- a/sources/shiboken2/doc/typesystem_manipulating_objects.rst +++ b/sources/shiboken2/doc/typesystem_manipulating_objects.rst @@ -157,6 +157,13 @@ add-function The ``since`` attribute specify the API version when this function was added. + Within the signature, names for the function parameters can be specified by + enclosing them within the delimiter *@*: + + .. code-block:: c++ + + void foo(int @parameter1@,float) + .. _conversion-rule-on-types: conversion-rule diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp index fe03d3489..49064a6a5 100644 --- a/sources/shiboken2/generator/generator.cpp +++ b/sources/shiboken2/generator/generator.cpp @@ -219,9 +219,12 @@ void Generator::addInstantiatedContainersAndSmartPointers(const AbstractMetaType const AbstractMetaTypeList &instantiations = type->instantiations(); for (const AbstractMetaType* t : instantiations) addInstantiatedContainersAndSmartPointers(t, context); - if (!type->typeEntry()->isContainer() && !type->typeEntry()->isSmartPointer()) + const auto typeEntry = type->typeEntry(); + const bool isContainer = typeEntry->isContainer(); + if (!isContainer + && !(typeEntry->isSmartPointer() && typeEntry->generateCode())) { return; - bool isContainer = type->typeEntry()->isContainer(); + } if (type->hasTemplateChildren()) { QString piece = isContainer ? QStringLiteral("container") : QStringLiteral("smart pointer"); QString warning = @@ -244,6 +247,13 @@ void Generator::addInstantiatedContainersAndSmartPointers(const AbstractMetaType // Is smart pointer. if (!m_d->instantiatedSmartPointerNames.contains(typeName)) { m_d->instantiatedSmartPointerNames.append(typeName); + if (type->isConstant() || type->referenceType() != NoReference) { + // Strip a "const QSharedPtr<Foo> &" or similar to "QSharedPtr<Foo>" (PYSIDE-1016) + auto fixedType = type->copy(); + fixedType->setReferenceType(NoReference); + fixedType->setConstant(false); + type = fixedType; + } m_d->instantiatedSmartPointers.append(type); } } @@ -912,7 +922,6 @@ QString getClassTargetFullName(const AbstractMetaType *metaType, bool includePac QString getFilteredCppSignatureString(QString signature) { - TypeInfo::stripQualifiers(&signature); // for const refs to smart pointers signature.replace(QLatin1String("::"), QLatin1String("_")); signature.replace(QLatin1Char('<'), QLatin1Char('_')); signature.replace(QLatin1Char('>'), QLatin1Char('_')); diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 8ee0a9cf2..c033b8a21 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -3686,14 +3686,29 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const TypeEn } s << INDENT << "Shiboken::Enum::setTypeConverter(" << enumPythonType << ", converter);" << endl; - QStringList cppSignature = enumType->qualifiedCppName().split(QLatin1String("::"), QString::SkipEmptyParts); - while (!cppSignature.isEmpty()) { - QString signature = cppSignature.join(QLatin1String("::")); - s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \""; - if (flags) - s << "QFlags<"; - s << signature << "\");" << endl; - cppSignature.removeFirst(); + + QString signature = enumType->qualifiedCppName(); + // Replace "QFlags<Class::Option>" by "Class::Options" + if (flags && signature.startsWith(QLatin1String("QFlags<")) && signature.endsWith(QLatin1Char('>'))) { + signature.chop(1); + signature.remove(0, 7); + const int lastQualifierPos = signature.lastIndexOf(QLatin1String("::")); + if (lastQualifierPos != -1) { + signature.replace(lastQualifierPos + 2, signature.size() - lastQualifierPos - 2, + flags->flagsName()); + } else { + signature = flags->flagsName(); + } + } + + while (true) { + s << INDENT << "Shiboken::Conversions::registerConverterName(converter, \"" + << signature << "\");\n"; + const int qualifierPos = signature.indexOf(QLatin1String("::")); + if (qualifierPos != -1) + signature.remove(0, qualifierPos + 2); + else + break; } } s << INDENT << '}' << endl; @@ -5806,7 +5821,7 @@ bool CppGenerator::finishGeneration() if (usePySideExtensions()) { // initialize the qApp module. - s << INDENT << "NotifyModuleForQApp(module);" << endl; + s << INDENT << "NotifyModuleForQApp(module, qApp);" << endl; } s << endl; s << "SBK_MODULE_INIT_FUNCTION_END" << endl; diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.cpp b/sources/shiboken2/generator/shiboken2/headergenerator.cpp index 17ebbcde9..a55539d7c 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/headergenerator.cpp @@ -593,10 +593,8 @@ void HeaderGenerator::writeSbkTypeFunction(QTextStream& s, const AbstractMetaCla void HeaderGenerator::writeSbkTypeFunction(QTextStream &s, const AbstractMetaType *metaType) { - QString signature = metaType->cppSignature(); - TypeInfo::stripQualifiers(&signature); // for const refs to smart pointers - s << "template<> inline PyTypeObject *SbkType< ::" << signature << " >() " - << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(metaType) << "); }\n"; + s << "template<> inline PyTypeObject* SbkType< ::" << metaType->cppSignature() << " >() " + << "{ return reinterpret_cast<PyTypeObject*>(" << cpythonTypeNameExt(metaType) << "); }\n"; } void HeaderGenerator::writeInheritedOverloads(QTextStream& s) diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 5d599fe95..2b3b20c75 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -2681,11 +2681,8 @@ QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry* type) QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType* type) { QString result = QLatin1String("SBK"); - const auto *typeEntry = type->typeEntry(); - if (typeEntry->isContainer() - || typeEntry->isSmartPointer()) { // PYSIDE-1024 + if (type->typeEntry()->isContainer()) result += QLatin1Char('_') + moduleName().toUpper(); - } result += processInstantiationsVariableName(type); appendIndexSuffix(&result); return result; diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index faaca5e4b..f38b55b56 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -76,11 +76,21 @@ extern "C" static void SbkObjectTypeDealloc(PyObject* pyObj); static PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* kwds); +// PYSIDE-908: The function PyType_Modified does not work in PySide, so we need to +// explicitly pass __doc__. For __signature__ it _did_ actually work, because +// it was not existing before. We add them both for clarity. +static PyGetSetDef SbkObjectType_Type_getsetlist[] = { + {const_cast<char*>("__signature__"), (getter)Sbk_TypeGet___signature__}, + {const_cast<char*>("__doc__"), (getter)Sbk_TypeGet___doc__}, + {nullptr} // Sentinel +}; + static PyType_Slot SbkObjectType_Type_slots[] = { {Py_tp_dealloc, (void *)SbkObjectTypeDealloc}, {Py_tp_setattro, (void *)PyObject_GenericSetAttr}, {Py_tp_base, (void *)&PyType_Type}, {Py_tp_alloc, (void *)PyType_GenericAlloc}, + {Py_tp_getset, (void *)SbkObjectType_Type_getsetlist}, {Py_tp_new, (void *)SbkObjectTypeTpNew}, {Py_tp_free, (void *)PyObject_GC_Del}, {0, 0} diff --git a/sources/shiboken2/libshiboken/qapp_macro.cpp b/sources/shiboken2/libshiboken/qapp_macro.cpp index 19e985b20..ea9cf0c86 100644 --- a/sources/shiboken2/libshiboken/qapp_macro.cpp +++ b/sources/shiboken2/libshiboken/qapp_macro.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "basewrapper.h" +#include "autodecref.h" extern "C" { @@ -93,13 +94,14 @@ static int qApp_var_ref = 0; static int qApp_content_ref = 0; static int -reset_qApp_var() +reset_qApp_var(void) { PyObject **mod_ptr; - for (mod_ptr = qApp_moduledicts; *mod_ptr != NULL; mod_ptr++) { + for (mod_ptr = qApp_moduledicts; *mod_ptr != nullptr; mod_ptr++) { // We respect whatever the user may have set. - if (PyDict_GetItem(*mod_ptr, qApp_var) == NULL) { + PyObject *existing = PyDict_GetItem(*mod_ptr, qApp_var); + if (existing == nullptr || Py_TYPE(existing) == Py_NONE_TYPE) { if (PyDict_SetItem(*mod_ptr, qApp_var, qApp_content) < 0) return -1; } @@ -135,8 +137,13 @@ MakeSingletonQAppWrapper(PyTypeObject *type) if (Py_REFCNT(qApp_content) > qApp_content_ref) qApp_content_ref = Py_REFCNT(qApp_content); - if (Py_TYPE(qApp_content) != Py_NONE_TYPE) + if (Py_TYPE(qApp_content) != Py_NONE_TYPE) { + // Remove the "_" variable which might hold a reference to qApp. + Shiboken::AutoDecRef pymain(PyImport_ImportModule("__main__")); + if (pymain.object() && PyObject_HasAttrString(pymain.object(), "_")) + PyObject_DelAttrString(pymain.object(), "_"); Py_REFCNT(qApp_var) = 1; // fuse is armed... + } if (type == Py_NONE_TYPE) { // Debug mode showed that we need to do more than just remove the // reference. To keep everything in the right order, it is easiest @@ -149,8 +156,8 @@ MakeSingletonQAppWrapper(PyTypeObject *type) Py_TYPE(qApp_content) = Py_NONE_TYPE; Py_REFCNT(qApp_var) = qApp_var_ref; Py_REFCNT(qApp_content) = Py_REFCNT(Py_None); - if (__moduleShutdown != NULL) - Py_DECREF(PyObject_CallFunction(__moduleShutdown, (char *)"()")); + if (__moduleShutdown != nullptr) + Py_XDECREF(PyObject_CallFunction(__moduleShutdown, const_cast<char *>("()"))); } else (void)PyObject_INIT(qApp_content, type); @@ -216,9 +223,29 @@ setup_qApp_var(PyObject *module) } void -NotifyModuleForQApp(PyObject *module) +NotifyModuleForQApp(PyObject *module, void *qApp) { setup_qApp_var(module); + /* + * PYSIDE-571: Check if an QApplication instance exists before the import. + * This happens in scriptableapplication and application_test.py . + * + * Crucial Observation + * =================== + * + * A Q*Application object from C++ does not have a wrapper or constructor + * like instances created by Python. It makes no sense to support + * deletion or special features like qApp resurrection. + * + * Therefore, the implementation is very simple and just redirects the + * qApp_contents variable and assigns the instance, instead of vice-versa. + */ + if (qApp != nullptr) { + Shiboken::AutoDecRef pycore(PyImport_ImportModule("PySide2.QtCore")); + Shiboken::AutoDecRef coreapp(PyObject_GetAttrString(pycore, "QCoreApplication")); + qApp_content = PyObject_CallMethod(coreapp, "instance", ""); + reset_qApp_var(); + } } diff --git a/sources/shiboken2/libshiboken/qapp_macro.h b/sources/shiboken2/libshiboken/qapp_macro.h index ded892383..be45241de 100644 --- a/sources/shiboken2/libshiboken/qapp_macro.h +++ b/sources/shiboken2/libshiboken/qapp_macro.h @@ -46,7 +46,7 @@ extern "C" { LIBSHIBOKEN_API PyObject *MakeSingletonQAppWrapper(PyTypeObject *type); -LIBSHIBOKEN_API void NotifyModuleForQApp(PyObject *module); +LIBSHIBOKEN_API void NotifyModuleForQApp(PyObject *module, void *qApp); } // extern "C" diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index c83f90d64..8003f142a 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -1180,4 +1180,24 @@ SetError_Argument(PyObject *args, const char *func_name) PyErr_SetObject(err, msg); } +/* + * Support for the metatype SbkObjectType_Type's tp_getset. + * + * This was not necessary for __signature__, because PyType_Type inherited it. + * But the __doc__ attribute existed already by inheritance, and calling + * PyType_Modified() is not supported. So we added the getsets explicitly + * to the metatype. + */ + +PyObject * +Sbk_TypeGet___signature__(PyObject *ob, const char *modifier) +{ + return pyside_tp_get___signature__(ob, modifier); +} + +PyObject *Sbk_TypeGet___doc__(PyObject *ob) +{ + return pyside_tp_get___doc__(ob); +} + } //extern "C" diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h index 6b477c52e..57fd4047a 100644 --- a/sources/shiboken2/libshiboken/signature.h +++ b/sources/shiboken2/libshiboken/signature.h @@ -48,6 +48,8 @@ extern "C" LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char *[]); LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *[]); LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *); +LIBSHIBOKEN_API PyObject *Sbk_TypeGet___signature__(PyObject *, const char *); +LIBSHIBOKEN_API PyObject *Sbk_TypeGet___doc__(PyObject *); } // extern "C" diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 306103304..5f92446cf 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -208,25 +208,20 @@ class Reloader(object): This is a singleton class which provides the update function for the shiboken and PySide classes. """ - _uninitialized = "Shiboken minimal sample other smart".split() - _prefixes = [""] - try: - import PySide2 - _uninitialized += PySide2.__all__ + ["testbinding"] - _prefixes += ["PySide2."] - except ImportError: - pass - def __init__(self): self.sys_module_count = 0 - self.uninitialized = self._uninitialized + + @staticmethod + def module_valid(mod): + if getattr(mod, "__file__", None) and not os.path.isdir(mod.__file__): + ending = os.path.splitext(mod.__file__)[-1] + return ending not in (".py", ".pyc", ".pyo", ".pyi") + return False def update(self): """ - update is responsible to import all modules from shiboken and PySide - which are already in sys.modules. - The purpose is to follow all user imports without introducing new - ones. + 'update' imports all binary modules which are already in sys.modules. + The reason is to follow all user imports without introducing new ones. This function is called by pyside_type_init to adapt imports when the number of imported modules has changed. """ @@ -234,37 +229,32 @@ class Reloader(object): return self.sys_module_count = len(sys.modules) g = globals() - for mod_name in self.uninitialized[:]: - for prefix in self._prefixes: - import_name = prefix + mod_name - if import_name in sys.modules: - # check if this is a real module - check_module(sys.modules[import_name]) - # module is real - self.uninitialized.remove(mod_name) - proc_name = "init_" + mod_name - if proc_name in g: - # Do the 'import {import_name}' first. - # 'top' is PySide2 when we do 'import PySide.QtCore' - # or Shiboken if we do 'import Shiboken'. - # Convince yourself that these two lines below have the same - # global effect as "import Shiboken" or "import PySide2.QtCore". - top = __import__(import_name) - g[top.__name__] = top - # Modules are in place, we can update the type_map. - g.update(g[proc_name]()) + # PYSIDE-1009: Try to recognize unknown modules in errorhandler.py + candidates = list(mod_name for mod_name in sys.modules + if self.module_valid(sys.modules[mod_name])) + for mod_name in candidates: + # 'top' is PySide2 when we do 'import PySide.QtCore' + # or Shiboken if we do 'import Shiboken'. + # Convince yourself that these two lines below have the same + # global effect as "import Shiboken" or "import PySide2.QtCore". + top = __import__(mod_name) + g[top.__name__] = top + proc_name = "init_" + mod_name.replace(".", "_") + if proc_name in g: + # Modules are in place, we can update the type_map. + g.update(g.pop(proc_name)()) + def check_module(mod): # During a build, there exist the modules already as directories, # although the '*.so' was not yet created. This causes a problem # in Python 3, because it accepts folders as namespace modules # without enforcing an '__init__.py'. - if not getattr(mod, "__file__", None) or os.path.isdir(mod.__file__): + if not Reloader.module_valid(mod): mod_name = mod.__name__ - raise ImportError("Module '{mod_name}' is at most a namespace!" + raise ImportError("Module '{mod_name}' is not a binary module!" .format(**locals())) - update_mapping = Reloader().update type_map = {} namespace = globals() # our module's __dict__ @@ -274,6 +264,7 @@ type_map.update({ "QVector": typing.List, "QSet": typing.Set, "QPair": Pair, + "QMap": typing.Dict, }) @@ -348,7 +339,7 @@ def init_smart(): return locals() # The PySide Part -def init_QtCore(): +def init_PySide2_QtCore(): from PySide2.QtCore import Qt, QUrl, QDir from PySide2.QtCore import QRect, QSize, QPoint, QLocale, QByteArray from PySide2.QtCore import QMarginsF # 5.9 @@ -401,7 +392,6 @@ def init_QtCore(): "list of QAbstractState": typing.List[PySide2.QtCore.QAbstractState], "list of QAbstractAnimation": typing.List[PySide2.QtCore.QAbstractAnimation], "QVariant()": Invalid(Variant), - "QMap": typing.Dict, "PySide2.QtCore.bool": bool, "QHash": typing.Dict, "PySide2.QtCore.QChar": Char, @@ -495,7 +485,7 @@ def init_QtCore(): return locals() -def init_QtGui(): +def init_PySide2_QtGui(): from PySide2.QtGui import QPageLayout, QPageSize # 5.12 macOS type_map.update({ "QVector< QTextLayout.FormatRange >()": [], # do we need more structure? @@ -525,7 +515,7 @@ def init_QtGui(): return locals() -def init_QtWidgets(): +def init_PySide2_QtWidgets(): from PySide2.QtWidgets import QWidget, QMessageBox, QStyleOption, QStyleHintReturn, QStyleOptionComplex from PySide2.QtWidgets import QGraphicsItem, QStyleOptionGraphicsItem # 5.9 type_map.update({ @@ -557,7 +547,7 @@ def init_QtWidgets(): return locals() -def init_QtSql(): +def init_PySide2_QtSql(): from PySide2.QtSql import QSqlDatabase type_map.update({ "QLatin1String(defaultConnection)": QSqlDatabase.defaultConnection, @@ -566,7 +556,7 @@ def init_QtSql(): return locals() -def init_QtNetwork(): +def init_PySide2_QtNetwork(): type_map.update({ "QMultiMap": MultiMap, "zero(unsigned short)": 0, @@ -576,7 +566,7 @@ def init_QtNetwork(): return locals() -def init_QtXmlPatterns(): +def init_PySide2_QtXmlPatterns(): from PySide2.QtXmlPatterns import QXmlName type_map.update({ "QXmlName.PrefixCode": Missing("PySide2.QtXmlPatterns.QXmlName.PrefixCode"), @@ -585,7 +575,7 @@ def init_QtXmlPatterns(): return locals() -def init_QtMultimedia(): +def init_PySide2_QtMultimedia(): import PySide2.QtMultimediaWidgets # Check if foreign import is valid. See mapping.py in shiboken2. check_module(PySide2.QtMultimediaWidgets) @@ -596,7 +586,7 @@ def init_QtMultimedia(): return locals() -def init_QtOpenGL(): +def init_PySide2_QtOpenGL(): type_map.update({ "GLuint": int, "GLenum": int, @@ -612,7 +602,7 @@ def init_QtOpenGL(): return locals() -def init_QtQml(): +def init_PySide2_QtQml(): type_map.update({ "QJSValueList()": [], "PySide2.QtQml.bool volatile": bool, @@ -624,7 +614,7 @@ def init_QtQml(): return locals() -def init_QtQuick(): +def init_PySide2_QtQuick(): type_map.update({ "PySide2.QtQuick.QSharedPointer": int, "PySide2.QtCore.uint": int, @@ -635,35 +625,35 @@ def init_QtQuick(): return locals() -def init_QtScript(): +def init_PySide2_QtScript(): type_map.update({ "QScriptValueList()": [], }) return locals() -def init_QtTest(): +def init_PySide2_QtTest(): type_map.update({ "PySide2.QtTest.QTouchEventSequence": PySide2.QtTest.QTest.QTouchEventSequence, }) return locals() # from 5.9 -def init_QtWebEngineWidgets(): +def init_PySide2_QtWebEngineWidgets(): type_map.update({ "zero(PySide2.QtWebEngineWidgets.QWebEnginePage.FindFlags)": 0, }) return locals() # from 5.6, MSVC -def init_QtWinExtras(): +def init_PySide2_QtWinExtras(): type_map.update({ "QList< QWinJumpListItem* >()": [], }) return locals() # from 5.12, macOS -def init_QtDataVisualization(): +def init_PySide2_QtDataVisualization(): from PySide2.QtDataVisualization import QtDataVisualization QtDataVisualization.QBarDataRow = typing.List[QtDataVisualization.QBarDataItem] QtDataVisualization.QBarDataArray = typing.List[QtDataVisualization.QBarDataRow] |