diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-01-14 12:30:48 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-01-14 12:30:50 +0100 |
commit | 317eb0ad107d056f4af097ff94d99233009c393d (patch) | |
tree | f4b8aaa87f49dce41400b6a05980ad8cd434728b | |
parent | 907ff205912cc8d8d0a09c8096f5089dae5eeca9 (diff) | |
parent | 2fc25e72b289b0f8e10110ffa886cad864d3147c (diff) |
Merge remote-tracking branch 'origin/5.12' into dev
Change-Id: Id589406a8107c8e24942f072356e5e6f9e5b051b
33 files changed, 465 insertions, 310 deletions
diff --git a/README.pyside2.md b/README.pyside2.md index 53f7bc9d0..f1bd8aaa4 100644 --- a/README.pyside2.md +++ b/README.pyside2.md @@ -2,11 +2,11 @@ ### Introduction -PySide is the [Python Qt bindings project](http://wiki.qt.io/Qt_for_Python), -providing access to the complete Qt 5.12+ framework as well as to generator -tools for rapidly generating Python bindings for any C++ libraries. +PySide2 is the official Python module from the +[Qt for Python project](http://wiki.qt.io/Qt_for_Python), +which provides access to the complete Qt 5.12+ framework. -The PySide project is developed in the open, with all facilities you'd expect +The Qt for Python project is developed in the open, with all facilities you'd expect from any modern OSS project such as all code in a git repository and an open design process. We welcome any contribution conforming to the [Qt Contribution Agreement](https://www.qt.io/contributionagreement/). @@ -21,7 +21,7 @@ and [PyPi](https://pypi.org/project/PySide2/): #### Dependencies -PySide versions following 5.12 use a C++ parser based on +PySide2 versions following 5.12 use a C++ parser based on [Clang](http://clang.org/). The Clang library (C-bindings), version 6.0 or higher is required for building. Prebuilt versions of it can be downloaded from [download.qt.io](http://download.qt.io/development_releases/prebuilt/libclang/). @@ -51,7 +51,7 @@ This process will include getting the code: then install the dependencies, and following the instructions per platform. A common build command will look like: - python setup.py install --qmake=<path/to/qmake/> --jobs=8 --build-tests + python setup.py install --qmake=<path/to/qmake/> --parallel=8 --build-tests You can obtain more information about the options to build PySide and Shiboken in [our wiki](https://wiki.qt.io/Qt_for_Python/). diff --git a/README.shiboken2-generator.md b/README.shiboken2-generator.md index f29f40634..a7fd32244 100644 --- a/README.shiboken2-generator.md +++ b/README.shiboken2-generator.md @@ -1,4 +1,4 @@ -# shiboken2-generator +# Shiboken2-generator Shiboken is the generator used by the Qt for Python project. It outputs C++ code for CPython extensions, which can be compiled diff --git a/README.shiboken2.md b/README.shiboken2.md index d9cd32a40..3d92f2a46 100644 --- a/README.shiboken2.md +++ b/README.shiboken2.md @@ -1,7 +1,7 @@ -# shiboken2 module +# Shiboken2 module -The purpose of the shiboken2 Python module is to access information -related to the binding generation that could be used to integrate +The purpose of the [shiboken2 Python module](https://wiki.qt.io/Qt_for_Python/Shiboken) +is to access information related to the binding generation that could be used to integrate C++ programs to Python, or even to get useful information to debug an application. diff --git a/build_scripts/config.py b/build_scripts/config.py index f47230a6d..8356be302 100644 --- a/build_scripts/config.py +++ b/build_scripts/config.py @@ -125,7 +125,7 @@ class Config(object): setup_kwargs = {} setup_kwargs['long_description'] = self.get_long_description() - setup_kwargs['long_description_content_type'] = 'text/markdown', + setup_kwargs['long_description_content_type'] = 'text/markdown' setup_kwargs['keywords'] = 'Qt' setup_kwargs['author'] = 'Qt for Python Team' setup_kwargs['author_email'] = 'pyside@qt-project.org' @@ -207,8 +207,7 @@ class Config(object): elif self.internal_build_type == self.pyside_option_name: setup_kwargs['name'] = self.pyside_st_name - setup_kwargs['description'] = ("Python bindings for the Qt cross-platform application" - " and UI framework"), + setup_kwargs['description'] = "Python bindings for the Qt cross-platform application and UI framework" setup_kwargs['install_requires'] = [self.shiboken_module_st_name] setup_kwargs['entry_points'] = { 'console_scripts': [ diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml index c70049d8e..7531b33b8 100644 --- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml +++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml @@ -1210,6 +1210,11 @@ </add-function> </value-type> <value-type name="QPersistentModelIndex" hash-function="qHash"> + <modify-function signature="internalPointer()const"> + <inject-code class="target" position="beginning"> + <insert-template name="return_internal_pointer" /> + </inject-code> + </modify-function> <modify-function signature="operator const QModelIndex&()const"> <modify-argument index="return"> <parent index="this" action="add"/> @@ -1460,12 +1465,38 @@ <object-type name="QAbstractItemModel"> <enum-type name="CheckIndexOption" flags="CheckIndexOptions" since="5.11"/> <enum-type name="LayoutChangeHint"/> + <!-- This function was replaced by a added function --> + <modify-function signature="createIndex(int,int,void*)const" remove="all"/> <!-- This function is the same as createIndex(int, int, int)const --> <modify-function signature="createIndex(int,int,quintptr)const"> <modify-argument index="3"> <replace-default-expression with="0"/> </modify-argument> </modify-function> + <add-function signature="createIndex(int,int,PyObject*)const" return-type="QModelIndex"> + <modify-argument index="1"> + <rename to="row"/> + </modify-argument> + <modify-argument index="2"> + <rename to="column"/> + </modify-argument> + <modify-argument index="3"> + <rename to="ptr"/> + </modify-argument> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qabstractitemmodel-createindex"/> + <inject-documentation mode="append" format="target"> + Creates a model index for the given row and column with the internal pointer ptr. + When using a QSortFilterProxyModel, its indexes have their own internal pointer. + It is not advisable to access this internal pointer outside of the model. + Use the data() function instead. + This function provides a consistent interface that model subclasses must use to create model indexes. + + .. warning:: Because of some Qt/Python itegration rules, the ptr argument do not get the reference + incremented during the QModelIndex life time. So it is necessary to keep the object used + on ptr argument alive during the whole process. + Do not destroy the object if you are not sure about that. + </inject-documentation> + </add-function> <inject-code class="target" position="end" file="../glue/qtcore.cpp" snippet="qabstractitemmodel"/> <modify-function signature="mimeData(QModelIndexList)const"> <modify-argument index="return"> @@ -2774,6 +2805,11 @@ </modify-function> </object-type> <value-type name="QModelIndex" hash-function="qHash"> + <modify-function signature="internalPointer()const"> + <inject-code class="target" position="beginning"> + <insert-template name="return_internal_pointer" /> + </inject-code> + </modify-function> <modify-function signature="model()const"> <modify-argument index="return"> <define-ownership class="target" owner="default"/> diff --git a/sources/pyside2/PySide2/QtQuick/pysidequickregistertype.cpp b/sources/pyside2/PySide2/QtQuick/pysidequickregistertype.cpp index 93a8f281e..e50b1dfc7 100644 --- a/sources/pyside2/PySide2/QtQuick/pysidequickregistertype.cpp +++ b/sources/pyside2/PySide2/QtQuick/pysidequickregistertype.cpp @@ -40,6 +40,7 @@ #include "pysidequickregistertype.h" #include <pyside.h> +#include <pyside_p.h> #include <shiboken.h> // Auto generated headers. @@ -122,7 +123,7 @@ void registerTypeIfInheritsFromClass( PyTypeObject *typeToRegister, const QByteArray &typePointerName, const QByteArray &typeListName, - QMetaObject *typeMetaObject, + const QMetaObject *typeMetaObject, QQmlPrivate::RegisterType *type, bool ®istered) { @@ -199,9 +200,7 @@ bool quickRegisterType(PyObject *pyObj, const char *uri, int versionMajor, int v return false; // Used inside macros to register the type. - QMetaObject *metaObject = - reinterpret_cast<QMetaObject *>( - ObjectType::getTypeUserData(reinterpret_cast<SbkObjectType *>(pyObj))); + const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObj); Q_ASSERT(metaObject); diff --git a/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml b/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml index e6021bfe1..456a411d3 100644 --- a/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml +++ b/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml @@ -660,7 +660,7 @@ <modify-argument index="2"> <reference-count action="set"/> </modify-argument> - <inject-code file="../glue/qtwidgets.cpp" snippet="qitemeditorfactory-registerEditor"/> + <inject-code file="../glue/qtwidgets.cpp" snippet="qitemeditorfactory-registereditor"/> </modify-function> <modify-function signature="setDefaultFactory(QItemEditorFactory*)"> <modify-argument index="1"> diff --git a/sources/pyside2/PySide2/support/signature/mapping.py b/sources/pyside2/PySide2/support/signature/mapping.py index 61fa2d41f..96afd3c7a 100644 --- a/sources/pyside2/PySide2/support/signature/mapping.py +++ b/sources/pyside2/PySide2/support/signature/mapping.py @@ -63,6 +63,7 @@ class Reloader(Sbk_Reloader): Sbk_Reloader.update(self, globals()) update_mapping = Reloader().update +namespace = globals() # our module's __dict__, updated def init_QtCore(): diff --git a/sources/pyside2/PySide2/templates/core_common.xml b/sources/pyside2/PySide2/templates/core_common.xml index 0fe19273b..4f715ee1f 100644 --- a/sources/pyside2/PySide2/templates/core_common.xml +++ b/sources/pyside2/PySide2/templates/core_common.xml @@ -260,6 +260,13 @@ %PYARG_0 = Shiboken::String::fromCString(qPrintable(format)); </template> + <template name="return_internal_pointer"> + %PYARG_0 = reinterpret_cast<PyObject*>(%CPPSELF.%FUNCTION_NAME()); + if (!%PYARG_0) + %PYARG_0 = Py_None; + Py_INCREF(%PYARG_0); + </template> + <!-- Helpers for modifying "bool nativeEventFilter(QByteArray, void*, long *result)" to return a tuple of bool,long --> <template name="return_native_eventfilter_conversion_variables"> diff --git a/sources/pyside2/cmake/Macros/PySideModules.cmake b/sources/pyside2/cmake/Macros/PySideModules.cmake index 98efd8c73..2cbda3975 100644 --- a/sources/pyside2/cmake/Macros/PySideModules.cmake +++ b/sources/pyside2/cmake/Macros/PySideModules.cmake @@ -170,9 +170,9 @@ macro(create_pyside_module) # install install(TARGETS ${module_NAME} LIBRARY DESTINATION "${PYTHON_SITE_PACKAGES}/PySide2") - install(DIRECTORY "${CMAKE_BINARY_DIR}/" DESTINATION "${PYTHON_SITE_PACKAGES}" - OPTIONAL - FILES_MATCHING PATTERN "*.pyi") + file(GLOB hinting_stub_files RELATIVE "${CMAKE_CURRENT_BINARY_DIR}/PySide2" "${CMAKE_CURRENT_BINARY_DIR}/PySide2/*.pyi") + install(FILES ${hinting_stub_files} + DESTINATION "${PYTHON_SITE_PACKAGES}/PySide2") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/PySide2/${module_NAME}/pyside2_${lower_module_name}_python.h DESTINATION include/PySide2${pyside2_SUFFIX}/${module_NAME}/) diff --git a/sources/pyside2/doc/extras/PySide.QtCore.Slot.rst b/sources/pyside2/doc/extras/PySide.QtCore.Slot.rst index 6b93014cf..3bc64c03a 100644 --- a/sources/pyside2/doc/extras/PySide.QtCore.Slot.rst +++ b/sources/pyside2/doc/extras/PySide.QtCore.Slot.rst @@ -7,33 +7,33 @@ Slot Detailed Description -------------------- - PySide adopt PyQt's new signal and slot syntax as-is. The PySide - implementation is functionally compatible with the PyQt 4.5 one, with the - exceptions listed bellow. + PySide2 adopt PyQt5's new signal and slot syntax as-is. The PySide2 + implementation is functionally compatible with the PyQt5 one, with the + exceptions listed below. - PyQt's new signal and slot style utilizes method and decorator names + PyQt5's new signal and slot style utilizes method and decorator names specific to their implementation. These will be generalized according to the table below: - ======= ====================== ============= - Module PyQt factory function PySide class - ======= ====================== ============= - QtCore pyqtSignal Signal - QtCore pyqtSlot Slot - ======= ====================== ============= + ======= ======================= ============= + Module PyQt5 factory function PySide2 class + ======= ======================= ============= + QtCore pyqtSignal Signal + QtCore pyqtSlot Slot + ======= ======================= ============= Q_INVOKABLE ----------- - PySide doesn't offer something identical to Q_INVOKABLE macro of Qt, the - reason is simple, PySide slots can have return values, so if you need to - create a invokable method that returns some value, declare it as a slot, - e.g.: + There is no equivalent of the Q_INVOKABLE macro of Qt + since PySide2 slots can actually have return values. + If you need to create a invokable method that returns some value, + declare it as a slot, e.g.: :: class Foo(QObject): - @Slot(result=int, float) + @Slot(float, result=int) def getFloatReturnInt(self, f): return int(f) diff --git a/sources/pyside2/doc/pyside-config.qdocconf.in b/sources/pyside2/doc/pyside-config.qdocconf.in index e897069ad..3fd567165 100644 --- a/sources/pyside2/doc/pyside-config.qdocconf.in +++ b/sources/pyside2/doc/pyside-config.qdocconf.in @@ -1,6 +1,8 @@ buildversion = @PYSIDE_QT_VERSION@ navigation.homepage = Qt for Python +macro.nullptr = "\\c{None}" + outputdir = @DOC_DATA_DIR@ outputformats = WebXML WebXML.quotinginformation = true diff --git a/sources/pyside2/tests/QtCore/qmodelindex_internalpointer_test.py b/sources/pyside2/tests/QtCore/qmodelindex_internalpointer_test.py index a67bb380a..770600ea3 100644 --- a/sources/pyside2/tests/QtCore/qmodelindex_internalpointer_test.py +++ b/sources/pyside2/tests/QtCore/qmodelindex_internalpointer_test.py @@ -33,7 +33,6 @@ import sys import unittest from PySide2.QtCore import * -from PySide2.support import VoidPtr class MyModel (QAbstractListModel): pass @@ -51,21 +50,20 @@ class TestQModelIndexInternalPointer(unittest.TestCase): def testInternalPointer(self): #Test QAbstractListModel.createIndex and - #QModelIndex.internalPointer + #QModelIndex.internalPointer with regular Python objects obj = QObject() - obj_ptr = VoidPtr(obj) - idx = self.model.createIndex(0, 0, obj) + idx = self.model.createIndex(0, 0, "Hello") i = idx.internalPointer() - self.assertEqual(int(obj_ptr), int(i)) + self.assertEqual(i, "Hello") def testReferenceCounting(self): #Test reference counting when retrieving data with #QModelIndex.internalPointer - o = QObject() + o = [1, 2, 3] o_refcnt = sys.getrefcount(o) idx = self.model.createIndex(0, 0, o) ptr = idx.internalPointer() - self.assertEqual(sys.getrefcount(o), o_refcnt) + self.assertEqual(sys.getrefcount(o), o_refcnt + 1) def testIndexForDefaultDataArg(self): diff --git a/sources/pyside2/tests/registry/exists_darwin_5_12_0_ci.py b/sources/pyside2/tests/registry/exists_darwin_5_12_0_ci.py index a2b6aa14f..7a070abfe 100644 --- a/sources/pyside2/tests/registry/exists_darwin_5_12_0_ci.py +++ b/sources/pyside2/tests/registry/exists_darwin_5_12_0_ci.py @@ -157,7 +157,7 @@ if "PySide2.QtCore" in sys.modules: "QAbstractItemModel.changePersistentIndexList": ('typing.List[int]', 'typing.List[int]'), "QAbstractItemModel.checkIndex": ('PySide2.QtCore.QModelIndex', 'PySide2.QtCore.QAbstractItemModel.CheckIndexOptions'), "QAbstractItemModel.columnCount": ('PySide2.QtCore.QModelIndex',), - "QAbstractItemModel.createIndex": ('int', 'int', 'int'), + "QAbstractItemModel.createIndex": [('int', 'int', 'int'), ('int', 'int', 'object')], "QAbstractItemModel.data": ('PySide2.QtCore.QModelIndex', 'int'), "QAbstractItemModel.decodeData": ('int', 'int', 'PySide2.QtCore.QModelIndex', 'PySide2.QtCore.QDataStream'), "QAbstractItemModel.dropMimeData": ('PySide2.QtCore.QMimeData', 'PySide2.QtCore.Qt.DropAction', 'int', 'int', 'PySide2.QtCore.QModelIndex'), @@ -15202,7 +15202,7 @@ if "PySide2.QtHelp" in sys.modules: "QHelpEngineCore.unregisterDocumentation": ('str',), # class PySide2.QtHelp.QHelpIndexModel: - "QHelpIndexModel.createIndex": [('int', 'int', 'int'), ('str',)], + "QHelpIndexModel.createIndex": [('int', 'int', 'int'), ('int', 'int', 'object'), ('str',)], "QHelpIndexModel.filter": ('str', 'str'), "QHelpIndexModel.isCreatingIndex": (), "QHelpIndexModel.linksForKeyword": ('str',), diff --git a/sources/pyside2/tests/registry/exists_opensuse423_5_12_0_ci.py b/sources/pyside2/tests/registry/exists_opensuse423_5_12_0_ci.py index 5cb8a2306..6caa0947d 100644 --- a/sources/pyside2/tests/registry/exists_opensuse423_5_12_0_ci.py +++ b/sources/pyside2/tests/registry/exists_opensuse423_5_12_0_ci.py @@ -157,7 +157,7 @@ if "PySide2.QtCore" in sys.modules: "QAbstractItemModel.changePersistentIndexList": ('typing.List[int]', 'typing.List[int]'), "QAbstractItemModel.checkIndex": ('PySide2.QtCore.QModelIndex', 'PySide2.QtCore.QAbstractItemModel.CheckIndexOptions'), "QAbstractItemModel.columnCount": ('PySide2.QtCore.QModelIndex',), - "QAbstractItemModel.createIndex": ('int', 'int', 'int'), + "QAbstractItemModel.createIndex": [('int', 'int', 'int'), ('int', 'int', 'object')], "QAbstractItemModel.data": ('PySide2.QtCore.QModelIndex', 'int'), "QAbstractItemModel.decodeData": ('int', 'int', 'PySide2.QtCore.QModelIndex', 'PySide2.QtCore.QDataStream'), "QAbstractItemModel.dropMimeData": ('PySide2.QtCore.QMimeData', 'PySide2.QtCore.Qt.DropAction', 'int', 'int', 'PySide2.QtCore.QModelIndex'), @@ -15213,7 +15213,7 @@ if "PySide2.QtHelp" in sys.modules: "QHelpEngineCore.unregisterDocumentation": ('str',), # class PySide2.QtHelp.QHelpIndexModel: - "QHelpIndexModel.createIndex": [('int', 'int', 'int'), ('str',)], + "QHelpIndexModel.createIndex": [('int', 'int', 'int'), ('int', 'int', 'object'), ('str',)], "QHelpIndexModel.filter": ('str', 'str'), "QHelpIndexModel.isCreatingIndex": (), "QHelpIndexModel.linksForKeyword": ('str',), diff --git a/sources/pyside2/tests/registry/exists_redhatenterpriselinuxworkstation74_5_12_0_ci.py b/sources/pyside2/tests/registry/exists_redhatenterpriselinuxworkstation74_5_12_0_ci.py index 969ac36c8..c7b58c3fb 100644 --- a/sources/pyside2/tests/registry/exists_redhatenterpriselinuxworkstation74_5_12_0_ci.py +++ b/sources/pyside2/tests/registry/exists_redhatenterpriselinuxworkstation74_5_12_0_ci.py @@ -157,7 +157,7 @@ if "PySide2.QtCore" in sys.modules: "QAbstractItemModel.changePersistentIndexList": ('typing.List[int]', 'typing.List[int]'), "QAbstractItemModel.checkIndex": ('PySide2.QtCore.QModelIndex', 'PySide2.QtCore.QAbstractItemModel.CheckIndexOptions'), "QAbstractItemModel.columnCount": ('PySide2.QtCore.QModelIndex',), - "QAbstractItemModel.createIndex": ('int', 'int', 'int'), + "QAbstractItemModel.createIndex": [('int', 'int', 'int'), ('int', 'int', 'object')], "QAbstractItemModel.data": ('PySide2.QtCore.QModelIndex', 'int'), "QAbstractItemModel.decodeData": ('int', 'int', 'PySide2.QtCore.QModelIndex', 'PySide2.QtCore.QDataStream'), "QAbstractItemModel.dropMimeData": ('PySide2.QtCore.QMimeData', 'PySide2.QtCore.Qt.DropAction', 'int', 'int', 'PySide2.QtCore.QModelIndex'), @@ -15213,7 +15213,7 @@ if "PySide2.QtHelp" in sys.modules: "QHelpEngineCore.unregisterDocumentation": ('str',), # class PySide2.QtHelp.QHelpIndexModel: - "QHelpIndexModel.createIndex": [('int', 'int', 'int'), ('str',)], + "QHelpIndexModel.createIndex": [('int', 'int', 'int'), ('int', 'int', 'object'), ('str',)], "QHelpIndexModel.filter": ('str', 'str'), "QHelpIndexModel.isCreatingIndex": (), "QHelpIndexModel.linksForKeyword": ('str',), diff --git a/sources/pyside2/tests/registry/exists_win32_5_12_0_ci.py b/sources/pyside2/tests/registry/exists_win32_5_12_0_ci.py index 189e5c74d..0a3f587f3 100644 --- a/sources/pyside2/tests/registry/exists_win32_5_12_0_ci.py +++ b/sources/pyside2/tests/registry/exists_win32_5_12_0_ci.py @@ -159,7 +159,7 @@ if "PySide2.QtCore" in sys.modules: "QAbstractItemModel.changePersistentIndexList": ('typing.List', 'typing.List'), "QAbstractItemModel.checkIndex": ('PySide2.QtCore.QModelIndex', 'PySide2.QtCore.QAbstractItemModel.CheckIndexOptions'), "QAbstractItemModel.columnCount": ('PySide2.QtCore.QModelIndex',), - "QAbstractItemModel.createIndex": ('int', 'int', 'int'), + "QAbstractItemModel.createIndex": [('int', 'int', 'int'), ('int', 'int', 'object')], "QAbstractItemModel.data": ('PySide2.QtCore.QModelIndex', 'int'), "QAbstractItemModel.decodeData": ('int', 'int', 'PySide2.QtCore.QModelIndex', 'PySide2.QtCore.QDataStream'), "QAbstractItemModel.dropMimeData": ('PySide2.QtCore.QMimeData', 'PySide2.QtCore.Qt.DropAction', 'int', 'int', 'PySide2.QtCore.QModelIndex'), @@ -15335,7 +15335,7 @@ if "PySide2.QtHelp" in sys.modules: "QHelpEngineCore.unregisterDocumentation": ('str',), # class PySide2.QtHelp.QHelpIndexModel: - "QHelpIndexModel.createIndex": [('int', 'int', 'int'), ('str',)], + "QHelpIndexModel.createIndex": [('int', 'int', 'int'), ('int', 'int', 'object'), ('str',)], "QHelpIndexModel.filter": ('str', 'str'), "QHelpIndexModel.isCreatingIndex": (), "QHelpIndexModel.linksForKeyword": ('str',), diff --git a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp index 9f71b495a..c438e0c37 100644 --- a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp @@ -59,7 +59,7 @@ void TestCodeInjections::testReadFile() const char* cppCode ="struct A {};\n"; int argc = 0; - char *argv[] = {NULL}; + char *argv[] = {nullptr}; QCoreApplication app(argc, argv); QString attribute = QLatin1String("file='") + filePath + QLatin1Char('\''); diff --git a/sources/shiboken2/generator/generator.h b/sources/shiboken2/generator/generator.h index 225e7aec7..cdf6d89b3 100644 --- a/sources/shiboken2/generator/generator.h +++ b/sources/shiboken2/generator/generator.h @@ -288,10 +288,10 @@ protected: /// Returns all container types found by APIExtractor ContainerTypeEntryList containerTypes() const; - /// Returns an AbstractMetaEnum for a given TypeEntry that is an EnumTypeEntry, or NULL if not found. + /// Returns an AbstractMetaEnum for a given TypeEntry that is an EnumTypeEntry, or nullptr if not found. const AbstractMetaEnum* findAbstractMetaEnum(const TypeEntry* typeEntry) const; - /// Returns an AbstractMetaEnum for a given AbstractMetaType that holds an EnumTypeEntry, or NULL if not found. + /// Returns an AbstractMetaEnum for a given AbstractMetaType that holds an EnumTypeEntry, or nullptr if not found. const AbstractMetaEnum* findAbstractMetaEnum(const AbstractMetaType* metaType) const; /// Generates a file for given AbstractMetaClass or AbstractMetaType (smart pointer case). diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp index 19ddd5f37..ea971287a 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp @@ -53,6 +53,8 @@ static inline QString titleAttribute() { return QStringLiteral("title"); } static inline QString fullTitleAttribute() { return QStringLiteral("fulltitle"); } static inline QString briefAttribute() { return QStringLiteral("brief"); } +static inline QString none() { return QStringLiteral("None"); } + static bool shouldSkip(const AbstractMetaFunction* func) { // Constructors go to separate section @@ -1787,11 +1789,13 @@ QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* /* cppClass */ || defValue.startsWith(QLatin1String("QList"))) { defValue = QLatin1String("list()"); } else if (defValue == QLatin1String("QVariant()")) { - defValue = QLatin1String("None"); + defValue = none(); } else { defValue.replace(QLatin1String("::"), QLatin1String(".")); - if (defValue == QLatin1String("0") && (arg->type()->isQObject() || arg->type()->isObject())) - defValue = QLatin1String("None"); + if (defValue == QLatin1String("nullptr")) + defValue = none(); + else if (defValue == QLatin1String("0") && (arg->type()->isQObject() || arg->type()->isObject())) + defValue = none(); } ret += QLatin1Char('=') + defValue; } diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 99bfae9f0..3d87fcff0 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -48,7 +48,7 @@ static const char CPP_ARG0[] = "cppArg0"; QHash<QString, QString> CppGenerator::m_nbFuncs = QHash<QString, QString>(); QHash<QString, QString> CppGenerator::m_sqFuncs = QHash<QString, QString>(); QHash<QString, QString> CppGenerator::m_mpFuncs = QHash<QString, QString>(); -QString CppGenerator::m_currentErrorCode(QLatin1String("0")); +QString CppGenerator::m_currentErrorCode(QLatin1String("{}")); static const char typeNameFunc[] = R"CPP( template <class T> @@ -552,9 +552,11 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) // Write methods definition s << "static PyMethodDef " << className << "_methods[] = {" << endl; s << methodsDefinitions << endl; - if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) - s << INDENT << "{\"__copy__\", (PyCFunction)" << className << "___copy__" << ", METH_NOARGS}," << endl; - s << INDENT << "{0} // Sentinel" << endl; + if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { + s << INDENT << "{\"__copy__\", reinterpret_cast<PyCFunction>(" << className << "___copy__)" + << ", METH_NOARGS}," << endl; + } + s << INDENT << '{' << NULL_PTR << ", " << NULL_PTR << "} // Sentinel" << endl; s << "};" << endl << endl; // Write tp_getattro function @@ -650,10 +652,10 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (canGenerateFieldSetter(metaField)) s << cpythonSetterFunctionName(metaField); else - s << '0'; + s << NULL_PTR; s << "}," << endl; } - s << INDENT << "{0} // Sentinel" << endl; + s << INDENT << '{' << NULL_PTR << "} // Sentinel" << endl; s << "};" << endl << endl; } @@ -962,7 +964,8 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (!injectedCodeCallsPythonOverride(func)) { s << INDENT; - s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call(" << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", NULL));" << endl; + s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call(" + << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", nullptr));" << endl; s << INDENT << "// An error happened in python code!" << endl; s << INDENT << "if (" << PYTHON_RETURN_VAR << ".isNull()) {" << endl; @@ -1090,7 +1093,7 @@ void CppGenerator::writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass s << INDENT << "if (QObject::d_ptr->metaObject)" << endl << INDENT << INDENT << "return QObject::d_ptr->dynamicMetaObject();" << endl; s << INDENT << "SbkObject* pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; - s << INDENT << "if (pySelf == NULL)" << endl; + s << INDENT << "if (pySelf == nullptr)" << endl; s << INDENT << INDENT << "return " << metaClass->qualifiedCppName() << "::metaObject();" << endl; s << INDENT << "return PySide::SignalManager::retrieveMetaObject(reinterpret_cast<PyObject*>(pySelf));" << endl; s << '}' << endl << endl; @@ -1099,7 +1102,7 @@ void CppGenerator::writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass s << "int " << wrapperClassName << "::qt_metacall(QMetaObject::Call call, int id, void** args)" << endl; s << "{" << endl; - AbstractMetaFunction *func = NULL; + AbstractMetaFunction *func = nullptr; AbstractMetaFunctionList list = metaClass->queryFunctionsByName(QLatin1String("qt_metacall")); if (list.size() == 1) func = list[0]; @@ -1127,7 +1130,7 @@ void CppGenerator::writeMetaCast(QTextStream& s, const AbstractMetaClass* metaCl QString wrapperClassName = wrapperName(metaClass); s << "void* " << wrapperClassName << "::qt_metacast(const char* _clname)" << endl; s << '{' << endl; - s << INDENT << "if (!_clname) return 0;" << endl; + s << INDENT << "if (!_clname) return {};" << endl; s << INDENT << "SbkObject* pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; s << INDENT << "if (pySelf && PySide::inherits(Py_TYPE(pySelf), _clname))" << endl; s << INDENT << INDENT << "return static_cast<void*>(const_cast< " << wrapperClassName << "* >(this));" << endl; @@ -1156,11 +1159,12 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream& s, const TypeEntry* } QString code; QTextStream c(&code); - c << INDENT << "*((" << cppTypeName << "*)cppOut) = "; + c << INDENT << "*reinterpret_cast<" << cppTypeName << "*>(cppOut) =\n" + << INDENT << " "; if (enumType->isFlags()) - c << cppTypeName << "(QFlag((int)PySide::QFlags::getValue(reinterpret_cast<PySideQFlagsObject*>(pyIn))))"; + c << cppTypeName << "(QFlag(int(PySide::QFlags::getValue(reinterpret_cast<PySideQFlagsObject*>(pyIn)))))"; else - c << "(" << cppTypeName << ") Shiboken::Enum::getValue(pyIn)"; + c << "static_cast<" << cppTypeName << ">(Shiboken::Enum::getValue(pyIn))"; c << ';' << endl; writePythonToCppFunction(s, code, typeName, typeName); @@ -1173,11 +1177,12 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream& s, const TypeEntry* << cppTypeName << " *>(cppIn));" << endl; c << INDENT; c << "return "; - if (enumType->isFlags()) - c << "reinterpret_cast<PyObject*>(PySide::QFlags::newObject(castCppIn, " << enumPythonType << "))"; - - else + if (enumType->isFlags()) { + c << "reinterpret_cast<PyObject*>(PySide::QFlags::newObject(castCppIn, " + << enumPythonType << "))"; + } else { c << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn)"; + } c << ';' << endl; writeCppToPythonFunction(s, code, typeName, typeName); s << endl; @@ -1195,8 +1200,9 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream& s, const TypeEntry* code.clear(); cppTypeName = getFullTypeName(flags).trimmed(); - c << INDENT << "*((" << cppTypeName << "*)cppOut) = " << cppTypeName; - c << "(QFlag((int)Shiboken::Enum::getValue(pyIn)));" << endl; + c << INDENT << "*reinterpret_cast<" << cppTypeName << "*>(cppOut) =\n" + << INDENT << " " << cppTypeName + << "(QFlag(int(Shiboken::Enum::getValue(pyIn))));" << endl; QString flagsTypeName = fixedCppTypeName(flags); writePythonToCppFunction(s, code, typeName, flagsTypeName); @@ -1204,11 +1210,20 @@ void CppGenerator::writeEnumConverterFunctions(QTextStream& s, const TypeEntry* code.clear(); c << INDENT << "Shiboken::AutoDecRef pyLong(PyNumber_Long(pyIn));" << endl; - c << INDENT << "*((" << cppTypeName << "*)cppOut) = " << cppTypeName; - c << "(QFlag((int)PyLong_AsLong(pyLong.object())));" << endl; + c << INDENT << "*reinterpret_cast<" << cppTypeName << "*>(cppOut) =\n" + << INDENT << " " << cppTypeName + << "(QFlag(int(PyLong_AsLong(pyLong.object()))));" << endl; + // PYSIDE-898: Include an additional condition to detect if the type of the + // enum corresponds to the object that is being evaluated. + // Using only `PyNumber_Check(...)` is too permissive, + // then we would have been unable to detect the difference between + // a PolarOrientation and Qt::AlignmentFlag, which was the main + // issue of the bug. + const QString numberCondition = QStringLiteral("PyNumber_Check(pyIn) && ") + pyTypeCheck; writePythonToCppFunction(s, code, QLatin1String("number"), flagsTypeName); - writeIsPythonConvertibleToCppFunction(s, QLatin1String("number"), flagsTypeName, - QLatin1String("PyNumber_Check(pyIn)")); + writeIsPythonConvertibleToCppFunction(s, QLatin1String("number"), flagsTypeName, numberCondition); + + } void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaClass *metaClass, @@ -1247,7 +1262,8 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); // "Is convertible" function for the Python object to C++ pointer conversion. - QString pyTypeCheck = QStringLiteral("PyObject_TypeCheck(pyIn, (PyTypeObject*)%1)").arg(cpythonType); + const QString pyTypeCheck = QLatin1String("PyObject_TypeCheck(pyIn, reinterpret_cast<PyTypeObject*>(") + + cpythonType + QLatin1String("))"); writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, pyTypeCheck, QString(), true); s << endl; @@ -1256,9 +1272,10 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla code.clear(); if (usePySideExtensions() && metaClass->isQObject()) { - c << INDENT << "return PySide::getWrapperForQObject((" << typeName << "*)cppIn, " << cpythonType << ");" << endl; + c << INDENT << "return PySide::getWrapperForQObject(reinterpret_cast<" + << typeName << "*>(const_cast<void*>(cppIn)), " << cpythonType << ");" << endl; } else { - c << INDENT << "PyObject* pyOut = (PyObject*)Shiboken::BindingManager::instance().retrieveWrapper(cppIn);" << endl; + c << INDENT << "auto pyOut = reinterpret_cast<PyObject*>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));" << endl; c << INDENT << "if (pyOut) {" << endl; { Indentation indent(INDENT); @@ -1306,8 +1323,9 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla else computedWrapperName = wrapperName(classContext.preciseType()); - c << INDENT << "return Shiboken::Object::newObject(" << cpythonType << ", new ::" << computedWrapperName; - c << "(*((" << typeName << "*)cppIn)), true, true);"; + c << INDENT << "return Shiboken::Object::newObject(" << cpythonType + << ", new ::" << computedWrapperName << "(*reinterpret_cast<const " + << typeName << "*>(cppIn)), true, true);"; writeCppToPythonFunction(s, code, sourceTypeName, targetTypeName); s << endl; @@ -1328,7 +1346,7 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla else wrappedCPtrExpression = cpythonWrapperCPtr(classContext.preciseType(), pyInVariable); - c << INDENT << "*((" << typeName << "*)cppOut) = *" + c << INDENT << "*reinterpret_cast<" << typeName << "*>(cppOut) = *" << wrappedCPtrExpression << ';'; writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); @@ -1593,7 +1611,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over } else { s << context.preciseType()->cppSignature(); } - s << "* cptr = 0;" << endl; + s << "* cptr{};" << endl; initPythonArguments = maxArgs > 0; usesNamedArguments = !ownerClass->isQObject() && overloadData.hasArgumentWithDefaultValue(); @@ -1604,7 +1622,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction()); } if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType()) - s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = 0;" << endl; + s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << "{};" << endl; initPythonArguments = minArgs != maxArgs || maxArgs > 1; usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue(); @@ -1613,9 +1631,14 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over if (maxArgs > 0) { s << INDENT << "int overloadId = -1;" << endl; s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR; - if (pythonFunctionWrapperUsesListOfArguments(overloadData)) - s << "[] = { 0" << QString::fromLatin1(", 0").repeated(maxArgs-1) << " }"; - s << ';' << endl; + if (pythonFunctionWrapperUsesListOfArguments(overloadData)) { + s << "[] = { " << NULL_PTR; + for (int i = 1; i < maxArgs; ++i) + s << ", " << NULL_PTR; + s << " };" << endl; + } else { + s << "{};" << endl; + } writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); } @@ -1659,7 +1682,7 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun QStringList argNamesList = argNamesSet.toList(); qSort(argNamesList.begin(), argNamesList.end()); if (argNamesList.isEmpty()) { - s << INDENT << "const char** argNames = 0;" << endl; + s << INDENT << "const char** argNames{};" << endl; } else { s << INDENT << "const char* argNames[] = {\"" << argNamesList.join(QLatin1String("\", \"")) << "\"};" << endl; @@ -1854,7 +1877,7 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction Indentation indent(INDENT); s << INDENT << "PyErr_Clear();" << endl; s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");" << endl; - s << INDENT << PYTHON_RETURN_VAR << " = 0;" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = " << NULL_PTR << ';' << endl; } s << INDENT << '}' << endl; } @@ -2083,93 +2106,8 @@ void CppGenerator::writeErrorSection(QTextStream& s, OverloadData& overloadData) QString argsVar = pythonFunctionWrapperUsesListOfArguments(overloadData) ? QLatin1String("args") : QLatin1String(PYTHON_ARG); - if (verboseErrorMessagesDisabled()) { - s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\", 0);" << endl; - } else { - QStringList overloadSignatures; - const OverloadData::MetaFunctionList &overloads = overloadData.overloads(); - for (const AbstractMetaFunction *f : overloads) { - QStringList args; - const AbstractMetaArgumentList &arguments = f->arguments(); - for (AbstractMetaArgument *arg : arguments) { - QString strArg; - AbstractMetaType* argType = arg->type(); - if (isCString(argType)) { - strArg = QLatin1String("\" SBK_BYTES_NAME \""); - } else if (argType->isPrimitive()) { - const PrimitiveTypeEntry* ptp = reinterpret_cast<const PrimitiveTypeEntry*>(argType->typeEntry()); - while (ptp->referencedTypeEntry()) - ptp = ptp->referencedTypeEntry(); - strArg = ptp->name(); - if (strArg == QLatin1String("QString")) { - strArg = QLatin1String("unicode"); - } else if (strArg == QLatin1String("QChar")) { - strArg = QLatin1String("1-unicode"); - } else { - strArg = ptp->name(); - static const QRegularExpression regex(QStringLiteral("^signed\\s+")); - Q_ASSERT(regex.isValid()); - strArg.remove(regex); - if (strArg == QLatin1String("double")) - strArg = QLatin1String("float"); - } - } else if (argType->typeEntry()->isContainer()) { - strArg = argType->fullName(); - if (strArg == QLatin1String("QList") || strArg == QLatin1String("QVector") - || strArg == QLatin1String("QLinkedList") || strArg == QLatin1String("QStack") - || strArg == QLatin1String("QQueue")) { - strArg = QLatin1String("list"); - } else if (strArg == QLatin1String("QMap") || strArg == QLatin1String("QHash") - || strArg == QLatin1String("QMultiMap") || strArg == QLatin1String("QMultiHash")) { - strArg = QLatin1String("dict"); - } else if (strArg == QLatin1String("QPair")) { - strArg = QLatin1String("2-tuple"); - } - } else { - strArg = argType->fullName(); - if (strArg == QLatin1String("PyUnicode")) - strArg = QLatin1String("unicode"); - else if (strArg == QLatin1String("PyString")) - strArg = QLatin1String("str"); - else if (strArg == QLatin1String("PyBytes")) - strArg = QLatin1String("\" SBK_BYTES_NAME \""); - else if (strArg == QLatin1String("PyByteArray")) - strArg = QLatin1String("bytearray"); - else if (strArg == QLatin1String("PySequence")) - strArg = QLatin1String("list"); - else if (strArg == QLatin1String("PyTuple")) - strArg = QLatin1String("tuple"); - else if (strArg == QLatin1String("PyDict")) - strArg = QLatin1String("dict"); - else if (strArg == QLatin1String("PyObject")) - strArg = QLatin1String("object"); - else if (strArg == QLatin1String("PyCallable")) - strArg = QLatin1String("callable"); - else if (strArg == QLatin1String("uchar")) - strArg = QLatin1String("buffer"); // This depends on an inject code to be true, but if it's not true - // the function wont work at all, so it must be true. - } - if (!arg->defaultValueExpression().isEmpty()) { - strArg += QLatin1String(" = "); - if ((isCString(argType) || isPointerToWrapperType(argType)) - && arg->defaultValueExpression() == QLatin1String("0")) { - strArg += QLatin1String("None"); - } else { - QString e = arg->defaultValueExpression(); - e.replace(QLatin1String("::"), QLatin1String(".")); - e.replace(QLatin1String("\""), QLatin1String("\\\"")); - strArg += e; - } - } - args << strArg; - } - overloadSignatures << QLatin1Char('"') + args.join(QLatin1String(", ")) + QLatin1Char('"'); - } - s << INDENT << "const char* overloads[] = {" << overloadSignatures.join(QLatin1String(", ")) - << ", 0};" << endl; - s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\", overloads);" << endl; - } - s << INDENT << returnStatement(m_currentErrorCode) << endl; + s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\");" << endl; + s << INDENT << "return " << m_currentErrorCode << ';' << endl; } void CppGenerator::writeFunctionReturnErrorCheckSection(QTextStream& s, bool hasReturnValue) @@ -2528,7 +2466,7 @@ void CppGenerator::writeOverloadedFunctionDecisor(QTextStream& s, const Overload { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"reverse operator not implemented.\");" << endl; - s << INDENT << "return 0;" << endl; + s << INDENT << "return {};" << endl; } s << INDENT << "}" << endl << endl; } @@ -2767,7 +2705,7 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, // When an argument is removed from a method signature and no other means of calling // the method are provided (as with code injection) the generator must abort. qFatal(qPrintable(QString::fromLatin1("No way to call '%1::%2' with the modifications described in the type system.") - .arg(func->ownerClass()->name(), func->signature())), NULL); + .arg(func->ownerClass()->name(), func->signature()))); } removedArgs++; continue; @@ -2848,7 +2786,8 @@ void CppGenerator::writeCppToPythonFunction(QTextStream& s, const QString& code, static void replaceCppToPythonVariables(QString& code, const QString& typeName) { - code.prepend(QString::fromLatin1("%1& cppInRef = *((%1*)cppIn);\n").arg(typeName)); + code.prepend(QLatin1String("auto &cppInRef = *reinterpret_cast<") + + typeName + QLatin1String("*>(const_cast<void *>(cppIn));\n")); code.replace(QLatin1String("%INTYPE"), typeName); code.replace(QLatin1String("%OUTTYPE"), QLatin1String("PyObject*")); code.replace(QLatin1String("%in"), QLatin1String("cppInRef")); @@ -2864,9 +2803,9 @@ void CppGenerator::writeCppToPythonFunction(QTextStream& s, const AbstractMetaTy { const CustomConversion* customConversion = containerType->typeEntry()->customConversion(); if (!customConversion) { - qFatal(qPrintable(QString::fromLatin1("Can't write the C++ to Python conversion function for container type '%1' - "\ - "no conversion rule was defined for it in the type system.") - .arg(containerType->typeEntry()->qualifiedCppName())), NULL); + qFatal("Can't write the C++ to Python conversion function for container type '%s' - "\ + "no conversion rule was defined for it in the type system.", + qPrintable(containerType->typeEntry()->qualifiedCppName())); } if (!containerType->typeEntry()->isContainer()) { writeCppToPythonFunction(s, customConversion); @@ -2919,7 +2858,7 @@ void CppGenerator::writeIsPythonConvertibleToCppFunction(QTextStream& s, Indentation indent(INDENT); s << INDENT << "return " << pythonToCppFuncName << ';' << endl; } - s << INDENT << "return 0;" << endl; + s << INDENT << "return {};" << endl; s << '}' << endl; } @@ -2939,8 +2878,9 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, conversion = QLatin1Char('*') + cpythonWrapperCPtr(sourceType->typeEntry(), QLatin1String("pyIn")); if (!preConversion.isEmpty()) c << INDENT << preConversion << endl; - c << INDENT << QString::fromLatin1("*((%1*)cppOut) = %1(%2);") - .arg(getFullTypeName(targetType->typeEntry()), conversion); + const QString fullTypeName = getFullTypeName(targetType->typeEntry()); + c << INDENT << "*reinterpret_cast<" << fullTypeName << "*>(cppOut) = " + << fullTypeName << '(' << conversion << ");"; QString sourceTypeName = fixedCppTypeName(sourceType); QString targetTypeName = fixedCppTypeName(targetType); writePythonToCppFunction(s, code, sourceTypeName, targetTypeName); @@ -2966,7 +2906,8 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, code.replace(QLatin1String("%INTYPE"), inType); code.replace(QLatin1String("%OUTTYPE"), targetType->qualifiedCppName()); code.replace(QLatin1String("%in"), QLatin1String("pyIn")); - code.replace(QLatin1String("%out"), QString::fromLatin1("*((%1*)cppOut)").arg(getFullTypeName(targetType))); + code.replace(QLatin1String("%out"), + QLatin1String("*reinterpret_cast<") + getFullTypeName(targetType) + QLatin1String("*>(cppOut)")); QString sourceTypeName = fixedCppTypeName(toNative); QString targetTypeName = fixedCppTypeName(targetType); @@ -2991,9 +2932,9 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, } if (typeCheck.isEmpty()) { if (!toNative->sourceType() || toNative->sourceType()->isPrimitive()) { - qFatal(qPrintable(QString::fromLatin1("User added implicit conversion for C++ type '%1' must provide either an input "\ - "type check function or a non primitive type entry.") - .arg(targetType->qualifiedCppName())), NULL); + qFatal("User added implicit conversion for C++ type '%s' must provide either an input "\ + "type check function or a non primitive type entry.", + qPrintable(targetType->qualifiedCppName())); } typeCheck = QString::fromLatin1("PyObject_TypeCheck(%in, %1)").arg(cpythonTypeNameExt(toNative->sourceType())); @@ -3019,7 +2960,8 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, const Abs QString cppTypeName = getFullTypeNameWithoutModifiers(containerType); QString code; QTextStream c(&code); - c << INDENT << QString::fromLatin1("%1& cppOutRef = *((%1*)cppOut);").arg(cppTypeName) << endl; + c << INDENT << "auto &cppOutRef = *reinterpret_cast<" + << cppTypeName << "*>(cppOut);\n"; code.append(toCppConversions.constFirst()->conversion()); for (int i = 0; i < containerType->instantiations().count(); ++i) { const AbstractMetaType* type = containerType->instantiations().at(i); @@ -3384,8 +3326,12 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f mc << func->originalName(); } else { - if (!func->isStatic()) - mc << "((::" << wrapperName(func->ownerClass()) << "*) " << CPP_SELF_VAR << ")->"; + if (!func->isStatic()) { + const auto *owner = func->ownerClass(); + const bool directInheritance = context.metaClass() == owner; + mc << (directInheritance ? "static_cast" : "reinterpret_cast") + << "<::" << wrapperName(owner) << "*>(" << CPP_SELF_VAR << ")->"; + } if (!func->isAbstract()) mc << (func->isProtected() ? wrapperName(func->ownerClass()) : @@ -3836,6 +3782,29 @@ bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass* metaClass) return false; } +struct pyTypeSlotEntry +{ + explicit pyTypeSlotEntry(const char *name, const QString &function) : + m_name(name), m_function(function) {} + + const char *m_name; + const QString &m_function; +}; + +QTextStream &operator<<(QTextStream &str, const pyTypeSlotEntry &e) +{ + str << '{' << e.m_name << ','; + const int padding = qMax(0, 18 - int(strlen(e.m_name))); + for (int p = 0; p < padding; ++p) + str << ' '; + if (e.m_function.isEmpty()) + str << NULL_PTR; + else + str << "reinterpret_cast<void*>(" << e.m_function << ')'; + str << "},\n"; + return str; +} + void CppGenerator::writeClassDefinition(QTextStream &s, const AbstractMetaClass *metaClass, GeneratorContext &classContext) @@ -3844,11 +3813,11 @@ void CppGenerator::writeClassDefinition(QTextStream &s, QString tp_init; QString tp_new; QString tp_dealloc; - QString tp_hash(QLatin1Char('0')); - QString tp_call = tp_hash; + QString tp_hash; + QString tp_call; QString cppClassName = metaClass->qualifiedCppName(); const QString className = chopType(cpythonTypeName(metaClass)); - QString baseClassName(QLatin1Char('0')); + QString baseClassName; AbstractMetaFunctionList ctors; const AbstractMetaFunctionList &allCtors = metaClass->queryFunctions(AbstractMetaClass::Constructors); for (AbstractMetaFunction *f : allCtors) { @@ -3869,7 +3838,7 @@ void CppGenerator::writeClassDefinition(QTextStream &s, tp_dealloc = metaClass->hasPrivateDestructor() ? QLatin1String("SbkDeallocWrapperWithPrivateDtor") : QLatin1String("object_dealloc /* PYSIDE-832: Prevent replacement of \"0\" with subtype_dealloc. */"); - tp_init = QLatin1String("0"); + tp_init.clear(); } else { QString deallocClassName; if (shouldGenerateCppWrapper(metaClass)) @@ -3880,11 +3849,12 @@ void CppGenerator::writeClassDefinition(QTextStream &s, tp_dealloc = QLatin1String("&SbkDeallocQAppWrapper"); else tp_dealloc = QLatin1String("&SbkDeallocWrapper"); - tp_init = (onlyPrivCtor || ctors.isEmpty()) ? QLatin1String("0") : cpythonFunctionName(ctors.constFirst()); + if (!onlyPrivCtor && !ctors.isEmpty()) + tp_init = cpythonFunctionName(ctors.constFirst()); } - QString tp_getattro(QLatin1Char('0')); - QString tp_setattro = tp_getattro; + QString tp_getattro; + QString tp_setattro; if (usePySideExtensions() && (metaClass->qualifiedCppName() == QLatin1String("QObject"))) { tp_getattro = cpythonGetattroFunctionName(metaClass); tp_setattro = cpythonSetattroFunctionName(metaClass); @@ -3921,11 +3891,11 @@ void CppGenerator::writeClassDefinition(QTextStream &s, tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC")); } - QString tp_richcompare = QString(QLatin1Char('0')); + QString tp_richcompare; if (!metaClass->isNamespace() && metaClass->hasComparisonOperatorOverload()) tp_richcompare = cpythonBaseName(metaClass) + QLatin1String("_richcompare"); - QString tp_getset = QString(QLatin1Char('0')); + QString tp_getset; if (shouldGenerateGetSetList(metaClass) && !classContext.forSmartPointer()) tp_getset = cpythonGettersSettersDefinitionName(metaClass); @@ -3936,7 +3906,7 @@ void CppGenerator::writeClassDefinition(QTextStream &s, if (m_tpFuncs.contains(func->name())) m_tpFuncs[func->name()] = cpythonFunctionName(func); } - if (m_tpFuncs[QLatin1String("__repr__")] == QLatin1String("0") + if (m_tpFuncs.value(QLatin1String("__repr__")).isEmpty() && !metaClass->isQObject() && metaClass->hasToStringCapability()) { m_tpFuncs[QLatin1String("__repr__")] = writeReprFunction(s, classContext); @@ -3979,23 +3949,23 @@ void CppGenerator::writeClassDefinition(QTextStream &s, s << "}" << endl; s << endl; s << "static PyType_Slot " << className << "_slots[] = {" << endl; - s << INDENT << "{Py_tp_base, (void *)0}, // inserted by introduceWrapperType" << endl; - s << INDENT << "{Py_tp_dealloc, (void *)" << tp_dealloc << "}," << endl; - s << INDENT << "{Py_tp_repr, (void *)" << m_tpFuncs[QLatin1String("__repr__")] << "}," << endl; - s << INDENT << "{Py_tp_hash, (void *)" << tp_hash << "}," << endl; - s << INDENT << "{Py_tp_call, (void *)" << tp_call << "}," << endl; - s << INDENT << "{Py_tp_str, (void *)" << m_tpFuncs[QLatin1String("__str__")] << "}," << endl; - s << INDENT << "{Py_tp_getattro, (void *)" << tp_getattro << "}," << endl; - s << INDENT << "{Py_tp_setattro, (void *)" << tp_setattro << "}," << endl; - s << INDENT << "{Py_tp_traverse, (void *)" << className << "_traverse}," << endl; - s << INDENT << "{Py_tp_clear, (void *)" << className << "_clear}," << endl; - s << INDENT << "{Py_tp_richcompare, (void *)" << tp_richcompare << "}," << endl; - s << INDENT << "{Py_tp_iter, (void *)" << m_tpFuncs[QLatin1String("__iter__")] << "}," << endl; - s << INDENT << "{Py_tp_iternext, (void *)" << m_tpFuncs[QLatin1String("__next__")] << "}," << endl; - s << INDENT << "{Py_tp_methods, (void *)" << className << "_methods}," << endl; - s << INDENT << "{Py_tp_getset, (void *)" << tp_getset << "}," << endl; - s << INDENT << "{Py_tp_init, (void *)" << tp_init << "}," << endl; - s << INDENT << "{Py_tp_new, (void *)" << tp_new << "}," << endl; + s << INDENT << "{Py_tp_base, nullptr}, // inserted by introduceWrapperType" << endl; + s << INDENT << pyTypeSlotEntry("Py_tp_dealloc", tp_dealloc) + << INDENT << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(QLatin1String("__repr__"))) + << INDENT << pyTypeSlotEntry("Py_tp_hash", tp_hash) + << INDENT << pyTypeSlotEntry("Py_tp_call", tp_call) + << INDENT << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(QLatin1String("__str__"))) + << INDENT << pyTypeSlotEntry("Py_tp_getattro", tp_getattro) + << INDENT << pyTypeSlotEntry("Py_tp_setattro", tp_setattro) + << INDENT << pyTypeSlotEntry("Py_tp_traverse", className + QLatin1String("_traverse")) + << INDENT << pyTypeSlotEntry("Py_tp_clear", className + QLatin1String("_clear")) + << INDENT << pyTypeSlotEntry("Py_tp_richcompare", tp_richcompare) + << INDENT << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(QLatin1String("__iter__"))) + << INDENT << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(QLatin1String("__next__"))) + << INDENT << pyTypeSlotEntry("Py_tp_methods", className + QLatin1String("_methods")) + << INDENT << pyTypeSlotEntry("Py_tp_getset", tp_getset) + << INDENT << pyTypeSlotEntry("Py_tp_init", tp_init) + << INDENT << pyTypeSlotEntry("Py_tp_new", tp_new); if (supportsSequenceProtocol(metaClass)) { s << INDENT << "// type supports sequence protocol" << endl; writeTypeAsSequenceDefinition(s, metaClass); @@ -4009,7 +3979,7 @@ void CppGenerator::writeClassDefinition(QTextStream &s, s << INDENT << "// type supports number protocol" << endl; writeTypeAsNumberDefinition(s, metaClass); } - s << INDENT << "{0, 0}" << endl; + s << INDENT << "{0, " << NULL_PTR << '}' << endl; s << "};" << endl; s << "static PyType_Spec " << className << "_spec = {" << endl; s << INDENT << "\"" << computedClassTargetFullName << "\"," << endl; @@ -4259,7 +4229,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, const AbstractMetaField *metaField, GeneratorContext &context) { - ErrorCode errorCode(0); + ErrorCode errorCode(QString::fromLatin1(NULL_PTR)); s << "static PyObject* " << cpythonGetterFunctionName(metaField) << "(PyObject* self, void*)" << endl; s << '{' << endl; @@ -4271,9 +4241,9 @@ void CppGenerator::writeGetterFunction(QTextStream &s, QString cppField; if (avoidProtectedHack() && metaField->isProtected()) { - cppField = QString::fromLatin1("((%1*)%2)->%3()") - .arg(wrapperName(metaField->enclosingClass()), QLatin1String(CPP_SELF_VAR), - protectedFieldGetterName(metaField)); + QTextStream(&cppField) << "static_cast<" + << wrapperName(metaField->enclosingClass()) << "*>(" + << CPP_SELF_VAR << ")->" << protectedFieldGetterName(metaField) << "()"; } else { cppField = QLatin1String(CPP_SELF_VAR) + QLatin1String("->") + metaField->name(); if (newWrapperSameObject) { @@ -4296,7 +4266,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, cppField = QLatin1String("fieldValue"); } - s << INDENT << "PyObject* pyOut = 0;\n"; + s << INDENT << "PyObject* pyOut = {};\n"; if (newWrapperSameObject) { // Special case colocated field with same address (first field in a struct) s << INDENT << "if (reinterpret_cast<void *>(" @@ -4316,7 +4286,8 @@ void CppGenerator::writeGetterFunction(QTextStream &s, s << INDENT << "else if (Shiboken::BindingManager::instance().hasWrapper(" << cppField << ")) {" << "\n"; { Indentation indent(INDENT); - s << INDENT << "pyOut = (PyObject*)Shiboken::BindingManager::instance().retrieveWrapper(" << cppField << ");" << "\n"; + s << INDENT << "pyOut = reinterpret_cast<PyObject*>(Shiboken::BindingManager::instance().retrieveWrapper(" + << cppField << "));" << "\n"; s << INDENT << "Py_IncRef(pyOut);" << "\n"; s << INDENT << "return pyOut;" << "\n"; } @@ -4346,7 +4317,7 @@ void CppGenerator::writeSetterFunction(QTextStream &s, writeCppSelfDefinition(s, context); - s << INDENT << "if (pyIn == 0) {" << endl; + s << INDENT << "if (pyIn == " << NULL_PTR << ") {" << endl; { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_TypeError, \"'"; @@ -4375,9 +4346,9 @@ void CppGenerator::writeSetterFunction(QTextStream &s, s << getFullTypeNameWithoutModifiers(fieldType); s << (fieldType->indirections() == 1 ? "*" : "") << " cppOut;" << endl; s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut);" << endl; - s << INDENT << QString::fromLatin1("((%1*)%2)->%3(cppOut)") - .arg(wrapperName(metaField->enclosingClass()), - QLatin1String(CPP_SELF_VAR), protectedFieldSetterName(metaField)); + s << INDENT << "static_cast<" << wrapperName(metaField->enclosingClass()) + << "*>(" << CPP_SELF_VAR << ")->" << protectedFieldSetterName(metaField) + << "(cppOut)"; } else if (isCppIntegralPrimitive(fieldType) || fieldType->typeEntry()->isEnum() || fieldType->typeEntry()->isFlags()) { s << getFullTypeNameWithoutModifiers(fieldType) << " cppOut_local = " << cppField << ';' << endl; s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_local);" << endl; @@ -4410,7 +4381,7 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co s << '{' << endl; writeCppSelfDefinition(s, context, false, true); writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); - s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = 0;" << endl; + s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << "{};" << endl; s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); s << endl; @@ -4531,7 +4502,8 @@ void CppGenerator::writeMethodDefinitionEntry(QTextStream& s, const AbstractMeta int min = overloadData.minArgs(); int max = overloadData.maxArgs(); - s << '"' << func->name() << "\", (PyCFunction)" << cpythonFunctionName(func) << ", "; + s << '"' << func->name() << "\", reinterpret_cast<PyCFunction>(" + << cpythonFunctionName(func) << "), "; if ((min == max) && (max < 2) && !usePyArgs) { if (max == 0) s << "METH_NOARGS"; @@ -4842,7 +4814,7 @@ void CppGenerator::writeFlagsNumberMethodsDefinition(QTextStream& s, const Abstr s << "#ifndef IS_PY3K" << endl; s << INDENT << "{Py_nb_long, (void *)" << cpythonName << "_long}," << endl; s << "#endif" << endl; - s << INDENT << "{0, 0} // sentinel" << endl; + s << INDENT << "{0, " << NULL_PTR << "} // sentinel" << endl; s << "};" << endl << endl; } @@ -4858,11 +4830,15 @@ void CppGenerator::writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEn AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR << ", cppArg;" << endl; s << "#ifdef IS_PY3K" << endl; - s << INDENT << CPP_SELF_VAR << " = (::" << flagsEntry->originalName() << ")(int)PyLong_AsLong(self);" << endl; - s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyLong_AsLong(" << PYTHON_ARG << ");" << endl; + s << INDENT << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName() + << ">(int(PyLong_AsLong(self)));" << endl; + s << INDENT << "cppArg = static_cast<" << flagsEntry->originalName() << ">(int(PyLong_AsLong(" + << PYTHON_ARG << ")));" << endl; s << "#else" << endl; - s << INDENT << CPP_SELF_VAR << " = (::" << flagsEntry->originalName() << ")(int)PyInt_AsLong(self);" << endl; - s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyInt_AsLong(" << PYTHON_ARG << ");" << endl; + s << INDENT << CPP_SELF_VAR << " = static_cast<::" << flagsEntry->originalName() + << ">(int(PyInt_AsLong(self)));" << endl; + s << INDENT << "cppArg = static_cast<" << flagsEntry->originalName() + << ">(int(PyInt_AsLong(" << PYTHON_ARG << ")));" << endl; s << "#endif" << endl << endl; s << INDENT << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;" << endl; s << INDENT << "return "; @@ -4941,15 +4917,16 @@ void CppGenerator::writeClassRegister(QTextStream &s, QString pyTypeBasesVariable = chopType(pyTypeName) + QLatin1String("_Type_bases"); const AbstractMetaClassList baseClasses = getBaseClasses(metaClass); if (metaClass->baseClassNames().size() > 1) { - s << INDENT << "PyObject* " << pyTypeBasesVariable << " = PyTuple_Pack(" << baseClasses.size() << ',' << endl; - QStringList bases; - for (const AbstractMetaClass *base : baseClasses) - bases << QLatin1String("(PyObject*)") + cpythonTypeNameExt(base->typeEntry()); + s << INDENT << "PyObject* " << pyTypeBasesVariable + << " = PyTuple_Pack(" << baseClasses.size() << ',' << endl; Indentation indent(INDENT); - QString separator; - QTextStream sep(&separator); - sep << "," << endl << INDENT; - s << INDENT << bases.join(separator) << ");" << endl << endl; + for (int i = 0, size = baseClasses.size(); i < size; ++i) { + if (i) + s << "," << endl; + s << INDENT << "reinterpret_cast<PyObject*>(" + << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')'; + } + s << ");" << endl << endl; } // Create type and insert it in the module or enclosing class. @@ -5215,7 +5192,7 @@ void CppGenerator::writeTypeDiscoveryFunction(QTextStream& s, const AbstractMeta } } - s << INDENT << "return 0;" << endl; + s << INDENT << "return {};" << endl; s << "}\n\n"; } @@ -5344,7 +5321,7 @@ void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &conte s << INDENT << "} else {" << endl; { Indentation indent(INDENT); - s << INDENT << "if (!PyErr_ExceptionMatches(PyExc_AttributeError)) return NULL;" << endl; + s << INDENT << "if (!PyErr_ExceptionMatches(PyExc_AttributeError)) return nullptr;" << endl; s << INDENT << "PyErr_Clear();" << endl; s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for " @@ -5371,7 +5348,7 @@ void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &conte s << INDENT << "PyErr_Format(PyExc_AttributeError," << endl; s << INDENT << " \"'%.50s' object has no attribute '%.400s'\"," << endl; s << INDENT << " tp->tp_name, PyBytes_AS_STRING(name));" << endl; - s << INDENT << "return NULL;" << endl; + s << INDENT << "return nullptr;" << endl; } s << INDENT << "} else {" << endl; { @@ -5644,13 +5621,13 @@ bool CppGenerator::finishGeneration() s << "static struct PyModuleDef moduledef = {" << endl; s << " /* m_base */ PyModuleDef_HEAD_INIT," << endl; s << " /* m_name */ \"" << moduleName() << "\"," << endl; - s << " /* m_doc */ 0," << endl; + s << " /* m_doc */ nullptr," << endl; s << " /* m_size */ -1," << endl; s << " /* m_methods */ " << moduleName() << "_methods," << endl; - s << " /* m_reload */ 0," << endl; - s << " /* m_traverse */ 0," << endl; - s << " /* m_clear */ 0," << endl; - s << " /* m_free */ 0" << endl; + s << " /* m_reload */ nullptr," << endl; + s << " /* m_traverse */ nullptr," << endl; + s << " /* m_clear */ nullptr," << endl; + s << " /* m_free */ nullptr" << endl; s << "};" << endl << endl; s << "#endif" << endl; s << "SBK_MODULE_INIT_FUNCTION_BEGIN(" << moduleName() << ")" << endl; @@ -5963,7 +5940,7 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();" << endl; s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; s << INDENT << "*_item = cppValue;" << endl; - s << INDENT << "return 0;" << endl; + s << INDENT << "return {};" << endl; s << '}' << endl; } void CppGenerator::writeIndexError(QTextStream& s, const QString& errorMsg) diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index b9eea7529..ac0b2ffaf 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -41,7 +41,6 @@ #include <limits> #include <memory> -static const char NULL_VALUE[] = "NULL"; static const char AVOID_PROTECTED_HACK[] = "avoid-protected-hack"; static const char PARENT_CTOR_HEURISTIC[] = "enable-parent-ctor-heuristic"; static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic"; @@ -53,6 +52,7 @@ const char *CPP_ARG = "cppArg"; const char *CPP_ARG_REMOVED = "removed_cppArg"; const char *CPP_RETURN_VAR = "cppResult"; const char *CPP_SELF_VAR = "cppSelf"; +const char *NULL_PTR = "nullptr"; const char *PYTHON_ARG = "pyArg"; const char *PYTHON_ARGS = "pyArgs"; const char *PYTHON_OVERRIDE_VAR = "pyOverride"; @@ -151,10 +151,10 @@ ShibokenGenerator::~ShibokenGenerator() = default; void ShibokenGenerator::clearTpFuncs() { - m_tpFuncs.insert(QLatin1String("__str__"), QLatin1String("0")); - m_tpFuncs.insert(QLatin1String("__repr__"), QLatin1String("0")); - m_tpFuncs.insert(QLatin1String("__iter__"), QLatin1String("0")); - m_tpFuncs.insert(QLatin1String("__next__"), QLatin1String("0")); + m_tpFuncs.insert(QLatin1String("__str__"), QString()); + m_tpFuncs.insert(QLatin1String("__repr__"), QString()); + m_tpFuncs.insert(QLatin1String("__iter__"), QString()); + m_tpFuncs.insert(QLatin1String("__next__"), QString()); } void ShibokenGenerator::initPrimitiveTypesCorrespondences() @@ -1396,7 +1396,7 @@ QString ShibokenGenerator::argumentString(const AbstractMetaFunction *func, { QString default_value = argument->originalDefaultValueExpression(); if (default_value == QLatin1String("NULL")) - default_value = QLatin1String(NULL_VALUE); + default_value = QLatin1String(NULL_PTR); //WORKAROUND: fix this please if (default_value.startsWith(QLatin1String("new "))) diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.h b/sources/shiboken2/generator/shiboken2/shibokengenerator.h index 60e31a99b..d2c4b3292 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.h +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.h @@ -33,6 +33,7 @@ extern const char *CPP_ARG; extern const char *CPP_ARG_REMOVED; extern const char *CPP_RETURN_VAR; extern const char *CPP_SELF_VAR; +extern const char *NULL_PTR; extern const char *PYTHON_ARG; extern const char *PYTHON_ARGS; extern const char *PYTHON_OVERRIDE_VAR; @@ -200,7 +201,7 @@ protected: void writeToCppConversion(QTextStream& s, const AbstractMetaType* type, const AbstractMetaClass* context, const QString& inArgName, const QString& outArgName); void writeToCppConversion(QTextStream& s, const AbstractMetaClass* metaClass, const QString& inArgName, const QString& outArgName); - /// Returns true if the argument is a pointer that rejects NULL values. + /// Returns true if the argument is a pointer that rejects nullptr values. bool shouldRejectNullPointerArgument(const AbstractMetaFunction* func, int argIndex); /// Verifies if the class should have a C++ wrapper generated for it, instead of only a Python wrapper. @@ -208,7 +209,7 @@ protected: /// Adds enums eligible for generation from classes/namespaces marked not to be generated. static void lookForEnumsInClassesNotToBeGenerated(AbstractMetaEnumList& enumList, const AbstractMetaClass* metaClass); - /// Returns the enclosing class for an enum, or NULL if it should be global. + /// Returns the enclosing class for an enum, or nullptr if it should be global. const AbstractMetaClass* getProperEnclosingClassForEnum(const AbstractMetaEnum* metaEnum); QString wrapperName(const AbstractMetaClass* metaClass) const; @@ -299,7 +300,7 @@ protected: * \param type A string representing the type to be discovered. * \param metaType A pointer to an AbstractMetaType pointer, to where write a new meta type object * if one is produced from the \p type string. This object must be deallocated by - * the caller. It will set the target variable to NULL, is \p type is a Python type. + * the caller. It will set the target variable to nullptr, is \p type is a Python type. * \return A custom check if \p type is a custom type, or an empty string if \p metaType * receives an existing type object. */ @@ -377,9 +378,10 @@ protected: /** * Builds an AbstractMetaType object from a QString. - * Returns NULL if no type could be built from the string. + * Returns nullptr if no type could be built from the string. * \param typeSignature The string describing the type to be built. - * \return A new AbstractMetaType object that must be deleted by the caller, or a NULL pointer in case of failure. + * \return A new AbstractMetaType object that must be deleted by the caller, + * or a nullptr pointer in case of failure. */ AbstractMetaType *buildAbstractMetaTypeFromString(QString typeSignature, QString *errorMessage = nullptr); diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index e820e749b..3e1a7e8e5 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -596,42 +596,10 @@ void init() shibokenAlreadInitialised = true; } -void setErrorAboutWrongArguments(PyObject* args, const char* funcName, const char** cppOverloads) -{ - std::string msg; - std::string params; - if (args) { - if (PyTuple_Check(args)) { - for (Py_ssize_t i = 0, max = PyTuple_GET_SIZE(args); i < max; ++i) { - if (i) - params += ", "; - PyObject* arg = PyTuple_GET_ITEM(args, i); - params += Py_TYPE(arg)->tp_name; - } - } else { - params = Py_TYPE(args)->tp_name; - } - } - - if (!cppOverloads) { - msg = "'" + std::string(funcName) + "' called with wrong argument types: " + params; - } else { - msg = "'" + std::string(funcName) + "' called with wrong argument types:\n "; - msg += funcName; - msg += '('; - msg += params; - msg += ")\n"; - msg += "Supported signatures:"; - for (int i = 0; cppOverloads[i]; ++i) { - msg += "\n "; - msg += funcName; - msg += '('; - msg += cppOverloads[i]; - msg += ')'; - } - } - PyErr_SetString(PyExc_TypeError, msg.c_str()); - +// setErrorAboutWrongArguments now gets overload info from the signature module. +void setErrorAboutWrongArguments(PyObject *args, const char *funcName) +{ + SetError_Argument(args, funcName); } class FindBaseTypeVisitor : public HierarchyVisitor diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h index 15682c600..e1cc64ba9 100644 --- a/sources/shiboken2/libshiboken/basewrapper.h +++ b/sources/shiboken2/libshiboken/basewrapper.h @@ -141,7 +141,9 @@ void callCppDestructor(void* cptr) * Shiboken::importModule is DEPRECATED. Use Shiboken::Module::import() instead. */ SBK_DEPRECATED(LIBSHIBOKEN_API bool importModule(const char* moduleName, PyTypeObject*** cppApiPtr)); -LIBSHIBOKEN_API void setErrorAboutWrongArguments(PyObject* args, const char* funcName, const char** cppOverloads); + +// setErrorAboutWrongArguments now gets overload info from the signature module. +LIBSHIBOKEN_API void setErrorAboutWrongArguments(PyObject* args, const char* funcName); namespace ObjectType { diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index 564e5fcef..a8874e2e0 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -74,6 +74,7 @@ typedef struct safe_globals_struc { // init part 2: run module PyObject *sigparse_func; PyObject *createsig_func; + PyObject *seterror_argument_func; } safe_globals_struc, *safe_globals; static safe_globals pyside_globals = 0; @@ -510,6 +511,9 @@ init_phase_2(safe_globals_struc *p, PyMethodDef *methods) p->createsig_func = PyObject_GetAttrString(p->helper_module, "create_signature"); if (p->createsig_func == NULL) goto error; + p->seterror_argument_func = PyObject_GetAttrString(p->helper_module, "seterror_argument"); + if (p->seterror_argument_func == NULL) + goto error; return 0; error: @@ -950,4 +954,29 @@ FinishSignatureInitialization(PyObject *module, const char *signatures) } } +void +SetError_Argument(PyObject *args, const char *func_name) +{ + /* + * This function replaces the type error construction with extra + * overloads parameter in favor of using the signature module. + * Error messages are rare, so we do it completely in Python. + */ + init_module_1(); + init_module_2(); + Shiboken::AutoDecRef res(PyObject_CallFunction( + pyside_globals->seterror_argument_func, + const_cast<char *>("(Os)"), args, func_name)); + if (res.isNull()) { + PyErr_Print(); + Py_FatalError("seterror_argument did not receive a result"); + } + PyObject *err, *msg; + if (!PyArg_UnpackTuple(res, func_name, 2, 2, &err, &msg)) { + PyErr_Print(); + Py_FatalError("unexpected failure in seterror_argument"); + } + PyErr_SetObject(err, msg); +} + } //extern "C" diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h index b65317662..c850698a6 100644 --- a/sources/shiboken2/libshiboken/signature.h +++ b/sources/shiboken2/libshiboken/signature.h @@ -47,6 +47,7 @@ extern "C" LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char *); //WS LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *); +LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *); } // extern "C" diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index 0eba3eaff..373ce102f 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -57,6 +57,8 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py" "${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/__init__.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/errorhandler.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/errorhandler.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/layout.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/layout.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/loader.py" diff --git a/sources/shiboken2/shibokenmodule/support/signature/errorhandler.py b/sources/shiboken2/shibokenmodule/support/signature/errorhandler.py new file mode 100644 index 000000000..902bb05af --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/errorhandler.py @@ -0,0 +1,124 @@ +############################################################################# +## +## 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$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +errorhandler.py + +This module handles the TypeError messages which were previously +produced by the generated C code. + +This version is at least consistent with the signatures, which +are created by the same module. + +Experimentally, we are trying to guess those errors which are +just the wrong number of elements in an iterator. +At the moment, it is unclear whether the information given is +enough to produce a useful ValueError. + +This matter will be improved in a later version. +""" + +from signature_loader import get_signature, inspect +from signature_loader.mapping import update_mapping, namespace +from textwrap import dedent + + +def qt_isinstance(inst, the_type): + if the_type == float: + return isinstance(inst, int) or isinstance(int, float) + try: + return isinstance(inst, the_type) + except TypeError as e: + print("FIXME", e) + return False + + +def matched_type(args, sigs): + for sig in sigs: + params = list(sig.parameters.values()) + if len(args) > len(params): + continue + if len(args) < len(params): + k = len(args) + if params[k].default is params[k].empty: + # this is a necessary parameter, so it fails. + continue + ok = True + for arg, param in zip(args, params): + ann = param.annotation + if qt_isinstance(arg, ann): + continue + ok = False + if ok: + return sig + return None + + +def seterror_argument(args, func_name): + update_mapping() + func = eval(func_name, namespace) + sigs = get_signature(func, "typeerror") + if type(sigs) != list: + sigs = [sigs] + if type(args) != tuple: + args = (args,) + # temp! + found = matched_type(args, sigs) + if found: + msg = dedent(""" + '{func_name}' called with wrong argument values: + {func_name}{args} + Found signature: + {func_name}{found} + """.format(**locals())).strip() + return ValueError, msg + type_str = ", ".join(type(arg).__name__ for arg in args) + msg = dedent(""" + '{func_name}' called with wrong argument types: + {func_name}({type_str}) + Supported signatures: + """.format(**locals())).strip() + for sig in sigs: + msg += "\n {func_name}{sig}".format(**locals()) + # We don't raise the error here, to avoid the loader in the traceback. + return TypeError, msg + +# end of file diff --git a/sources/shiboken2/shibokenmodule/support/signature/loader.py b/sources/shiboken2/shibokenmodule/support/signature/loader.py index de27d441c..be30483fe 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/support/signature/loader.py @@ -181,6 +181,8 @@ with ensure_import_support(): mapping = sbk_mapping mapping.__name__ = "mapping" put_into_loader_package(mapping) + from support.signature import errorhandler + put_into_loader_package(errorhandler) from support.signature import layout put_into_loader_package(layout) from support.signature.lib import enum_sig diff --git a/sources/shiboken2/shibokenmodule/support/signature/mapping.py b/sources/shiboken2/shibokenmodule/support/signature/mapping.py index 0195f0280..40db43729 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/support/signature/mapping.py @@ -203,6 +203,7 @@ def check_module(mod): update_mapping = Reloader().update type_map = {} +namespace = globals() # our module's __dict__ def init_Shiboken(): diff --git a/sources/shiboken2/shibokenmodule/support/signature/parser.py b/sources/shiboken2/shibokenmodule/support/signature/parser.py index 5178d9ef9..2f6e6e3f5 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/support/signature/parser.py @@ -45,8 +45,7 @@ import warnings import types import keyword import functools -from signature_loader.mapping import ( - type_map, update_mapping, __dict__ as namespace) +from signature_loader.mapping import type_map, update_mapping, namespace _DEBUG = False LIST_KEYWORDS = False diff --git a/sources/shiboken2/tests/samplebinding/decisor_test.py b/sources/shiboken2/tests/samplebinding/decisor_test.py index 9102e2d4e..734c43760 100644 --- a/sources/shiboken2/tests/samplebinding/decisor_test.py +++ b/sources/shiboken2/tests/samplebinding/decisor_test.py @@ -43,13 +43,15 @@ class DecisorTest(unittest.TestCase): This can trigger the bug #262, which means using an argument not provided by the user.''' pt = Point() - self.assertRaises(TypeError, SampleNamespace.forceDecisorSideA, pt) + # This exception may move from a TypeError to a ValueError. + self.assertRaises((TypeError, ValueError), SampleNamespace.forceDecisorSideA, pt) def testCallWithInvalidParametersSideB(self): '''Same as the previous test, but with an integer as first argument, just to complicate things for the overload method decisor.''' pt = Point() - self.assertRaises(TypeError, SampleNamespace.forceDecisorSideB, 1, pt) + # This exception may move from a TypeError to a ValueError. + self.assertRaises((TypeError, ValueError), SampleNamespace.forceDecisorSideB, 1, pt) def testDecideCallWithInheritance(self): '''Call methods overloads that receive parent and inheritor classes' instances.''' |