From 66615a89ef66c39d62a502df3eb1ff629aa39154 Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Sat, 14 Jul 2018 15:10:56 +0200 Subject: Prepare the Signature Module For More Applications This is the preparation for a number of planned applications and extensions using the signature module. This general overhaul contains: - Extraction of signature enumerations into enum_sigs.py, - a list of current keyword errors in arguments which are unsolved in shiboken, but temporarily fixed in parser.py (too many for XML), - fix spurious duplications in multiple signatures - corrections for keyword errors in function names which cannot be fixed by Python (quite few), - fixing "..." arguments into "*args", - supporting the "slot wrapper" type. This is necessary for methods like "__add__", "__mul__" etc. - Create an extra function "get_signature" that has a parameter to modify the appearance, i.e. without self, without returntype, etc. Task-number: PYSIDE-510 Change-Id: If16f7bf02c6e7cbbdc970058bb630ea4db2b854a Reviewed-by: Qt CI Bot Reviewed-by: Alexandru Croitor --- keyword-errors.lst | 43 +++ sources/pyside2/PySide2/CMakeLists.txt | 4 + .../PySide2/QtGui/typesystem_gui_common.xml | 7 +- .../pyside2/PySide2/support/signature/__init__.py | 6 +- .../PySide2/support/signature/fix-complaints.py | 2 +- .../PySide2/support/signature/lib/__init__.py | 40 +++ .../PySide2/support/signature/lib/enum_sig.py | 134 ++++++++++ .../pyside2/PySide2/support/signature/loader.py | 10 +- .../pyside2/PySide2/support/signature/mapping.py | 31 ++- .../pyside2/PySide2/support/signature/parser.py | 47 +++- sources/pyside2/tests/registry/init_platform.py | 90 +------ sources/shiboken2/libshiboken/pep384impl.cpp | 27 +- sources/shiboken2/libshiboken/pep384impl.h | 8 +- sources/shiboken2/libshiboken/signature.cpp | 291 +++++++++++++++------ sources/shiboken2/libshiboken/signature.h | 6 +- sources/shiboken2/libshiboken/typespec.cpp | 1 + 16 files changed, 546 insertions(+), 201 deletions(-) create mode 100644 keyword-errors.lst create mode 100644 sources/pyside2/PySide2/support/signature/lib/__init__.py create mode 100644 sources/pyside2/PySide2/support/signature/lib/enum_sig.py diff --git a/keyword-errors.lst b/keyword-errors.lst new file mode 100644 index 000000000..af8c581a5 --- /dev/null +++ b/keyword-errors.lst @@ -0,0 +1,43 @@ +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QAbstractItemModel.changePersistentIndex', 'arglist': 'from:PySide2.QtCore.QModelIndex,to:PySide2.QtCore.QModelIndex', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QAbstractItemModel.changePersistentIndexList', 'arglist': 'from:QModelIndexList,to:QModelIndexList', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QByteArray.indexOf', 'arglist': 'a:PySide2.QtCore.QByteArray,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QByteArray.lastIndexOf', 'arglist': 'a:PySide2.QtCore.QByteArray,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': '1', 'funcname': 'PySide2.QtCore.QByteArrayMatcher.indexIn', 'arglist': 'ba:PySide2.QtCore.QByteArray,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': '0', 'funcname': 'PySide2.QtCore.QByteArrayMatcher.indexIn', 'arglist': 'str:str,len:int,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QItemSelection.indexOf', 'arglist': 't:PySide2.QtCore.QItemSelectionRange,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QItemSelection.lastIndexOf', 'arglist': 't:PySide2.QtCore.QItemSelectionRange,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QItemSelection.move', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QTextCodec.convertToUnicode', 'arglist': 'in:str,length:int,state:PySide2.QtCore.QTextCodec.ConverterState', 'returntype': 'QString'} +KEYWORD {'multi': '0', 'funcname': 'PySide2.QtCore.QTextCodec.toUnicode', 'arglist': 'in:str,length:int,state:PySide2.QtCore.QTextCodec.ConverterState=nullptr', 'returntype': 'QString'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QVariantAnimation.interpolated', 'arglist': 'from:QVariant,to:QVariant,progress:double', 'returntype': 'QVariant'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QXmlStreamAttributes.indexOf', 'arglist': 't:PySide2.QtCore.QXmlStreamAttribute,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QXmlStreamAttributes.lastIndexOf', 'arglist': 't:PySide2.QtCore.QXmlStreamAttribute,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QXmlStreamAttributes.move', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QAbstractTextDocumentLayout.documentChanged', 'arglist': 'from:int,charsRemoved:int,charsAdded:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QIconEngine.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'bool'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QKeySequence.__lshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygon.indexOf', 'arglist': 't:PySide2.QtCore.QPoint,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygon.lastIndexOf', 'arglist': 't:PySide2.QtCore.QPoint,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygon.move', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygonF.indexOf', 'arglist': 't:PySide2.QtCore.QPointF,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygonF.lastIndexOf', 'arglist': 't:PySide2.QtCore.QPointF,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygonF.move', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QQuaternion.rotationTo', 'arglist': 'from:PySide2.QtGui.QVector3D,to:PySide2.QtGui.QVector3D', 'returntype': 'PySide2.QtGui.QQuaternion'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QStandardItem.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QStandardItem.__rshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': '4', 'funcname': 'PySide2.QtGui.QTextDocument.find', 'arglist': 'expr:PySide2.QtCore.QRegExp,from:int=0,options:PySide2.QtGui.QTextDocument.FindFlags=QTextDocument.FindFlags()', 'returntype': 'PySide2.QtGui.QTextCursor'} +KEYWORD {'multi': '2', 'funcname': 'PySide2.QtGui.QTextDocument.find', 'arglist': 'expr:PySide2.QtCore.QRegularExpression,from:int=0,options:PySide2.QtGui.QTextDocument.FindFlags=QTextDocument.FindFlags()', 'returntype': 'PySide2.QtGui.QTextCursor'} +KEYWORD {'multi': '0', 'funcname': 'PySide2.QtGui.QTextDocument.find', 'arglist': 'subString:QString,from:int=0,options:PySide2.QtGui.QTextDocument.FindFlags=QTextDocument.FindFlags()', 'returntype': 'PySide2.QtGui.QTextCursor'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QTextDocument.markContentsDirty', 'arglist': 'from:int,length:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QHeaderView.moveSection', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QLayout.replaceWidget', 'arglist': 'from:PySide2.QtWidgets.QWidget,to:PySide2.QtWidgets.QWidget,options:PySide2.QtCore.Qt.FindChildOptions=Qt.FindChildrenRecursively', 'returntype': 'PySide2.QtWidgets.QLayoutItem'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QListWidgetItem.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QListWidgetItem.__rshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QPlainTextDocumentLayout.documentChanged', 'arglist': 'from:int,arg__2:int,charsAdded:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTabBar.moveTab', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTableWidgetItem.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTableWidgetItem.__rshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTreeWidgetItem.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTreeWidgetItem.__rshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtMultimedia.QAudio.convertVolume', 'arglist': 'volume:double,from:PySide2.QtMultimedia.QAudio.VolumeScale,to:PySide2.QtMultimedia.QAudio.VolumeScale', 'returntype': 'double'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtMultimedia.QMediaPlaylist.moveMedia', 'arglist': 'from:int,to:int', 'returntype': 'bool'} diff --git a/sources/pyside2/PySide2/CMakeLists.txt b/sources/pyside2/PySide2/CMakeLists.txt index 0263f7441..0a76b6344 100644 --- a/sources/pyside2/PySide2/CMakeLists.txt +++ b/sources/pyside2/PySide2/CMakeLists.txt @@ -46,6 +46,10 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/mapping.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/mapping.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/parser.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/parser.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/enum_sig.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/enum_sig.py" COPYONLY) if (PYTHON_VERSION_MAJOR EQUAL 3) else() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/backport_inspect.py" diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml index f67b5a306..6e80230d7 100644 --- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml +++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml @@ -2694,10 +2694,10 @@ The signature "QList" is needed by the __reduce__ methods, but created by some other object used elsewhere. - After the matrix type was changed, "QList" was nowhere created. + After the matrix type was changed, "QList" was created nowhere. I don't know an explicit way to produce the right conversion function, so what I did - was to create a dummy function and immediately dele it again. + was to create a dummy function and immediately delete it again. This has the desired effect of creating the implicitly needed "QList" conversion, although the dummy function goes away. @@ -2986,7 +2986,9 @@ + + " @@ -3013,7 +3015,6 @@ } - diff --git a/sources/pyside2/PySide2/support/signature/__init__.py b/sources/pyside2/PySide2/support/signature/__init__.py index 0ff9ec7e9..14e63a5fb 100644 --- a/sources/pyside2/PySide2/support/signature/__init__.py +++ b/sources/pyside2/PySide2/support/signature/__init__.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. @@ -40,3 +40,7 @@ from __future__ import print_function, absolute_import from .loader import inspect +from PySide2 import QtCore +if QtCore.QProcess.__signature__: + pass # trigger initialization +from signature_loader import get_signature diff --git a/sources/pyside2/PySide2/support/signature/fix-complaints.py b/sources/pyside2/PySide2/support/signature/fix-complaints.py index fa2b44420..e078ef1ab 100644 --- a/sources/pyside2/PySide2/support/signature/fix-complaints.py +++ b/sources/pyside2/PySide2/support/signature/fix-complaints.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. diff --git a/sources/pyside2/PySide2/support/signature/lib/__init__.py b/sources/pyside2/PySide2/support/signature/lib/__init__.py new file mode 100644 index 000000000..2d640cb89 --- /dev/null +++ b/sources/pyside2/PySide2/support/signature/lib/__init__.py @@ -0,0 +1,40 @@ +############################################################################# +## +## Copyright (C) 2018 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$ +## +############################################################################# + +# this file intentionally left blank diff --git a/sources/pyside2/PySide2/support/signature/lib/enum_sig.py b/sources/pyside2/PySide2/support/signature/lib/enum_sig.py new file mode 100644 index 000000000..ddc03c097 --- /dev/null +++ b/sources/pyside2/PySide2/support/signature/lib/enum_sig.py @@ -0,0 +1,134 @@ +############################################################################# +## +## Copyright (C) 2018 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 sys +from PySide2.support.signature import inspect + + +class ExactEnumerator(object): + """ + ExactEnumerator enumerates all signatures in a module as they are. + + This class is used for generating complete listings of all signatures. + An appropriate formatter should be supplied, if printable output + is desired. + """ + def __init__(self, formatter, result_type=dict): + self.fmt = formatter + self.result_type = result_type + + def module(self, mod_name): + __import__(mod_name) + with self.fmt.module(mod_name): + module = sys.modules[mod_name] + members = inspect.getmembers(module, inspect.isclass) + ret = self.result_type() + for class_name, klass in members: + ret.update(self.klass(class_name, klass)) + return ret + + def klass(self, class_name, klass): + bases_list = [] + for base in klass.__bases__: + name = base.__name__ + if name == "object": + pass + else: + modname = base.__module__ + name = modname + "." + base.__name__ + bases_list.append(name) + class_str = "{}({})".format(class_name, ", ".join(bases_list)) + with self.fmt.klass(class_name, class_str): + ret = self.function("__init__", klass) + # class_members = inspect.getmembers(klass) + # gives us also the inherited things. + class_members = sorted(list(klass.__dict__.items())) + for func_name, func in class_members: + ret.update(self.function(func_name, func)) + return ret + + def function(self, func_name, func): + ret = self.result_type() + signature = getattr(func, '__signature__', None) + if signature is not None: + with self.fmt.function(func_name, signature) as key: + ret[key] = signature + return ret + + +def simplify(signature): + if isinstance(signature, list): + # remove duplicates which still sometimes occour: + ret = set(simplify(sig) for sig in signature) + return sorted(ret) if len(ret) > 1 else list(ret)[0] + ret = [] + for pv in signature.parameters.values(): + txt = str(pv) + if ":" not in txt: # 'self' or '*args' + continue + txt = txt[txt.index(":") + 1:] + if "=" in txt: + txt = txt[:txt.index("=")] + quote = txt[0] + if quote in ("'", '"') and txt[-1] == quote: + txt = txt[1:-1] + ret.append(txt.strip()) + return tuple(ret) + + +class SimplifyingEnumerator(ExactEnumerator): + """ + SimplifyingEnumerator enumerates all signatures in a module filtered. + + There are no default values, no variable + names and no self parameter. Only types are present after simplification. + The functions 'next' resp. '__next__' are removed + to make the output identical for Python 2 and 3. + An appropriate formatter should be supplied, if printable output + is desired. + """ + + def function(self, func_name, func): + ret = self.result_type() + signature = getattr(func, '__signature__', None) + sig = simplify(signature) if signature is not None else None + if sig is not None and func_name not in ("next", "__next__"): + with self.fmt.function(func_name, sig) as key: + ret[key] = sig + return ret diff --git a/sources/pyside2/PySide2/support/signature/loader.py b/sources/pyside2/PySide2/support/signature/loader.py index f51bafe79..a055337bf 100644 --- a/sources/pyside2/PySide2/support/signature/loader.py +++ b/sources/pyside2/PySide2/support/signature/loader.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. @@ -82,13 +82,17 @@ sys.path.pop(0) # break the Python license decorated files without an encoding line. # name used in signature.cpp -def create_signature(props, sig_kind): +def create_signature(props, key): if not props: # empty signatures string return if isinstance(props["multi"], list): - return list(create_signature(elem, sig_kind) + return list(create_signature(elem, key) for elem in props["multi"]) + if type(key) is tuple: + sig_kind, modifier = key + else: + sig_kind, modifier = key, None varnames = props["varnames"] if sig_kind == "method": varnames = ("self",) + varnames diff --git a/sources/pyside2/PySide2/support/signature/mapping.py b/sources/pyside2/PySide2/support/signature/mapping.py index dd3df0988..6b7d1ad01 100644 --- a/sources/pyside2/PySide2/support/signature/mapping.py +++ b/sources/pyside2/PySide2/support/signature/mapping.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. @@ -46,7 +46,7 @@ This module has the mapping from the pyside C-modules view of signatures to the Python representation. The PySide modules are not loaded in advance, but only after they appear -in sys.modules. This minimises the loading overhead. +in sys.modules. This minimizes the loading overhead. In principle, we need to re-load the module, when the imports change. But it is much easier to do it on demand, when we get an exception. See _resolve_value() in singature.py @@ -71,7 +71,6 @@ FloatMatrix = typing.List[typing.List[float]] # Pair could be more specific, but we loose the info in the generator. Pair = typing.Tuple[typing.Any, typing.Any] MultiMap = typing.DefaultDict[str, typing.List[str]] -Text = typing.Text # ulong_max is only 32 bit on windows. ulong_max = 2*sys.maxsize+1 if len(struct.pack("L", 1)) != 4 else 0xffffffff @@ -153,7 +152,7 @@ type_map = {} def init_QtCore(): import PySide2.QtCore - from PySide2.QtCore import Qt, QUrl, QDir, QGenericArgument + from PySide2.QtCore import Qt, QUrl, QDir from PySide2.QtCore import QRect, QSize, QPoint, QLocale, QByteArray from PySide2.QtCore import QMarginsF # 5.9 try: @@ -201,9 +200,8 @@ def init_QtCore(): "ULONG_MAX": ulong_max, "quintptr": int, "PyCallable": typing.Callable, - "...": ellipsis, # no idea how this should be translated... maybe so? "PyTypeObject": type, - "PySequence": typing.Sequence, + "PySequence": typing.Iterable, # important for numpy "qptrdiff": int, "true": True, "Qt.HANDLE": int, # be more explicit with some consts? @@ -242,7 +240,7 @@ def init_QtCore(): "QDir.SortFlags(QDir.Name | QDir.IgnoreCase)"), "PyBytes": bytes, "PyByteArray": bytearray, - "PyUnicode": Text, + "PyUnicode": typing.Text, "signed long": int, "PySide2.QtCore.int": int, "PySide2.QtCore.char": StringList, # A 'char **' is a list of strings. @@ -259,13 +257,13 @@ def init_QtCore(): "float[][]": FloatMatrix, # 5.9 "PySide2.QtCore.unsigned int": int, # 5.9 Ubuntu "PySide2.QtCore.long long": int, # 5.9, MSVC 15 - "QGenericArgument(nullptr)": QGenericArgument(None), # 5.10 + "QGenericArgument(nullptr)": ellipsis, # 5.10 "QModelIndex()": Invalid("PySide2.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?! - "QGenericArgument((0))": None, # 5.6, RHEL 6.6. Is that ok? - "QGenericArgument()": None, - "QGenericArgument(0)": None, - "QGenericArgument(NULL)": None, # 5.6, MSVC - "QGenericArgument(Q_NULLPTR)": None, + "QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok? + "QGenericArgument()": ellipsis, + "QGenericArgument(0)": ellipsis, + "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC + "QGenericArgument(Q_NULLPTR)": ellipsis, "zero(PySide2.QtCore.QObject)": None, "zero(PySide2.QtCore.QThread)": None, "zero(quintptr)": 0, @@ -289,6 +287,8 @@ def init_QtCore(): "zero(PySide2.QtCore.QEvent.Type)": None, "CheckIndexOption.NoOption": Instance( "PySide2.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11 + "QVariantMap": dict, + "PySide2.QtCore.QCborStreamReader.StringResult": typing.AnyStr, }) try: type_map.update({ @@ -301,7 +301,6 @@ def init_QtCore(): def init_QtGui(): import PySide2.QtGui - from PySide2.QtGui import QPageLayout, QPageSize # 5.9 type_map.update({ "QVector< QTextLayout.FormatRange >()": [], # do we need more structure? "USHRT_MAX": ushort_max, @@ -313,7 +312,7 @@ def init_QtGui(): "GL_COLOR_BUFFER_BIT": GL_COLOR_BUFFER_BIT, "GL_NEAREST": GL_NEAREST, "WId": WId, - "PySide2.QtGui.QPlatformSurface": Virtual("PySide2.QtGui.QPlatformSurface"), # hmm... + "PySide2.QtGui.QPlatformSurface": int, # a handle "QList< QTouchEvent.TouchPoint >()": [], # XXX improve? "QPixmap()": Default("PySide2.QtGui.QPixmap"), # can't create without qApp "PySide2.QtCore.uint8_t": int, # macOS 5.9 @@ -325,6 +324,7 @@ def init_QtGui(): "zero(PySide2.QtGui.QTextLayout.FormatRange)": None, "zero(PySide2.QtGui.QTouchDevice)": None, "zero(PySide2.QtGui.QScreen)": None, + "PySide2.QtGui.QGenericMatrix": Missing("PySide2.QtGui.QGenericMatrix"), }) return locals() @@ -396,7 +396,6 @@ def init_QtMultimedia(): import PySide2.QtMultimedia import PySide2.QtMultimediaWidgets type_map.update({ - "QVariantMap": dict, "QGraphicsVideoItem": PySide2.QtMultimediaWidgets.QGraphicsVideoItem, "QVideoWidget": PySide2.QtMultimediaWidgets.QVideoWidget, }) diff --git a/sources/pyside2/PySide2/support/signature/parser.py b/sources/pyside2/PySide2/support/signature/parser.py index 9313fb540..dd6640fde 100644 --- a/sources/pyside2/PySide2/support/signature/parser.py +++ b/sources/pyside2/PySide2/support/signature/parser.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. @@ -48,6 +48,7 @@ import functools from .mapping import type_map, update_mapping, __dict__ as namespace _DEBUG = False +LIST_KEYWORDS = False """ parser.py @@ -119,6 +120,8 @@ def _parse_line(line): for arg in arglist: name, ann = arg.split(":") if name in keyword.kwlist: + if LIST_KEYWORDS: + print("KEYWORD", ret) name = name + "_" if "=" in ann: ann, default = ann.split("=") @@ -130,6 +133,10 @@ def _parse_line(line): multi = ret["multi"] if multi is not None: ret["multi"] = int(multi) + funcname = ret["funcname"] + parts = funcname.split(".") + if parts[-1] in keyword.kwlist: + ret["funcname"] = funcname + "_" return ret def make_good_value(thing, valtype): @@ -192,8 +199,14 @@ def calculate_props(line): arglist = res["arglist"] annotations = {} _defaults = [] - for tup in arglist: + for idx, tup in enumerate(arglist): name, ann = tup[:2] + if ann == "...": + name = "*args" + # copy the fields back :() + ann = 'NULL' # maps to None + tup = name, ann + arglist[idx] = tup annotations[name] = _resolve_type(ann, line) if len(tup) == 3: default = _resolve_value(tup[2], ann, line) @@ -214,6 +227,31 @@ def calculate_props(line): props["multi"] = res["multi"] return props +def fixup_multilines(sig_str): + lines = list(line.strip() for line in sig_str.strip().splitlines()) + res = [] + multi_lines = [] + for line in lines: + multi = re.match(r"([0-9]+):", line) + if multi: + idx, rest = int(multi.group(1)), line[multi.end():] + multi_lines.append(rest) + if idx > 0: + continue + # remove duplicates + multi_lines = list(set(multi_lines)) + # renumber or return a single line + nmulti = len(multi_lines) + if nmulti > 1: + for idx, line in enumerate(multi_lines): + res.append("{}:{}".format(nmulti-idx-1, line)) + else: + res.append(multi_lines[0]) + multi_lines = [] + else: + res.append(line) + return res + def pyside_type_init(typemod, sig_str): dprint() if type(typemod) is types.ModuleType: @@ -222,9 +260,10 @@ def pyside_type_init(typemod, sig_str): dprint("Initialization of type '{}.{}'".format(typemod.__module__, typemod.__name__)) update_mapping() + lines = fixup_multilines(sig_str) ret = {} multi_props = [] - for line in sig_str.strip().splitlines(): + for line in lines: props = calculate_props(line) shortname = props["name"] multi = props["multi"] @@ -232,10 +271,10 @@ def pyside_type_init(typemod, sig_str): ret[shortname] = props dprint(props) else: - fullname = props.pop("fullname") multi_props.append(props) if multi > 0: continue + fullname = props.pop("fullname") multi_props = {"multi": multi_props, "fullname": fullname} ret[shortname] = multi_props dprint(multi_props) diff --git a/sources/pyside2/tests/registry/init_platform.py b/sources/pyside2/tests/registry/init_platform.py index 03d0ed133..22875a63e 100644 --- a/sources/pyside2/tests/registry/init_platform.py +++ b/sources/pyside2/tests/registry/init_platform.py @@ -55,9 +55,10 @@ from textwrap import dedent all_modules = list("PySide2." + x for x in PySide2.__all__) -from PySide2.support.signature import inspect from PySide2.QtCore import __version__ +from PySide2.support.signature.lib.enum_sig import SimplifyingEnumerator + is_py3 = sys.version_info[0] == 3 is_ci = os.environ.get("QTEST_ENVIRONMENT", "") == "ci" # Python2 legacy: Correct 'linux2' to 'linux', recommended way. @@ -114,7 +115,7 @@ class Formatter(object): Formatter is formatting the signature listing of an enumerator. It is written as context managers in order to avoid many callbacks. - The division in formatter and enumerator is done to keep the + The separation in formatter and enumerator is done to keep the unrelated tasks of enumeration and formatting apart. """ def __init__(self, outfile): @@ -134,7 +135,7 @@ class Formatter(object): self.print(" })") @contextmanager - def klass(self, class_name): + def klass(self, class_name, class_str): self.class_name = class_name self.print() self.print(" # class {}.{}:".format(self.mod_name, class_name)) @@ -152,89 +153,6 @@ class Formatter(object): yield key -class ExactEnumerator(object): - """ - ExactEnumerator enumerates all signatures in a module as they are. - - This class is used for generating complete listings of all signatures. - An appropriate formatter should be supplied, if printable output - is desired. - """ - def __init__(self, formatter, result_type=dict): - self.fmt = formatter - self.result_type = result_type - - def module(self, mod_name): - __import__(mod_name) - with self.fmt.module(mod_name): - module = sys.modules[mod_name] - members = inspect.getmembers(module, inspect.isclass) - ret = self.result_type() - for class_name, klass in members: - ret.update(self.klass(class_name, klass)) - return ret - - def klass(self, class_name, klass): - with self.fmt.klass(class_name): - ret = self.function("__init__", klass) - # class_members = inspect.getmembers(klass) - # gives us also the inherited things. - class_members = sorted(list(klass.__dict__.items())) - for func_name, func in class_members: - ret.update(self.function(func_name, func)) - return ret - - def function(self, func_name, func): - ret = self.result_type() - signature = getattr(func, '__signature__', None) - if signature is not None: - with self.fmt.function(func_name, signature) as key: - ret[key] = signature - return ret - - -def simplify(signature): - if isinstance(signature, list): - # remove duplicates which still sometimes occour: - ret = set(simplify(sig) for sig in signature) - return sorted(ret) if len(ret) > 1 else list(ret)[0] - ret = [] - for pv in signature.parameters.values(): - txt = str(pv) - if txt == "self": - continue - txt = txt[txt.index(":") + 1:] - if "=" in txt: - txt = txt[:txt.index("=")] - quote = txt[0] - if quote in ("'", '"') and txt[-1] == quote: - txt = txt[1:-1] - ret.append(txt) - return tuple(ret) - - -class SimplifyingEnumerator(ExactEnumerator): - """ - SimplifyingEnumerator enumerates all signatures in a module filtered. - - There are no default values, no variable - names and no self parameter. Only types are present after simplification. - The functions 'next' resp. '__next__' are removed - to make the output identical for Python 2 and 3. - An appropriate formatter should be supplied, if printable output - is desired. - """ - - def function(self, func_name, func): - ret = self.result_type() - signature = getattr(func, '__signature__', None) - sig = simplify(signature) if signature is not None else None - if sig is not None and func_name not in ("next", "__next__"): - with self.fmt.function(func_name, sig) as key: - ret[key] = sig - return ret - - def enum_all(): fmt = Formatter(None) enu = SimplifyingEnumerator(fmt) diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp index 4481f1cdd..18094c5c0 100644 --- a/sources/shiboken2/libshiboken/pep384impl.cpp +++ b/sources/shiboken2/libshiboken/pep384impl.cpp @@ -398,7 +398,10 @@ PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals) return ret; } +#endif // Py_LIMITED_API + // This is only a simple local helper that returns a computed variable. +// Used also in Python 2. static PyObject * PepRun_GetResult(const char *command, const char *resvar) { @@ -415,6 +418,8 @@ PepRun_GetResult(const char *command, const char *resvar) return res; } +#ifdef Py_LIMITED_API + /***************************************************************************** * * Support for classobject.h @@ -499,13 +504,26 @@ PyTypeObject *PepStaticMethod_TypePtr = NULL; static PyTypeObject *getStaticMethodType(void) { + // this works for Python 3, only + // "StaticMethodType = type(str.__dict__['maketrans'])\n"; static const char prog[] = - "StaticMethodType = type(str.__dict__['maketrans'])\n"; - return (PyTypeObject *) PepRun_GetResult(prog, "StaticMethodType"); + "from xxsubtype import spamlist\n" + "StaticMethod_Type = type(spamlist.__dict__['staticmeth'])\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "StaticMethod_Type"); } - #endif // Py_LIMITED_API +#if PY_VERSION_HEX < 0x03000000 +PyTypeObject *PepMethodDescr_TypePtr = NULL; + +static PyTypeObject *getMethodDescrType(void) +{ + static const char prog[] = + "MethodDescr_Type = type(str.split)\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "MethodDescr_Type"); +} +#endif + /***************************************************************************** * * Common newly needed functions @@ -630,6 +648,9 @@ Pep384_Init() PepFunction_TypePtr = getFunctionType(); PepStaticMethod_TypePtr = getStaticMethodType(); #endif +#if PY_VERSION_HEX < 0x03000000 + PepMethodDescr_TypePtr = getMethodDescrType(); +#endif } } // extern "C" diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h index b566a6218..6649fa95e 100644 --- a/sources/shiboken2/libshiboken/pep384impl.h +++ b/sources/shiboken2/libshiboken/pep384impl.h @@ -286,7 +286,7 @@ LIBSHIBOKEN_API PyObject *PyRun_String(const char *, int, PyObject *, PyObject * // But this is no problem as we check it's validity for every version. #define PYTHON_BUFFER_VERSION_COMPATIBLE (PY_VERSION_HEX >= 0x03030000 && \ - PY_VERSION_HEX < 0X0307FFFF) + PY_VERSION_HEX < 0x0307FFFF) #if !PYTHON_BUFFER_VERSION_COMPATIBLE # error Please check the buffer compatibility for this python version! #endif @@ -470,6 +470,12 @@ extern LIBSHIBOKEN_API PyTypeObject *PepStaticMethod_TypePtr; #else #define PepStaticMethod_TypePtr &PyStaticMethod_Type #endif +// Although not PEP specific, we resolve this similar issue, here: +#if PY_VERSION_HEX < 0x03000000 +extern LIBSHIBOKEN_API PyTypeObject *PepMethodDescr_TypePtr; +#else +#define PepMethodDescr_TypePtr &PyMethodDescr_Type +#endif /***************************************************************************** * diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index f0bb8e609..24d60acc4 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -77,18 +77,23 @@ typedef struct safe_globals_struc { static safe_globals pyside_globals = 0; -static PyObject *GetSignature_Function(PyCFunctionObject *); -static PyObject *GetSignature_TypeMod(PyObject *); +static PyObject *GetSignature_Function(PyCFunctionObject *, const char *); +static PyObject *GetSignature_TypeMod(PyObject *, const char *); +static PyObject *GetSignature_Wrapper(PyObject *, const char *); +static PyObject *get_signature(PyObject *self, PyObject *args); static PyObject *PySide_BuildSignatureProps(PyObject *class_mod); +static void init_module_1(void); +static void init_module_2(void); + const char helper_module_name[] = "signature_loader"; const char bootstrap_name[] = "bootstrap"; const char arg_name[] = "pyside_arg_dict"; const char func_name[] = "pyside_type_init"; static PyObject * -CreateSignature(PyObject *props, const char *sig_kind) +CreateSignature(PyObject *props, PyObject *key) { /* * Here is the new function to create all signatures. It simply calls @@ -97,22 +102,22 @@ CreateSignature(PyObject *props, const char *sig_kind) * to support '_signature_is_functionlike()'. */ return PyObject_CallFunction(pyside_globals->createsig_func, - (char *)"(Os)", props, sig_kind); + (char *)"(OO)", props, key); } static PyObject * -pyside_cf_get___signature__(PyObject *func) +pyside_cf_get___signature__(PyObject *func, const char *modifier) { - return GetSignature_Function((PyCFunctionObject *)func); + return GetSignature_Function((PyCFunctionObject *)func, modifier); } static PyObject * -pyside_sm_get___signature__(PyObject *sm) +pyside_sm_get___signature__(PyObject *sm, const char *modifier) { PyObject *func, *ret; func = PyObject_GetAttrString(sm, "__func__"); - ret = GetSignature_Function((PyCFunctionObject *)func); + ret = GetSignature_Function((PyCFunctionObject *)func, modifier); Py_XDECREF(func); return ret; } @@ -192,7 +197,7 @@ qualname_to_func(PyObject *ob) #endif static PyObject * -pyside_md_get___signature__(PyObject *ob) +pyside_md_get___signature__(PyObject *ob, const char *modifier) { PyObject *func; PyObject *result; @@ -218,21 +223,31 @@ pyside_md_get___signature__(PyObject *ob) return Py_None; if (func == NULL) Py_FatalError("missing mapping in MethodDescriptor"); - result = pyside_cf_get___signature__(func); + result = pyside_cf_get___signature__(func, modifier); Py_DECREF(func); return result; } static PyObject * -pyside_tp_get___signature__(PyObject *typemod) +pyside_wd_get___signature__(PyObject *ob, const char *modifier) +{ + return GetSignature_Wrapper(ob, modifier); +} + +static PyObject * +pyside_tp_get___signature__(PyObject *typemod, const char *modifier) { - return GetSignature_TypeMod(typemod); + return GetSignature_TypeMod(typemod, modifier); } +// forward +static PyObject * +GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier); + static PyObject * -GetSignature_Function(PyCFunctionObject *func) +GetSignature_Function(PyCFunctionObject *func, const char *modifier) { - PyObject *typemod, *type_name, *dict, *props, *value, *selftype; + PyObject *typemod, *type_name, *dict, *props, *selftype; PyObject *func_name = PyObject_GetAttrString((PyObject *)func, "__name__"); const char *sig_kind; int flags; @@ -241,12 +256,8 @@ GetSignature_Function(PyCFunctionObject *func) if (selftype == NULL) selftype = PyDict_GetItem(pyside_globals->map_dict, (PyObject *)func); if (selftype == NULL) { - if (!PyErr_Occurred()) { - PyErr_Format(PyExc_SystemError, - "the signature for \"%s\" should exist", - PepCFunction_GET_NAMESTR(func) - ); - } + if (!PyErr_Occurred()) + Py_RETURN_NONE; return NULL; } if ((PyType_Check(selftype) || PyModule_Check(selftype))) @@ -279,24 +290,46 @@ GetSignature_Function(PyCFunctionObject *func) sig_kind = "staticmethod"; else sig_kind = "method"; - value = PyDict_GetItemString(props, sig_kind); - if (value == NULL) { - // we need to compute a signature object - value = CreateSignature(props, sig_kind); - if (value != NULL) { - if (PyDict_SetItemString(props, sig_kind, value) < 0) - return NULL; - } - else + return GetSignature_Cached(props, sig_kind, modifier); +} + +static PyObject * +GetSignature_Wrapper(PyObject *ob, const char *modifier) +{ + PyObject *dict, *props; + PyObject *func_name = PyObject_GetAttrString(ob, "__name__"); + PyObject *objclass = PyObject_GetAttrString(ob, "__objclass__"); + PyObject *class_name = PyObject_GetAttrString(objclass, "__name__"); + const char *sig_kind; + + if (func_name == nullptr || objclass == nullptr || class_name == nullptr) + return nullptr; + dict = PyDict_GetItem(pyside_globals->arg_dict, class_name); + if (dict == NULL) + Py_RETURN_NONE; + if (PyTuple_Check(dict)) { + /* + * We do the initialization lazily. + * This has also the advantage that we can freely import PySide. + */ + dict = PySide_BuildSignatureProps(objclass); + if (dict == NULL) Py_RETURN_NONE; } - return Py_INCREF(value), value; + props = PyDict_GetItem(dict, func_name); + Py_DECREF(func_name); + Py_DECREF(objclass); + Py_DECREF(class_name); + if (props == NULL) + Py_RETURN_NONE; + sig_kind = "method"; + return GetSignature_Cached(props, sig_kind, modifier); } static PyObject * -GetSignature_TypeMod(PyObject *ob) +GetSignature_TypeMod(PyObject *ob, const char *modifier) { - PyObject *ob_name, *dict, *props, *value; + PyObject *ob_name, *dict, *props; const char *sig_kind; ob_name = PyObject_GetAttrString(ob, "__name__"); @@ -314,37 +347,62 @@ GetSignature_TypeMod(PyObject *ob) if (props == NULL) Py_RETURN_NONE; sig_kind = "method"; - value = PyDict_GetItemString(props, sig_kind); - if (value == NULL) { + return GetSignature_Cached(props, sig_kind, modifier); +} + +static PyObject * +GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier) +{ + PyObject *key, *value; + + if (modifier == nullptr) + key = Py_BuildValue("s", sig_kind); + else + key = Py_BuildValue("(ss)", sig_kind, modifier); + if (key == nullptr) + return nullptr; + value = PyDict_GetItem(props, key); + if (value == nullptr) { // we need to compute a signature object - value = CreateSignature(props, sig_kind); - if (value != NULL) { - if (PyDict_SetItemString(props, sig_kind, value) < 0) - return NULL; + value = CreateSignature(props, key); + if (value != nullptr) { + if (PyDict_SetItem(props, key, value) < 0) { + // this is an error + Py_DECREF(key); + return nullptr; + } } - else + else { + // key not found + Py_DECREF(key); Py_RETURN_NONE; + } } return Py_INCREF(value), value; } - static const char PySide_PythonCode[] = - "from __future__ import print_function, absolute_import\n" - "import sys, os, traceback\n" - - "pyside_package_dir = os.environ.get('PYSIDE_PACKAGE_DIR', '.')\n" - "__file__ = os.path.join(pyside_package_dir, 'support', 'signature', 'loader.py')\n" - - "def bootstrap():\n" - " try:\n" - " with open(__file__) as _f:\n" - " exec(compile(_f.read(), __file__, 'exec'))\n" - " except Exception as e:\n" - " print('Exception:', e)\n" - " traceback.print_exc(file=sys.stdout)\n" - " globals().update(locals())\n" - ; + "from __future__ import print_function, absolute_import\n" R"~(if True: + + import sys, os, traceback + + pyside_package_dir = os.environ.get('PYSIDE_PACKAGE_DIR') + if pyside_package_dir is None: + # This happens in shiboken running ctest. + from distutils.sysconfig import get_python_lib + pyside_package_dir = os.path.join(get_python_lib(), 'PySide2') + __file__ = os.path.join(pyside_package_dir, 'support', 'signature', 'loader.py') + + def bootstrap(): + try: + with open(__file__) as _f: + exec(compile(_f.read(), __file__, 'exec')) + except Exception as e: + print('Exception:', e) + traceback.print_exc(file=sys.stdout) + globals().update(locals()) + + )~"; static safe_globals_struc * init_phase_1(void) @@ -387,9 +445,10 @@ error: } static int -init_phase_2(safe_globals_struc *p) +init_phase_2(safe_globals_struc *p, PyMethodDef *methods) { - PyObject *bootstrap_func; + PyObject *bootstrap_func, *v = nullptr; + PyMethodDef *ml; bootstrap_func = PyObject_GetAttrString(p->helper_module, bootstrap_name); if (bootstrap_func == NULL) @@ -403,9 +462,22 @@ init_phase_2(safe_globals_struc *p) p->createsig_func = PyObject_GetAttrString(p->helper_module, "create_signature"); if (p->createsig_func == NULL) goto error; + + // The single function to be called, but maybe more to come. + for (ml = methods; ml->ml_name != NULL; ml++) { + v = PyCFunction_NewEx(ml, nullptr, nullptr); + if (v == nullptr) { + goto error; + } + if (PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0) { + goto error; + } + Py_DECREF(v); + } return 0; error: + Py_XDECREF(v); PyErr_SetString(PyExc_SystemError, "could not initialize part 2"); return -1; } @@ -413,8 +485,11 @@ error: static int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp) { - PyObject *dict = type->tp_dict; + PyObject *dict; + assert(PyType_Check(type)); + PyType_Ready(type); + dict = type->tp_dict; for (; gsp->name != NULL; gsp++) { PyObject *descr; if (PyDict_GetItemString(dict, gsp->name)) @@ -463,6 +538,45 @@ static PyGetSetDef new_PyType_getsets[] = { {0} }; +static PyGetSetDef new_PyWrapperDescr_getsets[] = { + {(char *) "__signature__", (getter)pyside_wd_get___signature__}, + {0} +}; + +//////////////////////////////////////////////////////////////////////////// +// +// get_signature -- providing a superior interface +// +// Additionally to the interface via __signature__, we also provide +// a general function, which allows for different signature layouts. +// The "modifier" argument is a string that is passed in from loader.py . +// Configuration what the modifiers mean is completely in Python. +// + +static PyObject * +get_signature(PyObject *self, PyObject *args) +{ + PyObject *ob; + const char *modifier = nullptr; + + init_module_1(); + init_module_2(); + + if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier)) + return NULL; + if (Py_TYPE(ob) == &PyCFunction_Type) + return pyside_cf_get___signature__(ob, modifier); + if (Py_TYPE(ob) == PepStaticMethod_TypePtr) + return pyside_sm_get___signature__(ob, modifier); + if (Py_TYPE(ob) == PepMethodDescr_TypePtr) + return pyside_md_get___signature__(ob, modifier); + if (PyType_Check(ob)) + return pyside_tp_get___signature__(ob, modifier); + if (Py_TYPE(ob) == &PyWrapperDescr_Type) + return pyside_wd_get___signature__(ob, modifier); + Py_RETURN_NONE; +} + //////////////////////////////////////////////////////////////////////////// // // This special Type_Ready does certain initializations earlier with @@ -497,23 +611,23 @@ void handler(int sig) { static int PySideType_Ready(PyTypeObject *type) { - PyObject *md; + PyObject *md, *wd; static int init_done = 0; if (!init_done) { - // Python2 does not expose certain types. We look them up: - // PyMethodDescr_Type 'type(str.__dict__["split"])' - // PyClassMethodDescr_Type. 'type(dict.__dict__["fromkeys"])' - // The latter is not needed until we use class methods in PySide. - md = PyObject_GetAttrString((PyObject *)&PyString_Type, "split"); - if (md == NULL + md = PyObject_GetAttrString((PyObject *)&PyString_Type, "split"); // method-descriptor + wd = PyObject_GetAttrString((PyObject *)Py_TYPE(Py_True), "__add__"); // wrapper-descriptor + if (md == nullptr || wd == nullptr || PyType_Ready(Py_TYPE(md)) < 0 - || add_more_getsets(Py_TYPE(md), new_PyMethodDescr_getsets) < 0 + || add_more_getsets(PepMethodDescr_TypePtr, new_PyMethodDescr_getsets) < 0 || add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets) < 0 || add_more_getsets(PepStaticMethod_TypePtr, new_PyStaticMethod_getsets) < 0 - || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0) + || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0 + || add_more_getsets(Py_TYPE(wd), new_PyWrapperDescr_getsets) < 0 + ) return -1; Py_DECREF(md); + Py_DECREF(wd); #ifndef _WIN32 // We enable the stack trace in CI, only. const char *testEnv = getenv("QTEST_ENVIRONMENT"); @@ -550,20 +664,26 @@ build_func_to_type(PyObject *obtype) return 0; } +static void +init_module_1(void) +{ + static int init_done = 0; + + if (!init_done) { + pyside_globals = init_phase_1(); + if (pyside_globals != nullptr) + init_done = 1; + } +} + static int PySide_BuildSignatureArgs(PyObject *module, PyObject *type, const char *signatures) { PyObject *type_name, *arg_tup; const char *name = NULL; - static int init_done = 0; - if (!init_done) { - pyside_globals = init_phase_1(); - if (pyside_globals == NULL) - return -1; - init_done = 1; - } + init_module_1();; arg_tup = Py_BuildValue("(Os)", type, signatures); if (arg_tup == NULL) return -1; @@ -599,23 +719,34 @@ PySide_BuildSignatureArgs(PyObject *module, PyObject *type, return 0; } -static PyObject * -PySide_BuildSignatureProps(PyObject *classmod) +static PyMethodDef signature_methods[] = { + {"get_signature", (PyCFunction)get_signature, METH_VARARGS, + "get the __signature__, but pass an optional string parameter"}, + {NULL, NULL} +}; + +static void +init_module_2(void) { - PyObject *arg_tup, *dict, *type_name; static int init_done = 0; if (!init_done) { - if (init_phase_2(pyside_globals) < 0) - return NULL; + init_phase_2(pyside_globals, signature_methods); init_done = 1; } +} + +static PyObject * +PySide_BuildSignatureProps(PyObject *classmod) +{ + PyObject *arg_tup, *dict, *type_name; /* * Here is the second part of the function. * This part will be called on-demand when needed by some attribute. * We simply pick up the arguments that we stored here and replace * them by the function result. */ + init_module_2(); type_name = PyObject_GetAttrString(classmod, "__name__"); if (type_name == NULL) return NULL; diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h index 3a229cb5c..b65317662 100644 --- a/sources/shiboken2/libshiboken/signature.h +++ b/sources/shiboken2/libshiboken/signature.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -45,8 +45,8 @@ extern "C" { -LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char*); -LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char*); +LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char *); //WS +LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *); } // extern "C" diff --git a/sources/shiboken2/libshiboken/typespec.cpp b/sources/shiboken2/libshiboken/typespec.cpp index 9123a09e0..a67daf12d 100644 --- a/sources/shiboken2/libshiboken/typespec.cpp +++ b/sources/shiboken2/libshiboken/typespec.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include "sbkpython.h" #include "typespec.h" #include -- cgit v1.2.3