diff options
9 files changed, 562 insertions, 277 deletions
diff --git a/.gitignore b/.gitignore index b8977159c..c4bc8c8f8 100644 --- a/.gitignore +++ b/.gitignore @@ -69,7 +69,7 @@ develop-eggs/ downloads/ eggs/ .eggs/ -lib/ +/lib/ lib64/ parts/ sdist/ diff --git a/sources/pyside2/PySide2/support/generate_pyi.py b/sources/pyside2/PySide2/support/generate_pyi.py index 294cdc91b..c732227f4 100644 --- a/sources/pyside2/PySide2/support/generate_pyi.py +++ b/sources/pyside2/PySide2/support/generate_pyi.py @@ -252,6 +252,10 @@ def generate_all_pyi(outpath, options): from PySide2.support.signature import inspect from PySide2.support.signature.lib.enum_sig import HintingEnumerator + # propagate USE_PEP563 to the mapping module. + # Perhaps this can be automated? + PySide2.support.signature.mapping.USE_PEP563 = USE_PEP563 + outpath = outpath or os.path.dirname(PySide2.__file__) name_list = PySide2.__all__ if options.modules == ["all"] else options.modules errors = ", ".join(set(name_list) - set(PySide2.__all__)) diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index 09731240f..057a995f8 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -61,6 +61,8 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/ "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/lib/__init__.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/lib/enum_sig.py" "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/lib/enum_sig.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/lib/tool.py" + "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/lib/tool.py" COPYONLY) if (PYTHON_VERSION_MAJOR EQUAL 3) else() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/backport_inspect.py" diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py index c43d6d076..bd827f1ee 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py @@ -58,20 +58,7 @@ used literally as strings like "signature", "existence", etc. from textwrap import dedent from shibokensupport.signature import inspect from shibokensupport.signature.mapping import ellipsis - - -class SimpleNamespace(object): - # From types.rst, because the builtin is implemented in Python 3, only. - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - def __repr__(self): - keys = sorted(self.__dict__) - items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) - return "{}({})".format(type(self).__name__, ", ".join(items)) - - def __eq__(self, other): - return self.__dict__ == other.__dict__ +from shibokensupport.signature.lib.tool import SimpleNamespace class SignatureLayout(SimpleNamespace): diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py new file mode 100644 index 000000000..b34bfb404 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py @@ -0,0 +1,135 @@ +############################################################################# +## +## 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 + +""" +tool.py + +Some useful stuff, see below. +""" + +from textwrap import dedent + + +class SimpleNamespace(object): + # From types.rst, because the builtin is implemented in Python 3, only. + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __repr__(self): + keys = sorted(self.__dict__) + items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) + return "{}({})".format(type(self).__name__, ", ".join(items)) + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + +try: + from types import SimpleNamespace +except ImportError: + pass + + +def build_brace_pattern(level, separators=""): + """ + Build a brace pattern upto a given depth + + The brace pattern parses any pattern with round, square, curly, or angle + brackets. Inside those brackets, any characters are allowed. + + The structure is quite simple and is recursively repeated as needed. + When separators are given, the match stops at that separator. + + Reason to use this instead of some Python function: + The resulting regex is _very_ fast! + + A faster replacement would be written in C, but this solution is + sufficient when the nesting level is not too large. + + Because of the recursive nature of the pattern, the size grows by a factor + of 4 at every level, as does the creation time. Up to a level of 6, this + is below 10 ms. + + There are other regex engines available which allow recursive patterns, + avoiding this problem completely. It might be considered to switch to + such an engine if the external module is not a problem. + """ + def escape(str): + return "".join("\\" + c for c in str) + + ro, rc = round = "()" + so, sc = square = "[]" + co, cc = curly = "CD" # we insert "{}", later... + ao, ac = angle = "<>" + qu, bs = '"', "\\" + all = round + square + curly + angle + __ = " " + ro, rc, so, sc, co, cc, ao, ac, separators, qu, bs, all = map( + escape, (ro, rc, so, sc, co, cc, ao, ac, separators, qu, bs, all)) + + no_brace_sep_q = r"[^{all}{separators}{qu}{bs}]".format(**locals()) + no_quote = r"(?: [^{qu}{bs}] | {bs}. )*".format(**locals()) + pattern = dedent(r""" + ( + (?: {__} {no_brace_sep_q} + | {qu} {no_quote} {qu} + | {ro} {replacer} {rc} + | {so} {replacer} {sc} + | {co} {replacer} {cc} + | {ao} {replacer} {ac} + )* + ) + """) + no_braces_q = "[^{all}{qu}{bs}]*".format(**locals()) + repeated = dedent(r""" + {indent} (?: {__} {no_braces_q} + {indent} | {qu} {no_quote} {qu} + {indent} | {ro} {replacer} {rc} + {indent} | {so} {replacer} {sc} + {indent} | {co} {replacer} {cc} + {indent} | {ao} {replacer} {ac} + {indent} )* + """) + for idx in range(level): + pattern = pattern.format(replacer = repeated if idx < level-1 else no_braces_q, + indent = idx * " ", **locals()) + return pattern.replace("C", "{").replace("D", "}") + +# eof diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 5f92446cf..b8097719a 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -61,24 +61,13 @@ class ellipsis(object): return "..." ellipsis = ellipsis() -StringList = typing.List[str] -IntList = typing.List[int] Point = typing.Tuple[float, float] -PointList = typing.List[Point] -IntMatrix = typing.List[IntList] Variant = typing.Any ModelIndexList = typing.List[int] QImageCleanupFunction = typing.Callable +StringList = typing.List[str] -# First time installing our own Pair type into typing. -T = TypeVar('T') -S = TypeVar('S') - -class Pair(Generic[T, S]): - __module__ = "typing" - -typing.Pair = Pair - +_S = TypeVar("_S") # Building our own Char type, which is much nicer than # Char = typing.Union[str, int] # how do I model the limitation to 1 char? @@ -174,7 +163,12 @@ class _NotCalled(str): text = self if self.endswith(")") else self + "()" return eval(text, namespace) -USE_PEP563 = sys.version_info[:2] >= (3, 7) +USE_PEP563 = False +# Note: we cannot know if this feature has been imported. +# Otherwise it would be "sys.version_info[:2] >= (3, 7)". +# We *can* eventually inspect sys.modules and look if +# the calling module has this future statement set, +# but should we do that? # Some types are abstract. They just show their name. @@ -183,10 +177,11 @@ class Virtual(_NotCalled): # Other types I simply could not find. class Missing(_NotCalled): - if not USE_PEP563: - # The string must be quoted, because the object does not exist. - def __repr__(self): - return '{}("{}")'.format(type(self).__name__, self) + # The string must be quoted, because the object does not exist. + def __repr__(self): + if USE_PEP563: + return _NotCalled.__repr__(self) + return '{}("{}")'.format(type(self).__name__, self) class Invalid(_NotCalled): @@ -260,20 +255,76 @@ type_map = {} namespace = globals() # our module's __dict__ type_map.update({ + "bool": bool, + "char": Char, + "char*": str, + "char*const": str, + "double": float, + "float": float, + "int": int, + "List": typing.List, + "long": int, + "PyCallable": typing.Callable, + "PyObject": object, + "PySequence": typing.Iterable, # important for numpy + "PyTypeObject": type, + "QChar": Char, + "QHash": typing.Dict, + "qint16": int, + "qint32": int, + "qint64": int, + "qint8": int, + "qintptr": int, "QList": typing.List, - "QVector": typing.List, - "QSet": typing.Set, - "QPair": Pair, + "qlonglong": int, "QMap": typing.Dict, + "QPair": typing.Tuple, + "qptrdiff": int, + "qreal": float, + "QSet": typing.Set, + "QString": str, + "QStringList": StringList, + "quint16": int, + "quint32": int, + "quint32": int, + "quint64": int, + "quint8": int, + "quintptr": int, + "qulonglong": int, + "QVariant": Variant, + "QVector": typing.List, + "real": float, + "short": int, + "signed char": Char, + "signed long": int, + "str": str, + "true": True, + "ULONG_MAX": ulong_max, + "unsigned char": Char, + "unsigned int": int, # should we define an unsigned type? + "unsigned long int": int, # 5.6, RHEL 6.6 + "unsigned long long": int, + "unsigned long": int, + "unsigned short int": int, # 5.6, RHEL 6.6 + "unsigned short": int, + "UnsignedShortType": int, # 5.9 + "void": int, # be more specific? + "WId": WId, + "zero(bytes)": b"", + "zero(Char)": 0, + "zero(float)": 0, + "zero(int)": 0, + "zero(object)": None, + "zero(str)": "", }) # The Shiboken Part def init_Shiboken(): type_map.update({ + "PyType": type, "shiboken2.bool": bool, "size_t": int, - "PyType": type, }) return locals() @@ -288,36 +339,32 @@ def init_minimal(): def init_sample(): import datetime type_map.update({ - "double": float, - "sample.int": int, + "char": Char, "Complex": complex, - "sample.OddBool": bool, - "sample.bool": bool, - "sample.PStr": str, + "double": float, + "Foo.HANDLE": int, + "HANDLE": int, + "Null": None, + "ObjectType.Identifier": Missing("sample.ObjectType.Identifier"), "OddBool": bool, "PStr": str, - "char": Char, + "PyDate": datetime.date, + "sample.bool": bool, "sample.char": Char, - "sample.Point": Point, + "sample.double": float, + "sample.int": int, "sample.ObjectType": object, - "std.string": str, - "HANDLE": int, - "Foo.HANDLE": int, + "sample.OddBool": bool, "sample.Photon.TemplateBase": Missing("sample.Photon.TemplateBase"), - "ObjectType.Identifier": Missing("sample.ObjectType.Identifier"), - "zero(HANDLE)": 0, - "Null": None, - "zero(sample.ObjectType)": None, + "sample.Point": Point, + "sample.PStr": str, + "sample.unsigned char": Char, "std.size_t": int, - 'Str("<unknown>")': "<unknown>", + "std.string": str, + "ZeroIn": 0, 'Str("<unk")': "<unk", + 'Str("<unknown>")': "<unknown>", 'Str("nown>")': "nown>", - "zero(sample.ObjectModel)": None, - "sample.unsigned char": Char, - "sample.double": float, - "zero(sample.bool)": False, - "PyDate": datetime.date, - "ZeroIn": 0, }) return locals() @@ -325,15 +372,20 @@ def init_sample(): def init_other(): import numbers type_map.update({ - "other.Number": numbers.Number, "other.ExtendsNoImplicitConversion": Missing("other.ExtendsNoImplicitConversion"), + "other.Number": numbers.Number, }) return locals() def init_smart(): + # This missing type should be defined in module smart. We cannot set it to Missing() + # because it is a container type. Therefore, we supply a surrogate: + global SharedPtr + class SharedPtr(Generic[_S]): + __module__ = "smart" + smart.SharedPtr = SharedPtr type_map.update({ - "smart.SharedPtr": Missing("smart.SharedPtr"), # bad object "SharedPtr<Obj >" "smart.Smart.Integer2": int, }) return locals() @@ -349,131 +401,88 @@ def init_PySide2_QtCore(): except ImportError: pass type_map.update({ - "str": str, - "int": int, - "QString": str, - "bool": bool, - "PyObject": object, - "void": int, # be more specific? - "char": Char, - "'%'": "%", "' '": " ", - "false": False, - "double": float, + "'%'": "%", "'g'": "g", + "4294967295UL": 4294967295, # 5.6, RHEL 6.6 + "CheckIndexOption.NoOption": Instance( + "PySide2.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11 + "false": False, + "list of QAbstractAnimation": typing.List[PySide2.QtCore.QAbstractAnimation], + "list of QAbstractState": typing.List[PySide2.QtCore.QAbstractState], "long long": int, - "unsigned int": int, # should we define an unsigned type? - "Q_NULLPTR": None, "long": int, - "float": float, - "short": int, - "unsigned long": int, - "unsigned long long": int, - "unsigned short": int, - "QStringList": StringList, - "QChar": Char, - "signed char": Char, - "QVariant": Variant, - "QVariant.Type": type, # not so sure here... - "QStringRef": str, - "QString()": "", - "QModelIndexList": ModelIndexList, - "unsigned char": Char, - "QJsonObject": typing.Dict[str, PySide2.QtCore.QJsonValue], - "QStringList()": [], - "ULONG_MAX": ulong_max, - "quintptr": int, + "NULL": None, # 5.6, MSVC + "nullptr": None, # 5.9 + "PyByteArray": bytearray, + "PyBytes": bytes, "PyCallable": typing.Callable, - "PyTypeObject": type, + "PyObject": object, "PySequence": typing.Iterable, # important for numpy - "qptrdiff": int, - "true": True, - "Qt.HANDLE": int, # be more explicit with some consts? - "list of QAbstractState": typing.List[PySide2.QtCore.QAbstractState], - "list of QAbstractAnimation": typing.List[PySide2.QtCore.QAbstractAnimation], - "QVariant()": Invalid(Variant), "PySide2.QtCore.bool": bool, - "QHash": typing.Dict, - "PySide2.QtCore.QChar": Char, - "PySide2.QtCore.qreal": float, + "PySide2.QtCore.char": StringList, # A 'char **' is a list of strings. + "PySide2.QtCore.double": float, "PySide2.QtCore.float": float, + "PySide2.QtCore.int": int, + "PySide2.QtCore.int32_t": int, # 5.9 + "PySide2.QtCore.int64_t": int, # 5.9 + "PySide2.QtCore.long long": int, # 5.9, MSVC 15 + "PySide2.QtCore.long": int, + "PySide2.QtCore.QCborStreamReader.StringResult": typing.AnyStr, + "PySide2.QtCore.QChar": Char, "PySide2.QtCore.qint16": int, "PySide2.QtCore.qint32": int, "PySide2.QtCore.qint64": int, "PySide2.QtCore.qint8": int, + "PySide2.QtCore.qreal": float, "PySide2.QtCore.QString": str, "PySide2.QtCore.QStringList": StringList, - "PySide2.QtCore.QVariant": Variant, "PySide2.QtCore.quint16": int, "PySide2.QtCore.quint32": int, "PySide2.QtCore.quint64": int, "PySide2.QtCore.quint8": int, + "PySide2.QtCore.QUrl.ComponentFormattingOptions": + PySide2.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why??? + "PySide2.QtCore.QVariant": Variant, "PySide2.QtCore.short": int, - "PySide2.QtCore.unsigned short": int, "PySide2.QtCore.signed char": Char, "PySide2.QtCore.uchar": Char, + "PySide2.QtCore.uint32_t": int, # 5.9 "PySide2.QtCore.unsigned char": Char, # 5.9 - "PySide2.QtCore.long": int, - "PySide2.QtCore.QUrl.ComponentFormattingOptions": - PySide2.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why??? - "QUrl.FormattingOptions(PrettyDecoded)": Instance( - "QUrl.FormattingOptions(QUrl.PrettyDecoded)"), - # from 5.9 + "PySide2.QtCore.unsigned int": int, # 5.9 Ubuntu + "PySide2.QtCore.unsigned short": int, + "PyTypeObject": type, + "PyUnicode": typing.Text, + "Q_NULLPTR": None, + "QChar": Char, "QDir.Filters(AllEntries | NoDotAndDotDot)": Instance( "QDir.Filters(QDir.AllEntries | QDir.NoDotAndDotDot)"), - "NULL": None, # 5.6, MSVC "QDir.SortFlags(Name | IgnoreCase)": Instance( "QDir.SortFlags(QDir.Name | QDir.IgnoreCase)"), - "PyBytes": bytes, - "PyByteArray": bytearray, - "PyUnicode": typing.Text, - "signed long": int, - "PySide2.QtCore.int": int, - "PySide2.QtCore.char": StringList, # A 'char **' is a list of strings. - "unsigned long int": int, # 5.6, RHEL 6.6 - "unsigned short int": int, # 5.6, RHEL 6.6 - "4294967295UL": 4294967295, # 5.6, RHEL 6.6 - "PySide2.QtCore.int32_t": int, # 5.9 - "PySide2.QtCore.int64_t": int, # 5.9 - "UnsignedShortType": int, # 5.9 - "nullptr": None, # 5.9 - "uint64_t": int, # 5.9 - "PySide2.QtCore.uint32_t": int, # 5.9 - "PySide2.QtCore.unsigned int": int, # 5.9 Ubuntu - "PySide2.QtCore.long long": int, # 5.9, MSVC 15 - "QGenericArgument(nullptr)": ellipsis, # 5.10 - "QModelIndex()": Invalid("PySide2.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?! "QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok? "QGenericArgument()": ellipsis, "QGenericArgument(0)": ellipsis, "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC + "QGenericArgument(nullptr)": ellipsis, # 5.10 "QGenericArgument(Q_NULLPTR)": ellipsis, - "zero(PySide2.QtCore.QObject)": None, - "zero(PySide2.QtCore.QThread)": None, - "zero(quintptr)": 0, - "zero(str)": "", - "zero(int)": 0, - "zero(PySide2.QtCore.QState)": None, - "zero(PySide2.QtCore.bool)": False, - "zero(PySide2.QtCore.int)": 0, - "zero(void)": None, - "zero(long long)": 0, - "zero(PySide2.QtCore.QAbstractItemModel)": None, - "zero(PySide2.QtCore.QJsonParseError)": None, - "zero(double)": 0.0, - "zero(PySide2.QtCore.qint64)": 0, - "zero(PySide2.QtCore.QTextCodec.ConverterState)": None, - "zero(long long)": 0, - "zero(QImageCleanupFunction)": None, - "zero(unsigned int)": 0, - "zero(PySide2.QtCore.QPoint)": Default("PySide2.QtCore.QPoint"), - "zero(unsigned char)": 0, - "zero(PySide2.QtCore.QEvent.Type)": None, - "CheckIndexOption.NoOption": Instance( - "PySide2.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11 + "QHash": typing.Dict, + "QJsonObject": typing.Dict[str, PySide2.QtCore.QJsonValue], + "QModelIndex()": Invalid("PySide2.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?! + "QModelIndexList": ModelIndexList, + "qptrdiff": int, + "QString": str, + "QString()": "", + "QStringList": StringList, + "QStringList()": [], + "QStringRef": str, + "Qt.HANDLE": int, # be more explicit with some consts? + "quintptr": int, + "QUrl.FormattingOptions(PrettyDecoded)": Instance( + "QUrl.FormattingOptions(QUrl.PrettyDecoded)"), + "QVariant": Variant, + "QVariant()": Invalid(Variant), + "QVariant.Type": type, # not so sure here... "QVariantMap": typing.Dict[str, Variant], - "PySide2.QtCore.QCborStreamReader.StringResult": typing.AnyStr, - "PySide2.QtCore.double": float, }) try: type_map.update({ @@ -488,29 +497,21 @@ def init_PySide2_QtCore(): def init_PySide2_QtGui(): from PySide2.QtGui import QPageLayout, QPageSize # 5.12 macOS type_map.update({ - "QVector< QTextLayout.FormatRange >()": [], # do we need more structure? - "USHRT_MAX": ushort_max, "0.0f": 0.0, "1.0f": 1.0, - "uint32_t": int, - "uint8_t": int, - "int32_t": int, "GL_COLOR_BUFFER_BIT": GL_COLOR_BUFFER_BIT, "GL_NEAREST": GL_NEAREST, - "WId": WId, + "int32_t": int, + "PySide2.QtCore.uint8_t": int, # macOS 5.9 + "PySide2.QtGui.QGenericMatrix": Missing("PySide2.QtGui.QGenericMatrix"), "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 - "zero(uint32_t)": 0, - "zero(PySide2.QtGui.QWindow)": None, - "zero(PySide2.QtGui.QOpenGLContext)": None, - "zero(PySide2.QtGui.QRegion)": None, - "zero(PySide2.QtGui.QPaintDevice)": None, - "zero(PySide2.QtGui.QTextLayout.FormatRange)": None, - "zero(PySide2.QtGui.QTouchDevice)": None, - "zero(PySide2.QtGui.QScreen)": None, - "PySide2.QtGui.QGenericMatrix": Missing("PySide2.QtGui.QGenericMatrix"), + "QVector< QTextLayout.FormatRange >()": [], # do we need more structure? + "uint32_t": int, + "uint8_t": int, + "USHRT_MAX": ushort_max, + "WId": WId, }) return locals() @@ -521,28 +522,15 @@ def init_PySide2_QtWidgets(): type_map.update({ "QMessageBox.StandardButtons(Yes | No)": Instance( "QMessageBox.StandardButtons(QMessageBox.Yes | QMessageBox.No)"), + "QVector< int >()": [], "QWidget.RenderFlags(DrawWindowBackground | DrawChildren)": Instance( "QWidget.RenderFlags(QWidget.DrawWindowBackground | QWidget.DrawChildren)"), + "SH_Default": QStyleHintReturn.SH_Default, + "SO_Complex": QStyleOptionComplex.SO_Complex, + "SO_Default": QStyleOption.SO_Default, "static_cast<Qt.MatchFlags>(Qt.MatchExactly|Qt.MatchCaseSensitive)": Instance( "Qt.MatchFlags(Qt.MatchExactly | Qt.MatchCaseSensitive)"), - "QVector< int >()": [], - "WId": WId, - # from 5.9 "Type": PySide2.QtWidgets.QListWidgetItem.Type, - "SO_Default": QStyleOption.SO_Default, - "SH_Default": QStyleHintReturn.SH_Default, - "SO_Complex": QStyleOptionComplex.SO_Complex, - "zero(PySide2.QtWidgets.QWidget)": None, - "zero(PySide2.QtWidgets.QGraphicsItem)": None, - "zero(PySide2.QtCore.QEvent)": None, - "zero(PySide2.QtWidgets.QStyleOption)": None, - "zero(PySide2.QtWidgets.QStyleHintReturn)": None, - "zero(PySide2.QtWidgets.QGraphicsLayoutItem)": None, - "zero(PySide2.QtWidgets.QListWidget)": None, - "zero(PySide2.QtGui.QKeySequence)": None, - "zero(PySide2.QtWidgets.QAction)": None, - "zero(PySide2.QtWidgets.QUndoCommand)": None, - "zero(WId)": 0, }) return locals() @@ -559,9 +547,6 @@ def init_PySide2_QtSql(): def init_PySide2_QtNetwork(): type_map.update({ "QMultiMap": MultiMap, - "zero(unsigned short)": 0, - "zero(PySide2.QtCore.QIODevice)": None, - "zero(QList)": [], }) return locals() @@ -569,8 +554,8 @@ def init_PySide2_QtNetwork(): def init_PySide2_QtXmlPatterns(): from PySide2.QtXmlPatterns import QXmlName type_map.update({ + "QXmlName.NamespaceCode": Missing("PySide2.QtXmlPatterns.QXmlName.NamespaceCode"), "QXmlName.PrefixCode": Missing("PySide2.QtXmlPatterns.QXmlName.PrefixCode"), - "QXmlName.NamespaceCode": Missing("PySide2.QtXmlPatterns.QXmlName.NamespaceCode") }) return locals() @@ -588,39 +573,31 @@ def init_PySide2_QtMultimedia(): def init_PySide2_QtOpenGL(): type_map.update({ - "GLuint": int, + "GLbitfield": int, "GLenum": int, + "GLfloat": float, # 5.6, MSVC 15 "GLint": int, - "GLbitfield": int, + "GLuint": int, "PySide2.QtOpenGL.GLint": int, "PySide2.QtOpenGL.GLuint": int, - "GLfloat": float, # 5.6, MSVC 15 - "zero(PySide2.QtOpenGL.QGLContext)": None, - "zero(GLenum)": 0, - "zero(PySide2.QtOpenGL.QGLWidget)": None, }) return locals() def init_PySide2_QtQml(): type_map.update({ - "QJSValueList()": [], "PySide2.QtQml.bool volatile": bool, - # from 5.9 + "QJSValueList()": [], "QVariantHash()": typing.Dict[str, Variant], # XXX sorted? - "zero(PySide2.QtQml.QQmlContext)": None, - "zero(PySide2.QtQml.QQmlEngine)": None, }) return locals() def init_PySide2_QtQuick(): type_map.update({ - "PySide2.QtQuick.QSharedPointer": int, "PySide2.QtCore.uint": int, + "PySide2.QtQuick.QSharedPointer": int, "T": int, - "zero(PySide2.QtQuick.QQuickItem)": None, - "zero(GLuint)": 0, }) return locals() @@ -638,13 +615,6 @@ def init_PySide2_QtTest(): }) return locals() -# from 5.9 -def init_PySide2_QtWebEngineWidgets(): - type_map.update({ - "zero(PySide2.QtWebEngineWidgets.QWebEnginePage.FindFlags)": 0, - }) - return locals() - # from 5.6, MSVC def init_PySide2_QtWinExtras(): type_map.update({ diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 72ca35757..6109bceee 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -47,6 +47,8 @@ import keyword import functools from shibokensupport.signature.mapping import (type_map, update_mapping, namespace, typing, _NotCalled) +from shibokensupport.signature.lib.tool import (SimpleNamespace, + build_brace_pattern) _DEBUG = False LIST_KEYWORDS = False @@ -78,6 +80,23 @@ def dprint(*args, **kw): pprint.pprint(arg) sys.stdout.flush() + +_cache = {} + +def _parse_arglist(argstr): + # The following is a split re. The string is broken into pieces which are + # between the recognized strings. Because the re has groups, both the + # strings and the separators are returned, where the strings are not + # interesting at all: They are just the commata. + key = "_parse_arglist" + if key not in _cache: + regex = build_brace_pattern(level=3, separators=",") + _cache[key] = re.compile(regex, flags=re.VERBOSE) + split = _cache[key].split + # Note: this list is interspersed with "," and surrounded by "" + return [x.strip() for x in split(argstr) if x.strip() not in ("", ",")] + + def _parse_line(line): line_re = r""" ((?P<multi> ([0-9]+)) : )? # the optional multi-index @@ -86,38 +105,9 @@ def _parse_line(line): ( -> (?P<returntype> .*) )? # the optional return type $ """ - ret = re.match(line_re, line, re.VERBOSE).groupdict() - arglist = ret["arglist"] - # The following is a split re. The string is broken into pieces which are - # between the recognized strings. Because the re has groups, both the - # strings and the delimiters are returned, where the strings are not - # interesting at all: They are just the commata. - # Note that it is necessary to put the characters with special handling in - # the first group (comma, brace, angle bracket). - # Then they are not recognized there, and we can handle them differently - # in the following expressions. - arglist = list(x.strip() for x in re.split(r""" - ( - (?: # inner group is not capturing - [^,()<>] # no commas or braces or angle brackets - | - \( - (?: - [^()]* # or one brace pair - | - \( - [^()]* # or doubls nested pair - \) - )* - \) - | - < # or one angle bracket pair - [^<>]* - > - )+ # longest possible span - ) # this list is interspersed with "," and surrounded by "" - """, arglist, flags=re.VERBOSE) - if x.strip() not in ("", ",")) + ret = SimpleNamespace(**re.match(line_re, line, re.VERBOSE).groupdict()) + argstr = ret.arglist + arglist = _parse_arglist(argstr) args = [] for arg in arglist: name, ann = arg.split(":") @@ -131,15 +121,16 @@ def _parse_line(line): else: tup = name, ann args.append(tup) - ret["arglist"] = args - multi = ret["multi"] + ret.arglist = args + multi = ret.multi if multi is not None: - ret["multi"] = int(multi) - funcname = ret["funcname"] + ret.multi = int(multi) + funcname = ret.funcname parts = funcname.split(".") if parts[-1] in keyword.kwlist: - ret["funcname"] = funcname + "_" - return ret + ret.funcname = funcname + "_" + return vars(ret) + def make_good_value(thing, valtype): try: @@ -153,6 +144,7 @@ def make_good_value(thing, valtype): except Exception: pass + def try_to_guess(thing, valtype): if "." not in thing and "(" not in thing: text = "{}.{}".format(valtype, thing) @@ -172,9 +164,13 @@ def try_to_guess(thing, valtype): return ret return None + def _resolve_value(thing, valtype, line): if thing in ("0", "None") and valtype: - thing = "zero({})".format(valtype) + if valtype.startswith("PySide2."): + return None + name = type_map[valtype].__name__ + thing = "zero({})".format(name) if thing in type_map: return type_map[thing] res = make_good_value(thing, valtype) @@ -192,40 +188,88 @@ def _resolve_value(thing, valtype, line): """.format(thing, line), RuntimeWarning) return thing + def _resolve_arraytype(thing, line): - thing = thing[:-2] - if thing.endswith("[]"): + search = re.search(r"\[(\d*)\]$", thing) + thing = thing[:search.start()] + if thing.endswith("]"): thing = _resolve_arraytype(thing, line) - # this mapping is in shiboken - thing = "QList[" + thing + "]" + if search.group(1): + # concrete array, use a tuple + nelem = int(search.group(1)) + thing = ", ".join([thing] * nelem) + thing = "Tuple[" + thing + "]" + else: + thing = "QList[" + thing + "]" return thing + def to_string(thing): if isinstance(thing, str): return thing if hasattr(thing, "__name__"): - dot = "." in str(type(thing)) + dot = "." in str(thing) return thing.__module__ + "." + thing.__name__ if dot else thing.__name__ # Note: This captures things from the typing module: return str(thing) -def _resolve_type(thing, line): - if thing.endswith("[]"): - thing = _resolve_arraytype(thing, line) + +matrix_pattern = "PySide2.QtGui.QGenericMatrix" + +# The matrix patch is borrowed from the future (extracted). +# It will work when the parser recognizes matrices. +def handle_matrix(arg): + n, m, typstr = tuple(map(lambda x:x.strip(), arg.split(","))) + assert typstr == "float" + result = "PySide2.QtGui.QMatrix{n}x{m}".format(**locals()) + return eval(result, namespace) + + +debugging_aid = """ +from inspect import currentframe + +def lno(level): + lineno = currentframe().f_back.f_lineno + spaces = level * " " + return "{lineno}{spaces}".format(**locals()) +""" + + +def _resolve_type(thing, line, level): + # Capture total replacements, first. Happens in + # "PySide2.QtCore.QCborStreamReader.StringResult[PySide2.QtCore.QByteArray]" + if thing in type_map: + return type_map[thing] + # Now the nested structures are handled. if "[" in thing: + # handle primitive arrays + if re.search(r"\[\d*\]$", thing): + thing = _resolve_arraytype(thing, line) # Handle a container return type. (see PYSIDE-921 in cppgenerator.cpp) contr, thing = re.match(r"(.*?)\[(.*?)\]$", thing).groups() - contr = to_string(_resolve_type(contr, line)) - thing = to_string(_resolve_type(thing, line)) + # Special case: Handle the generic matrices. + if contr == matrix_pattern: + return handle_matrix(thing) + contr = _resolve_type(contr, line, level+1) + if isinstance(contr, _NotCalled): + raise SystemError("Container types must exist:", repr(contr)) + contr = to_string(contr) + pieces = [] + for part in _parse_arglist(thing): + part = _resolve_type(part, line, level+1) + if isinstance(part, _NotCalled): + # fix the tag (i.e. "Missing") by repr + part = repr(part) + pieces.append(to_string(part)) + thing = ", ".join(pieces) result = "{contr}[{thing}]".format(**locals()) - if not isinstance(thing, _NotCalled): - result = eval(result, namespace) - return result + return eval(result, namespace) return _resolve_value(thing, None, line) + def calculate_props(line): - res = _parse_line(line) - arglist = res["arglist"] + parsed = SimpleNamespace(**_parse_line(line.strip())) + arglist = parsed.arglist annotations = {} _defaults = [] for idx, tup in enumerate(arglist): @@ -236,27 +280,33 @@ def calculate_props(line): ann = 'NULL' # maps to None tup = name, ann arglist[idx] = tup - annotations[name] = _resolve_type(ann, line) + annotations[name] = _resolve_type(ann, line, 0) if len(tup) == 3: default = _resolve_value(tup[2], ann, line) _defaults.append(default) defaults = tuple(_defaults) - returntype = res["returntype"] + returntype = parsed.returntype if returntype is not None: - annotations["return"] = _resolve_type(returntype, line) - props = {} - props["defaults"] = defaults - props["kwdefaults"] = {} - props["annotations"] = annotations - props["varnames"] = varnames = tuple(tup[0] for tup in arglist) - funcname = res["funcname"] - props["fullname"] = funcname + annotations["return"] = _resolve_type(returntype, line, 0) + props = SimpleNamespace() + props.defaults = defaults + props.kwdefaults = {} + props.annotations = annotations + props.varnames = varnames = tuple(tup[0] for tup in arglist) + funcname = parsed.funcname + props.fullname = funcname shortname = funcname[funcname.rindex(".")+1:] - props["name"] = shortname - props["multi"] = res["multi"] - return props + props.name = shortname + props.multi = parsed.multi + return vars(props) + def fixup_multilines(lines): + """ + Multilines can collapse when certain distinctions between C++ types + vanish after mapping to Python. + This function fixes this by re-computing multiline-ness. + """ res = [] multi_lines = [] for line in lines: @@ -280,6 +330,7 @@ def fixup_multilines(lines): res.append(line) return res + def pyside_type_init(type_key, sig_strings): dprint() dprint("Initialization of type key '{}'".format(type_key)) diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py index dba8f8c77..786a84ecb 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py @@ -1996,6 +1996,21 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): return _generic_new(collections.defaultdict, cls, *args, **kwds) +############################ +# Insertion by CT 2019-02-21 +# +class OrderedDict(collections.OrderedDict, MutableMapping[KT, VT]): + __slots__ = () + __extra__ = collections.OrderedDict + + def __new__(cls, *args, **kwds): + if cls._gorg is OrderedDict: + return collections.OrderedDict(*args, **kwds) + return _generic_new(collections.OrderedDict, cls, *args, **kwds) +# +############################ + + class Counter(collections.Counter, Dict[T, int]): __slots__ = () __extra__ = collections.Counter diff --git a/sources/shiboken2/tests/minimalbinding/brace_pattern_test.py b/sources/shiboken2/tests/minimalbinding/brace_pattern_test.py new file mode 100644 index 000000000..89df998e8 --- /dev/null +++ b/sources/shiboken2/tests/minimalbinding/brace_pattern_test.py @@ -0,0 +1,121 @@ +############################################################################# +## +## 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 absolute_import, print_function + +import re +import sys +import os + +import shiboken2 +type.__signature__ # trigger bootstrap + +from shibokensupport.signature.lib.tool import build_brace_pattern +import unittest + +""" +This test tests the brace pattern from signature.lib.tool +against a slower reference implementation. +The pattern is crucial, because it is used heavily in signature.parser . +""" + +# A slow reference parser for braces and strings +def check(s): + open, close = "[{(<", "]})>" + escape, quote = "\\", '"' + instring = blind = False + stack = [] + for c in s: + if instring: + if blind: + blind = False + elif c == escape: + blind = True + elif c == quote: + instring = False + stack.pop() + continue + if c in open: + stack.append(c) + elif c in close: + pos = close.index(c) + if ((len(stack) > 0) and + (open[pos] == stack[len(stack)-1])): + stack.pop() + else: + return False + elif c == escape: + return False + elif c == quote: + instring = True + stack.append(c) + return len(stack) == 0 + + +class TestBracePattern(unittest.TestCase): + tests = [ + (r'{[]{()}}', True), + (r'[{}{})(]', False), + (r'[{}{} ")(" ]', True), + (r'[{}{} ")(\" ]', False), + (r'[{}{} ")(\" ]"]', True), + (r'a < b ( c [ d { "} \"\"}" } ] ) >', True), + (r'a < b ( c [ d { } ] ) >', True), + (r'a < b ( c [ d { "huh" } ] ) >', True), + (r'a < b ( c [ d { "huh\" \" \\\"" } ] ) >', True), + ] + + def test_checkfunc(self): + for test, result in self.tests: + if result: + self.assertTrue(check(test)) + else: + self.assertFalse(check(test)) + + def test_the_brace_pattern(self): + func = re.compile(build_brace_pattern(5) + "$", re.VERBOSE).match + for test, result in self.tests: + if result: + self.assertTrue(func(test)) + else: + self.assertFalse(func(test)) + + +if __name__ == '__main__': + unittest.main() |