From b6c627e87f844e9ae68bcc579d3cb0ea784ac77c Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 8 Jul 2019 16:17:52 +0200 Subject: shiboken: List location of anonymous structs in rejected classes Anonymous structs showed up as mysterious, empty entries in mjb_rejected_classes.log. Output the location. Change-Id: I0754e426a9fff3d1dac03aa587747ab0d7816df5 Reviewed-by: Cristian Maureira-Fredes --- sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp | 4 ++++ sources/shiboken2/ApiExtractor/parser/codemodel.h | 1 + 2 files changed, 5 insertions(+) diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index bb9b94a5b..4b31dbb05 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -1052,6 +1052,10 @@ AbstractMetaClass *AbstractMetaBuilderPrivate::traverseClass(const FileModelItem reason = AbstractMetaBuilder::GenerationDisabled; } if (reason != AbstractMetaBuilder::NoReason) { + if (fullClassName.isEmpty()) { + QTextStream(&fullClassName) << "anonymous struct at " << classItem->fileName() + << ':' << classItem->startLine(); + } m_rejectedClasses.insert(fullClassName, reason); return nullptr; } diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.h b/sources/shiboken2/ApiExtractor/parser/codemodel.h index 777b2d103..80db2cce5 100644 --- a/sources/shiboken2/ApiExtractor/parser/codemodel.h +++ b/sources/shiboken2/ApiExtractor/parser/codemodel.h @@ -287,6 +287,7 @@ public: FileModelItem file() const; void getStartPosition(int *line, int *column); + int startLine() const { return m_startLine; } void setStartPosition(int line, int column); void getEndPosition(int *line, int *column); -- cgit v1.2.3 From 035a2991c9468bdaad961da11053cf4176791819 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 12 Jul 2019 10:02:30 +0200 Subject: wheel_tester.py: Add a command line option to disable wheel installation This is useful when testing in a local developer build. Change-Id: Ib875dabd21d437951d3909030b47805b807fac9d Reviewed-by: Christian Tismer --- testing/wheel_tester.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/testing/wheel_tester.py b/testing/wheel_tester.py index 180526b33..535cb3712 100644 --- a/testing/wheel_tester.py +++ b/testing/wheel_tester.py @@ -53,6 +53,7 @@ directory (e.g. setup.py bdist_wheel was already executed). """ from __future__ import print_function, absolute_import +from argparse import ArgumentParser, RawTextHelpFormatter import os, sys try: @@ -327,12 +328,13 @@ def try_build_examples(): run_make() -def run_wheel_tests(): +def run_wheel_tests(install_wheels): wheels_dir = get_wheels_dir() py_version = sys.version_info[0] - log.info("Attempting to install wheels.\n") - try_install_wheels(wheels_dir, py_version) + if install_wheels: + log.info("Attempting to install wheels.\n") + try_install_wheels(wheels_dir, py_version) log.info("Attempting to build examples.\n") try_build_examples() @@ -341,4 +343,10 @@ def run_wheel_tests(): if __name__ == "__main__": - run_wheel_tests() + parser = ArgumentParser(description="wheel_tester", + formatter_class=RawTextHelpFormatter) + parser.add_argument('--no-install-wheels', '-n', action='store_true', + help='Do not install wheels' + ' (for developer builds with virtualenv)') + options = parser.parse_args() + run_wheel_tests(not options.no_install_wheels) -- cgit v1.2.3 From f1796628ec2d81c656876876148b2bf5db47c472 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 11 Jul 2019 11:19:42 +0200 Subject: shiboken: Improve handling of pointer types for CONVERTTOCPP in injected code Code snippets like: QByteArray *cppSelf = %CONVERTTOCPP[QByteArray *](obj); cause an assert in shiboken since the '*' is captured into the target of the assignment and is not considered part of the type. Make the error message a bit clearer and add documentation. Fixes: PYSIDE-1037 Change-Id: Ie8da2f57ba91325ea677e1a00852e91726c0e09b Reviewed-by: Christian Tismer --- sources/shiboken2/ApiExtractor/messages.cpp | 17 ++++++++++++++++ sources/shiboken2/ApiExtractor/messages.h | 2 ++ sources/shiboken2/doc/typesystemvariables.rst | 23 ++++++++++++++++++++++ .../generator/shiboken2/shibokengenerator.cpp | 9 +++++---- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/sources/shiboken2/ApiExtractor/messages.cpp b/sources/shiboken2/ApiExtractor/messages.cpp index a6e75aac3..5b3a57fcc 100644 --- a/sources/shiboken2/ApiExtractor/messages.cpp +++ b/sources/shiboken2/ApiExtractor/messages.cpp @@ -311,6 +311,23 @@ QString msgCannotUseEnumAsInt(const QString &name) "Compilation errors may occur when used as a function argument."); } +QString msgConversionTypesDiffer(const QString &varType, const QString &conversionType) +{ + QString result; + QTextStream str(&result); + str << "Types of receiver variable ('" << varType + << "') and %%CONVERTTOCPP type system variable ('" << conversionType + << "') differ"; + QString strippedVarType = varType; + QString strippedConversionType = conversionType; + TypeInfo::stripQualifiers(&strippedVarType); + TypeInfo::stripQualifiers(&strippedConversionType); + if (strippedVarType == strippedConversionType) + str << " in qualifiers. Please make sure the type is a distinct token"; + str << '.'; + return result; +} + // main.cpp QString msgLeftOverArguments(const QMap &remainingArgs) diff --git a/sources/shiboken2/ApiExtractor/messages.h b/sources/shiboken2/ApiExtractor/messages.h index ab2bf64b6..2fee0de8f 100644 --- a/sources/shiboken2/ApiExtractor/messages.h +++ b/sources/shiboken2/ApiExtractor/messages.h @@ -115,6 +115,8 @@ QString msgCannotOpenForWriting(const QFile &f); QString msgCannotUseEnumAsInt(const QString &name); +QString msgConversionTypesDiffer(const QString &varType, const QString &conversionType); + QString msgLeftOverArguments(const QMap &remainingArgs); QString msgInvalidVersion(const QString &package, const QString &version); diff --git a/sources/shiboken2/doc/typesystemvariables.rst b/sources/shiboken2/doc/typesystemvariables.rst index a07ba0d8c..b1b9bbfe6 100644 --- a/sources/shiboken2/doc/typesystemvariables.rst +++ b/sources/shiboken2/doc/typesystemvariables.rst @@ -126,6 +126,29 @@ Variables Replaced by a |project| conversion call that converts a Python variable to a C++ variable of the type indicated by ``CPPTYPE``. + Typically, this is a variable assignment: + + .. code-block:: c++ + + double value = %CONVERTTOCPP[double](pyValue); + + Pointer assignments are also possible: + + .. code-block:: c++ + + void f(double *valuePtr) + { + *valuePtr = %CONVERTTOCPP[double](pyValue); + + Note however, that for variable definitions, the type must + be a space-delimited token: + + .. code-block:: c++ + + double * valuePtr = %CONVERTTOCPP[double](pyValue); + + since it otherwise would be indistinguishable from the pointer assignment + above. .. _converttopython: diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 8e27777d6..e41c91716 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -149,6 +149,8 @@ ShibokenGenerator::ShibokenGenerator() const char CHECKTYPE_REGEX[] = R"(%CHECKTYPE\[([^\[]*)\]\()"; const char ISCONVERTIBLE_REGEX[] = R"(%ISCONVERTIBLE\[([^\[]*)\]\()"; const char CONVERTTOPYTHON_REGEX[] = R"(%CONVERTTOPYTHON\[([^\[]*)\]\()"; + // Capture a '*' leading the variable name into the target + // so that "*valuePtr = %CONVERTTOCPP..." works as expected. const char CONVERTTOCPP_REGEX[] = R"((\*?%?[a-zA-Z_][\w\.]*(?:\[[^\[^<^>]+\])*)(?:\s+)=(?:\s+)%CONVERTTOCPP\[([^\[]*)\]\()"; m_typeSystemConvRegEx[TypeSystemCheckFunction] = QRegularExpression(QLatin1String(CHECKTYPE_REGEX)); @@ -2044,10 +2046,9 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa varType = miniNormalizer(varType); QString varName = list.at(1).trimmed(); if (!varType.isEmpty()) { - if (varType != conversionType->cppSignature()) { - qFatal("Types of receiver variable ('%s') and %%CONVERTTOCPP type system variable ('%s') differ.", - qPrintable(varType), qPrintable(conversionType->cppSignature())); - } + const QString conversionSignature = conversionType->cppSignature(); + if (varType != conversionSignature) + qFatal("%s", qPrintable(msgConversionTypesDiffer(varType, conversionSignature))); c << getFullTypeName(conversionType) << ' ' << varName; writeMinimalConstructorExpression(c, conversionType); c << ';' << endl; -- cgit v1.2.3 From 4905e2e2feacd346c2f781aa817b3d13ace3411d Mon Sep 17 00:00:00 2001 From: Cristian Maureira-Fredes Date: Tue, 16 Jul 2019 12:31:04 +0200 Subject: Use PyInstaller version 3.4 The newest version, 3.5, seems to have an issue related to out test, which sadly is making the test to fail. Until this is not figured out, let's use 3.4 to avoid other patches to be merged. Change-Id: I71e8aa616dff37b6c5b2552711140c0c0bf10d21 Reviewed-by: Christian Tismer --- coin_test_instructions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coin_test_instructions.py b/coin_test_instructions.py index acb27875d..aeedd03e7 100644 --- a/coin_test_instructions.py +++ b/coin_test_instructions.py @@ -67,7 +67,8 @@ def call_testrunner(python_ver, buildnro): _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH) rmtree(_env, True) run_instruction(["virtualenv", "-p", _pExe, _env], "Failed to create virtualenv") - install_pip_dependencies(env_pip, ["pip", "numpy", "PyOpenGL", "setuptools", "six", "pyinstaller"]) + # Keeping PyInstaller 3.4, because 3.5 seems to have broken our test + install_pip_dependencies(env_pip, ["pip", "numpy", "PyOpenGL", "setuptools", "six", "pyinstaller==3.4"]) install_pip_wheel_package(env_pip) cmd = [env_python, "testrunner.py", "test", "--blacklist", "build_history/blacklist.txt", -- cgit v1.2.3 From 78dad8180d797a647645b74255bfc29c46d7264a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=C3=A1n=20Maureira-Fredes?= Date: Wed, 29 May 2019 11:22:44 +0200 Subject: Add optional support for types in QSettings::value This add an optional named parameter to the function value() to automatically cast the type that is being returned by the function. An example of this situation could be an ini file that contains the value of a one-element list: settings.setValue('var', ['a']) The the ini file will be: [General] var=a # we cannot know that this is a list! Once we read it, we could specify if we want the default behavior, a str, or to cast the output to a list. settings.value('var') # Will get "a" settings.value('var', type=list) # Will get ["a"] The cppgenerator was modified to add a verification step before trying to get the named parameter, since it could be optional and having one named parameter was assumming that all of them were provided. Change-Id: I8f379debea86b42cf89019d432e990084c9e6614 Fixes: PYSIDE-1010 Reviewed-by: Qt CI Bot Reviewed-by: Christian Tismer --- .../PySide2/QtCore/typesystem_core_common.xml | 11 ++-- sources/pyside2/PySide2/glue/qtcore.cpp | 50 ++++++++++++++ sources/pyside2/tests/QtCore/CMakeLists.txt | 1 + sources/pyside2/tests/QtCore/qsettings_test.ini | 4 ++ sources/pyside2/tests/QtCore/qsettings_test.py | 77 ++++++++++++++++++++++ .../shiboken2/generator/shiboken2/cppgenerator.cpp | 10 ++- 6 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 sources/pyside2/tests/QtCore/qsettings_test.ini create mode 100644 sources/pyside2/tests/QtCore/qsettings_test.py diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml index 2173e747b..78a731458 100644 --- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml +++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml @@ -2511,11 +2511,12 @@ - - - .. warning:: QSettings.value can return different types (QVariant types) depending on the platform it's running on, so the safest way to use it is always casting the result to the desired type, e.g.: int(settings.value("myKey")) - - + + + + + diff --git a/sources/pyside2/PySide2/glue/qtcore.cpp b/sources/pyside2/PySide2/glue/qtcore.cpp index 930ad9349..3e1bab97b 100644 --- a/sources/pyside2/PySide2/glue/qtcore.cpp +++ b/sources/pyside2/PySide2/glue/qtcore.cpp @@ -56,6 +56,56 @@ bool py2kStrCheck(PyObject *obj) } // @snippet pystring-check +// @snippet qsettings-value +QVariant out = %CPPSELF.value(%1, %2); +PyTypeObject *typeObj = reinterpret_cast(%PYARG_3); + +if (typeObj) { + if (typeObj == &PyList_Type) { + QByteArrayList valuesList = out.toByteArray().split(','); + const int valuesSize = valuesList.size(); + if (valuesSize > 0) { + PyObject *list = PyList_New(valuesSize); + for (int i = 0; i < valuesSize; i++) { + PyObject *item = PyUnicode_FromString(valuesList[i].data()); + PyList_SET_ITEM(list, i, item); + Py_DECREF(item); + } + %PYARG_0 = list; + + } else { + %PYARG_0 = %CONVERTTOPYTHON[QVariant](out); + } + } else if (typeObj == &PyBytes_Type) { + QByteArray asByteArray = out.toByteArray(); + %PYARG_0 = PyBytes_FromString(asByteArray.data()); + } else if (typeObj == &PyUnicode_Type) { + QByteArray asByteArray = out.toByteArray(); + %PYARG_0 = PyUnicode_FromString(asByteArray.data()); +#ifdef IS_PY3K + } else if (typeObj == &PyLong_Type) { + float asFloat = out.toFloat(); + pyResult = PyLong_FromDouble(asFloat); +#else + } else if (typeObj == &PyInt_Type) { + float asFloat = out.toFloat(); + pyResult = PyInt_FromLong(long(asFloat)); +#endif + } else if (typeObj == &PyFloat_Type) { + float asFloat = out.toFloat(); + %PYARG_0 = PyFloat_FromDouble(asFloat); + } + // TODO: PyDict_Type and PyTuple_Type +} +else { + if (out == 0) + %PYARG_0 = Py_None; + else + %PYARG_0 = %CONVERTTOPYTHON[QVariant](out); +} + +// @snippet qsettings-value + // @snippet qvariant-conversion static const char *QVariant_resolveMetaType(PyTypeObject *type, int *typeId) { diff --git a/sources/pyside2/tests/QtCore/CMakeLists.txt b/sources/pyside2/tests/QtCore/CMakeLists.txt index 08e63d043..d05699f16 100644 --- a/sources/pyside2/tests/QtCore/CMakeLists.txt +++ b/sources/pyside2/tests/QtCore/CMakeLists.txt @@ -96,6 +96,7 @@ PYSIDE_TEST(qrect_test.py) PYSIDE_TEST(qregexp_test.py) PYSIDE_TEST(qregularexpression_test.py) PYSIDE_TEST(qresource_test.py) +PYSIDE_TEST(qsettings_test.py) PYSIDE_TEST(qsize_test.py) PYSIDE_TEST(qslot_object_test.py) PYSIDE_TEST(qsocketnotifier_test.py) diff --git a/sources/pyside2/tests/QtCore/qsettings_test.ini b/sources/pyside2/tests/QtCore/qsettings_test.ini new file mode 100644 index 000000000..f54ae0029 --- /dev/null +++ b/sources/pyside2/tests/QtCore/qsettings_test.ini @@ -0,0 +1,4 @@ +[General] +var1=a, b, c +var2=a + diff --git a/sources/pyside2/tests/QtCore/qsettings_test.py b/sources/pyside2/tests/QtCore/qsettings_test.py new file mode 100644 index 000000000..6d64b0db3 --- /dev/null +++ b/sources/pyside2/tests/QtCore/qsettings_test.py @@ -0,0 +1,77 @@ +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +'''Test cases for QDate''' + +import unittest + +import os +from helper import adjust_filename +import py3kcompat as py3k +from PySide2.QtCore import QSettings + +class TestQSettings(unittest.TestCase): + def testConversions(self): + file_path = adjust_filename('qsettings_test.ini', __file__) + settings = QSettings(file_path, QSettings.IniFormat) + + r = settings.value('var1') + self.assertEqual(type(r), list) + + r = settings.value('var2') + if py3k.IS_PY3K: + self.assertEqual(type(r), str) + else: + self.assertEqual(type(r), unicode) + + r = settings.value('var2', type=list) + self.assertEqual(type(r), list) + + + def testDefaultValueConversion(self): + settings = QSettings('foo.ini', QSettings.IniFormat) + r = settings.value('lala', 22) + if py3k.IS_PY3K: + self.assertEqual(type(r), int) + else: + self.assertEqual(type(r), long) + + r = settings.value('lala', 22, type=str) + self.assertEqual(type(r), str) + + r = settings.value('lala', 22, type=bytes) + self.assertEqual(type(r), bytes) + + r = settings.value('lala', 22, type=int) + self.assertEqual(type(r), int) + + r = settings.value('lala', 22, type=float) + self.assertEqual(type(r), float) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 84f0cd1f5..e36b6edc3 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -3032,18 +3032,21 @@ void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMe s << INDENT << "if (kwds) {" << endl; { Indentation indent(INDENT); - s << INDENT << "PyObject *"; + s << INDENT << "PyObject *keyName = nullptr;" << endl; + s << INDENT << "PyObject *value = nullptr;" << endl; for (const AbstractMetaArgument *arg : args) { int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG); - s << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; + s << INDENT << "keyName = Py_BuildValue(\"s\",\"" << arg->name() << "\");" << endl; + s << INDENT << "if (PyDict_Contains(kwds, keyName)) {" << endl; + s << INDENT << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; s << INDENT << "if (value && " << pyArgName << ") {" << endl; { Indentation indent(INDENT); s << INDENT << pyErrString.arg(arg->name()) << endl; s << INDENT << returnStatement(m_currentErrorCode) << endl; } - s << INDENT << "} else if (value) {" << endl; + s << INDENT << INDENT << "} else if (value) {" << endl; { Indentation indent(INDENT); s << INDENT << pyArgName << " = value;" << endl; @@ -3058,6 +3061,7 @@ void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMe s << INDENT << '}' << endl; if (arg != args.constLast()) s << INDENT; + s << "}" << endl; } } s << INDENT << '}' << endl; -- cgit v1.2.3 From 6c8fafb996e4cd2abd3dd82c3127c65b88157ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=C3=A1n=20Maureira-Fredes?= Date: Thu, 4 Jul 2019 09:12:37 +0200 Subject: Change QtQml tests file permissions to 644 Change-Id: I0447da5e1ace3f6acc930aa4296576ec447f1be5 Reviewed-by: Friedemann Kleint --- sources/pyside2/tests/QtQml/CMakeLists.txt | 0 sources/pyside2/tests/QtQml/bug_1029.py | 0 sources/pyside2/tests/QtQml/bug_1029.qml | 0 sources/pyside2/tests/QtQml/bug_451.py | 0 sources/pyside2/tests/QtQml/bug_451.qml | 0 sources/pyside2/tests/QtQml/bug_456.py | 0 sources/pyside2/tests/QtQml/bug_456.qml | 0 sources/pyside2/tests/QtQml/bug_557.py | 0 sources/pyside2/tests/QtQml/bug_726.py | 0 sources/pyside2/tests/QtQml/bug_726.qml | 0 sources/pyside2/tests/QtQml/bug_814.py | 0 sources/pyside2/tests/QtQml/bug_814.qml | 0 sources/pyside2/tests/QtQml/bug_825.py | 0 sources/pyside2/tests/QtQml/bug_825.qml | 0 sources/pyside2/tests/QtQml/bug_847.py | 0 sources/pyside2/tests/QtQml/bug_847.qml | 0 sources/pyside2/tests/QtQml/bug_915.py | 0 sources/pyside2/tests/QtQml/bug_926.py | 0 sources/pyside2/tests/QtQml/bug_926.qml | 0 sources/pyside2/tests/QtQml/bug_951.py | 0 sources/pyside2/tests/QtQml/bug_951.qml | 0 sources/pyside2/tests/QtQml/bug_995.py | 0 sources/pyside2/tests/QtQml/bug_995.qml | 0 sources/pyside2/tests/QtQml/bug_997.py | 0 sources/pyside2/tests/QtQml/bug_997.qml | 0 sources/pyside2/tests/QtQml/connect_python_qml.qml | 0 sources/pyside2/tests/QtQml/hw.qml | 0 sources/pyside2/tests/QtQml/qqmlnetwork_test.py | 0 sources/pyside2/tests/QtQml/qquickview_test.py | 0 sources/pyside2/tests/QtQml/registertype.py | 0 sources/pyside2/tests/QtQml/registertype.qml | 0 sources/pyside2/tests/QtQml/signal_arguments.py | 0 sources/pyside2/tests/QtQml/view.qml | 0 sources/pyside2/tests/QtQml/viewmodel.qml | 0 34 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 sources/pyside2/tests/QtQml/CMakeLists.txt mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_1029.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_1029.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_451.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_451.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_456.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_456.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_557.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_726.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_726.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_814.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_814.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_825.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_825.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_847.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_847.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_915.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_926.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_926.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_951.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_951.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_995.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_995.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_997.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/bug_997.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/connect_python_qml.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/hw.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/qqmlnetwork_test.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/qquickview_test.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/registertype.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/registertype.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/signal_arguments.py mode change 100755 => 100644 sources/pyside2/tests/QtQml/view.qml mode change 100755 => 100644 sources/pyside2/tests/QtQml/viewmodel.qml diff --git a/sources/pyside2/tests/QtQml/CMakeLists.txt b/sources/pyside2/tests/QtQml/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_1029.py b/sources/pyside2/tests/QtQml/bug_1029.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_1029.qml b/sources/pyside2/tests/QtQml/bug_1029.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_451.py b/sources/pyside2/tests/QtQml/bug_451.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_451.qml b/sources/pyside2/tests/QtQml/bug_451.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_456.py b/sources/pyside2/tests/QtQml/bug_456.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_456.qml b/sources/pyside2/tests/QtQml/bug_456.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_557.py b/sources/pyside2/tests/QtQml/bug_557.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_726.py b/sources/pyside2/tests/QtQml/bug_726.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_726.qml b/sources/pyside2/tests/QtQml/bug_726.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_814.py b/sources/pyside2/tests/QtQml/bug_814.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_814.qml b/sources/pyside2/tests/QtQml/bug_814.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_825.py b/sources/pyside2/tests/QtQml/bug_825.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_825.qml b/sources/pyside2/tests/QtQml/bug_825.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_847.py b/sources/pyside2/tests/QtQml/bug_847.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_847.qml b/sources/pyside2/tests/QtQml/bug_847.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_915.py b/sources/pyside2/tests/QtQml/bug_915.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_926.py b/sources/pyside2/tests/QtQml/bug_926.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_926.qml b/sources/pyside2/tests/QtQml/bug_926.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_951.py b/sources/pyside2/tests/QtQml/bug_951.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_951.qml b/sources/pyside2/tests/QtQml/bug_951.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_995.py b/sources/pyside2/tests/QtQml/bug_995.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_995.qml b/sources/pyside2/tests/QtQml/bug_995.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_997.py b/sources/pyside2/tests/QtQml/bug_997.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/bug_997.qml b/sources/pyside2/tests/QtQml/bug_997.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/connect_python_qml.qml b/sources/pyside2/tests/QtQml/connect_python_qml.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/hw.qml b/sources/pyside2/tests/QtQml/hw.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/qqmlnetwork_test.py b/sources/pyside2/tests/QtQml/qqmlnetwork_test.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/qquickview_test.py b/sources/pyside2/tests/QtQml/qquickview_test.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/registertype.py b/sources/pyside2/tests/QtQml/registertype.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/registertype.qml b/sources/pyside2/tests/QtQml/registertype.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/signal_arguments.py b/sources/pyside2/tests/QtQml/signal_arguments.py old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/view.qml b/sources/pyside2/tests/QtQml/view.qml old mode 100755 new mode 100644 diff --git a/sources/pyside2/tests/QtQml/viewmodel.qml b/sources/pyside2/tests/QtQml/viewmodel.qml old mode 100755 new mode 100644 -- cgit v1.2.3 From 295d9d7fb52505666a6dc9ac7b6fba2323814c48 Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Fri, 15 Feb 2019 18:39:45 +0100 Subject: Cleanup signature module before substantial change There were some refinements applied while developing "Support Pointer Primitive Types by Arrays or Result Tuples". This patch moves these changes out which are not essential for that patch. They include - sort all mapping groups by name - replace huge regex by a pattern generator - replace dictionary string entries by SimpleNameSpace - improve PEP 563 handling - simplify "zero(sometype)" substantially - better handling of "QGenericMatrix" (preview) A test for the generated pattern against a reference parser was added. Task-number: PYSIDE-795 Task-number: PYSIDE-951 Change-Id: I5a6b236850c63a7db77b7f7b88881486fd1e61be Reviewed-by: Cristian Maureira-Fredes --- .gitignore | 2 +- sources/pyside2/PySide2/support/generate_pyi.py | 4 + sources/shiboken2/shibokenmodule/CMakeLists.txt | 2 + .../files.dir/shibokensupport/signature/layout.py | 15 +- .../shibokensupport/signature/lib/tool.py | 135 ++++++++ .../files.dir/shibokensupport/signature/mapping.py | 360 ++++++++++----------- .../files.dir/shibokensupport/signature/parser.py | 185 +++++++---- .../files.dir/shibokensupport/typing27.py | 15 + .../tests/minimalbinding/brace_pattern_test.py | 121 +++++++ 9 files changed, 562 insertions(+), 277 deletions(-) create mode 100644 sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py create mode 100644 sources/shiboken2/tests/minimalbinding/brace_pattern_test.py 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("")': "", + "std.string": str, + "ZeroIn": 0, 'Str("")': "", '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" "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.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 ([0-9]+)) : )? # the optional multi-index @@ -86,38 +105,9 @@ def _parse_line(line): ( -> (?P .*) )? # 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() -- cgit v1.2.3 From 97718de50e14f69048993df4e87dda1cadcc599a Mon Sep 17 00:00:00 2001 From: Paul Wicking Date: Tue, 9 Jul 2019 12:10:36 +0000 Subject: Add missing space after comma according to style guide Change-Id: I66b59ef11aa268e17434832f69af4e2e27146286 Reviewed-by: Edward Welbourne --- examples/sql/books/createdb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/sql/books/createdb.py b/examples/sql/books/createdb.py index 1ca52470f..d03060ad5 100644 --- a/examples/sql/books/createdb.py +++ b/examples/sql/books/createdb.py @@ -101,10 +101,10 @@ def init_db(): check(db.open) q = QSqlQuery() - check(q.exec_,BOOKS_SQL) - check(q.exec_,AUTHORS_SQL) - check(q.exec_,GENRES_SQL) - check(q.prepare,INSERT_AUTHOR_SQL) + check(q.exec_, BOOKS_SQL) + check(q.exec_, AUTHORS_SQL) + check(q.exec_, GENRES_SQL) + check(q.prepare, INSERT_AUTHOR_SQL) asimovId = add_author(q, "Isaac Asimov", datetime(1920, 2, 1)) greeneId = add_author(q, "Graham Greene", datetime(1904, 10, 2)) -- cgit v1.2.3 From dc73e75c1802df9e7594097ca65327b937097b3f Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Sun, 14 Jul 2019 11:24:24 +0200 Subject: Fix RuntimeError: dictionary changed size during iteration Seems the dictionary of modules needs to be copied before iterating Fixes https://github.com/spyder-ide/qtpy/issues/195 Change-Id: I8517e031c4a250856d3318b364b1cd5dab3f8f80 Reviewed-by: Christian Tismer --- .../shibokenmodule/files.dir/shibokensupport/signature/mapping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index b8097719a..36b137104 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -225,7 +225,7 @@ class Reloader(object): self.sys_module_count = len(sys.modules) g = globals() # PYSIDE-1009: Try to recognize unknown modules in errorhandler.py - candidates = list(mod_name for mod_name in sys.modules + candidates = list(mod_name for mod_name in sys.modules.copy() if self.module_valid(sys.modules[mod_name])) for mod_name in candidates: # 'top' is PySide2 when we do 'import PySide.QtCore' -- cgit v1.2.3 From 9318c09dbaa0ac78beb8edfd76a650e46e1a1b09 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 29 Jul 2019 12:23:09 +0200 Subject: Fix build against Qt 5.13 For development purposes, the 5.14 branch is currently built against Qt 5.13. Qt 5.14 deprecation fixes must be guarded by version checks. Change-Id: I41f7185577c612e8daf8020b9fe57d9ff2c66379 Reviewed-by: Alexandru Croitor --- sources/pyside2/libpyside/globalreceiverv2.cpp | 4 ++++ sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/sources/pyside2/libpyside/globalreceiverv2.cpp b/sources/pyside2/libpyside/globalreceiverv2.cpp index 283fb9cd2..84ec92687 100644 --- a/sources/pyside2/libpyside/globalreceiverv2.cpp +++ b/sources/pyside2/libpyside/globalreceiverv2.cpp @@ -283,7 +283,11 @@ int GlobalReceiverV2::refCount(const QObject *link) const void GlobalReceiverV2::notify() { +#if QT_VERSION >= 0x050E00 const QSet objSet(m_refs.cbegin(), m_refs.cend()); +#else + const auto objSet = QSet::fromList(m_refs); +#endif Py_BEGIN_ALLOW_THREADS for (const QObject *o : objSet) { QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index c91ac4dd4..5dc761d49 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -1594,7 +1594,11 @@ void AbstractMetaBuilderPrivate::traverseEnums(const ScopeModelItem &scopeItem, const QStringList &enumsDeclarations) { const EnumList &enums = scopeItem->enums(); +#if QT_VERSION >= 0x050E00 const QSet enumsDeclarationSet(enumsDeclarations.cbegin(), enumsDeclarations.cend()); +#else + const QSet enumsDeclarationSet = QSet::fromList(enumsDeclarations); +#endif for (const EnumModelItem &enumItem : enums) { AbstractMetaEnum* metaEnum = traverseEnum(enumItem, metaClass, enumsDeclarationSet); if (metaEnum) { -- cgit v1.2.3 From 5838387e31987286ae836135ecd06326f0c7e1ba Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 31 Jul 2019 15:12:57 +0200 Subject: QtWinExtras: Add QtWin namespace Task-number: PYSIDE-1047 Change-Id: I864a5d1d0e57d15f913012f783876a38b9458315 Reviewed-by: Christian Tismer --- sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt | 1 + sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt b/sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt index cf308c31d..0e0b3d58b 100644 --- a/sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtWinExtras/CMakeLists.txt @@ -1,6 +1,7 @@ project(QtWinExtras) set(QtWinExtras_SRC +${QtWinExtras_GEN_DIR}/qtwin_wrapper.cpp ${QtWinExtras_GEN_DIR}/qwincolorizationchangeevent_wrapper.cpp ${QtWinExtras_GEN_DIR}/qwincompositionchangeevent_wrapper.cpp ${QtWinExtras_GEN_DIR}/qwinevent_wrapper.cpp diff --git a/sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml b/sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml index ec17428a1..db7416a24 100644 --- a/sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml +++ b/sources/pyside2/PySide2/QtWinExtras/typesystem_winextras.xml @@ -43,6 +43,10 @@ + + + + -- cgit v1.2.3 From 21d948aa47dfe62b58286a31c729e76c9e3c13db Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Tue, 6 Aug 2019 12:52:33 +0200 Subject: PySide: Create a framework for deprecated functions During development of the patch "Support Pointer Primitive Types by Arrays or Result Tuples" some functions in QtGui turned out to be removal candidates. The name "constData" should be deprecated in favor of the existing "data" function. Other implementation also do not have this. Instead of simply removing, we now create a surrogate function with the name "constData" in fure Python that gives a warning and calls the "data" function. This is now extracted into its own commit since the deprecation is a completely different and independent issue. The implementation does not do any extra imports in advance. and is easily extensible to more post-installation actions. Task-number: PYSIDE-795 Change-Id: I410c69a87d9f0df78f736991b2ee0a2747678911 Reviewed-by: Cristian Maureira-Fredes --- sources/pyside2/PySide2/CMakeLists.txt | 4 + sources/pyside2/PySide2/support/__init__.py | 2 + sources/pyside2/PySide2/support/deprecated.py | 80 ++++++++++++++++ sources/pyside2/tests/QtGui/CMakeLists.txt | 2 +- .../tests/QtGui/timed_app_and_patching_test.py | 60 ++++++++++++ sources/pyside2/tests/QtGui/timed_app_test.py | 41 -------- sources/shiboken2/libshiboken/signature.cpp | 19 +++- sources/shiboken2/shibokenmodule/CMakeLists.txt | 2 + .../shibokensupport/signature/importhandler.py | 103 +++++++++++++++++++++ .../files.dir/shibokensupport/signature/loader.py | 12 ++- 10 files changed, 281 insertions(+), 44 deletions(-) create mode 100644 sources/pyside2/PySide2/support/deprecated.py create mode 100644 sources/pyside2/tests/QtGui/timed_app_and_patching_test.py delete mode 100644 sources/pyside2/tests/QtGui/timed_app_test.py create mode 100644 sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py diff --git a/sources/pyside2/PySide2/CMakeLists.txt b/sources/pyside2/PySide2/CMakeLists.txt index d666751ea..e39db75a1 100644 --- a/sources/pyside2/PySide2/CMakeLists.txt +++ b/sources/pyside2/PySide2/CMakeLists.txt @@ -37,6 +37,10 @@ endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py" "${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/generate_pyi.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/generate_pyi.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/deprecated.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/deprecated.py" COPYONLY) # now compile all modules. file(READ "${CMAKE_CURRENT_BINARY_DIR}/pyside2_global.h" pyside2_global_contents) diff --git a/sources/pyside2/PySide2/support/__init__.py b/sources/pyside2/PySide2/support/__init__.py index dda01474d..8764fb5cb 100644 --- a/sources/pyside2/PySide2/support/__init__.py +++ b/sources/pyside2/PySide2/support/__init__.py @@ -38,3 +38,5 @@ ############################################################################# from shiboken2 import VoidPtr + +#eof diff --git a/sources/pyside2/PySide2/support/deprecated.py b/sources/pyside2/PySide2/support/deprecated.py new file mode 100644 index 000000000..8538826e4 --- /dev/null +++ b/sources/pyside2/PySide2/support/deprecated.py @@ -0,0 +1,80 @@ +# This Python file uses the following encoding: utf-8 +############################################################################# +## +## 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 + +""" +deprecated.py + +This module contains deprecated things that are removed from the interface. +They are implemented in Python again, together with a deprecation warning. + +Functions that are to be called for + PySide2. must be named + fix_for_ . + +Note that this fixing code is run after all initializations, but before the +import is finished. But that is no problem since the module is passed in. +""" + +import warnings +from textwrap import dedent + + +class PySideDeprecationWarningRemovedInQt6(Warning): + pass + + +def constData(self): + cls = self.__class__ + name = cls.__name__ + warnings.warn(dedent(""" + {name}.constData is unpythonic and will be removed in Qt For Python 6.0 . + Please use {name}.data instead.""" + .format(**locals())), PySideDeprecationWarningRemovedInQt6, stacklevel=2) + return cls.data(self) + + +def fix_for_QtGui(QtGui): + for name, cls in QtGui.__dict__.items(): + if name.startswith("QMatrix") and "data" in cls.__dict__: + cls.constData = constData + +# eof diff --git a/sources/pyside2/tests/QtGui/CMakeLists.txt b/sources/pyside2/tests/QtGui/CMakeLists.txt index 927e72468..172703ab9 100644 --- a/sources/pyside2/tests/QtGui/CMakeLists.txt +++ b/sources/pyside2/tests/QtGui/CMakeLists.txt @@ -46,4 +46,4 @@ PYSIDE_TEST(qtextdocumentwriter_test.py) PYSIDE_TEST(qtextline_test.py) PYSIDE_TEST(qtransform_test.py) PYSIDE_TEST(repr_test.py) -PYSIDE_TEST(timed_app_test.py) +PYSIDE_TEST(timed_app_and_patching_test.py) diff --git a/sources/pyside2/tests/QtGui/timed_app_and_patching_test.py b/sources/pyside2/tests/QtGui/timed_app_and_patching_test.py new file mode 100644 index 000000000..014aeec1a --- /dev/null +++ b/sources/pyside2/tests/QtGui/timed_app_and_patching_test.py @@ -0,0 +1,60 @@ +############################################################################# +## +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import unittest + +from helper import TimedQApplication +from PySide2.support import deprecated +from PySide2.support.signature import importhandler +from PySide2 import QtGui + + +class TestTimedApp(TimedQApplication): + '''Simple test case for TimedQApplication''' + + def testFoo(self): + #Simple test of TimedQApplication + self.app.exec_() + + +def fix_for_QtGui(QtGui): + QtGui.something = 42 + +class TestPatchingFramework(unittest.TestCase): + """Simple test that verifies that deprecated.py works""" + + deprecated.fix_for_QtGui = fix_for_QtGui + + def test_patch_works(self): + something = "something" + self.assertFalse(hasattr(QtGui, something)) + importhandler.finish_import(QtGui) + self.assertTrue(hasattr(QtGui, something)) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/pyside2/tests/QtGui/timed_app_test.py b/sources/pyside2/tests/QtGui/timed_app_test.py deleted file mode 100644 index dc0e7c4b0..000000000 --- a/sources/pyside2/tests/QtGui/timed_app_test.py +++ /dev/null @@ -1,41 +0,0 @@ -############################################################################# -## -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the test suite of Qt for Python. -## -## $QT_BEGIN_LICENSE:GPL-EXCEPT$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 3 as published by the Free Software -## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# - -import unittest - -from helper import TimedQApplication - -class TestTimedApp(TimedQApplication): - '''Simple test case for TimedQApplication''' - - def testFoo(self): - #Simple test of TimedQApplication - self.app.exec_() - -if __name__ == '__main__': - unittest.main() diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index e62f861a2..7eaf35e1c 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -77,6 +77,7 @@ typedef struct safe_globals_struc { PyObject *create_signature_func; PyObject *seterror_argument_func; PyObject *make_helptext_func; + PyObject *finish_import_func; } safe_globals_struc, *safe_globals; static safe_globals pyside_globals = nullptr; @@ -543,6 +544,9 @@ init_phase_1(void) if (p->value_dict == nullptr) goto error; + // This function will be disabled until phase 2 is done. + p->finish_import_func = nullptr; + return p; } error: @@ -585,6 +589,10 @@ init_phase_2(safe_globals_struc *p, PyMethodDef *methods) p->make_helptext_func = PyObject_GetAttrString(loader, "make_helptext"); if (p->make_helptext_func == nullptr) goto error; + p->finish_import_func = PyObject_GetAttrString(loader, "finish_import"); + if (p->finish_import_func == nullptr) + goto error; + return 0; return 0; } error: @@ -1014,7 +1022,16 @@ PySide_FinishSignatures(PyObject *module, const char *signatures[]) return -1; if (_finish_nested_classes(obdict) < 0) return -1; - return 0; + // The finish_import function will not work the first time since phase 2 + // was not yet run. But that is ok, because the first import is always for + // the shiboken module (or a test module). + if (pyside_globals->finish_import_func == nullptr) { + assert(strncmp(name, "PySide2.", 8) != 0); + return 0; + } + Shiboken::AutoDecRef ret(PyObject_CallFunction( + pyside_globals->finish_import_func, const_cast("(O)"), module)); + return ret.isNull() ? -1 : 0; } static int diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index 057a995f8..5e5cf21d7 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -53,6 +53,8 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/ "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/layout.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/loader.py" "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/loader.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/importhandler.py" + "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/importhandler.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/mapping.py" "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/mapping.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/parser.py" diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py new file mode 100644 index 000000000..0417f132a --- /dev/null +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py @@ -0,0 +1,103 @@ +############################################################################# +## +## 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 + +""" +importhandler.py + +This module handles special actions after the import of PySide modules. +The reason for this was the wish to replace some deprecated functions +by a Python implementation that gives a warning. + +It provides a framework to safely call functions outside of files.dir, +because the implementation of deprecated functions should be visible +to the users (in the hope they don't use it any longer ). + +As a first approach, the function finish_import redirects to +PySide2/support/deprecated.py . There can come other extensions as well. +""" + +try: + from PySide2.support import deprecated + have_deprecated = True +except ImportError: + have_deprecated = False + + +# called by loader.py from signature.cpp +def finish_import(module): + if have_deprecated and module.__name__.startswith("PySide2."): + try: + name = "fix_for_" + module.__name__.split(".")[1] + func = getattr(deprecated, name, None) + if func: + func(module) + except Exception as e: + name = e.__class__.__name__ + print(72 * "*") + print("Error in deprecated.py, ignored:") + print(" {name}: {e}".format(**locals())) + +""" +A note for people who might think this could be written in pure Python: + +Sure, by an explicit import of the modules to patch, this is no problem. +But in the general case, a module should only be imported on user +request and not because we want to patch it. So I started over. + +I then tried to do it on demand by redirection of the __import__ function. +Things worked quite nicely as it seemed, but at second view this solution +was much less appealing. + +Reason: +If someone executes as the first PySide statement + + from PySide2 import QtGui + +then this import is already running. We can see the other imports like the +diverse initializations and QtCore, because it is triggered by import of +QtGui. But the QtGui import can not be seen at all! + +With a lot of effort, sys.setprofile() and stack inspection with the inspect +module, it is *perhaps* possible to solve that. I tried for a day and then +gave up, since the solution is anyway not too nice when __import__ must +be overridden. +""" +#eof diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py index 6c76483a0..3d25c5690 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py @@ -102,6 +102,10 @@ def _typevar__repr__(self): # Note also that during the tests we have a different encoding that would # break the Python license decorated files without an encoding line. +# name used in signature.cpp +def pyside_type_init(type_key, sig_strings): + return parser.pyside_type_init(type_key, sig_strings) + # name used in signature.cpp def create_signature(props, key): return layout.create_signature(props, key) @@ -114,6 +118,11 @@ def seterror_argument(args, func_name): def make_helptext(func): return errorhandler.make_helptext(func) +# name used in signature.cpp +def finish_import(module): + return importhandler.finish_import(module) + + import signature_bootstrap from shibokensupport import signature signature.get_signature = signature_bootstrap.get_signature @@ -194,6 +203,7 @@ def move_into_pyside_package(): put_into_package(PySide2.support.signature, layout) put_into_package(PySide2.support.signature, lib) put_into_package(PySide2.support.signature, parser) + put_into_package(PySide2.support.signature, importhandler) put_into_package(PySide2.support.signature.lib, enum_sig) put_into_package(PySide2.support.signature, typing) @@ -204,8 +214,8 @@ from shibokensupport.signature import errorhandler from shibokensupport.signature import layout from shibokensupport.signature import lib from shibokensupport.signature import parser +from shibokensupport.signature import importhandler from shibokensupport.signature.lib import enum_sig -from shibokensupport.signature.parser import pyside_type_init if "PySide2" in sys.modules: # We publish everything under "PySide2.support.signature", again. -- cgit v1.2.3 From 87986cf77194b995785ae06e9eff07524b711dba Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Thu, 1 Aug 2019 15:24:00 +0200 Subject: Support Pointer Primitive Types by Arrays or Result Tuples -- This change is part of the improved numpy support -- Most primitive types are handled in XML, but this was not reflected by the signatures, error messages, doc strings and hinting stubs. In order to enhance the information shown to be more correct, the C++ parser part was rewritten for Python. It is written closely to Python syntax, but keeps the existing information about primitive types intact. AbstractMetaType::NativePointerAsArrayPattern is now used to mark a variable as an array. Heuristics are no longer used. If a pointer variable is not marked as an array, the Python parser generates a return value. If more than one value would be returned, a result-tuple is generated. Because we now have a deterministic categorization of types, the "const" attribute is no more needed and the entries in mapping.py are reduced. A few missing markers were added. The tool also now handles typing.List[] differently in arguments and return types. While return types stay lists, they are for now changed to typing.Sequence[] in argument lists. A test was included. These messages belong to the previous "deprecated functions" patch: Further, QMatrixMxN.constData was removed from the typesystem and replaced by a surrogate function that calls QMatrixMxN.data, but also generates a warning. The long forgotten generate_pyi.py was now published in the same course. Task-number: PYSIDE-795 Task-number: PYSIDE-951 Change-Id: Ia59fe4986919525a70ea7cc453c64cdf46e7fba0 Reviewed-by: Cristian Maureira-Fredes --- .../PySide2/QtCore/typesystem_core_common.xml | 2 + .../PySide2/QtGui/typesystem_gui_common.xml | 18 ++- .../pyside2/PySide2/QtOpenGL/typesystem_opengl.xml | 4 + .../shiboken2/ApiExtractor/abstractmetalang.cpp | 63 +++++++- sources/shiboken2/ApiExtractor/abstractmetalang.h | 6 +- .../shiboken2/generator/shiboken2/cppgenerator.cpp | 31 +--- sources/shiboken2/libshiboken/signature_doc.rst | 10 +- .../files.dir/shibokensupport/signature/mapping.py | 171 +++++++++++++-------- .../files.dir/shibokensupport/signature/parser.py | 110 +++++++++++-- .../samplebinding/pointerprimitivetype_test.py | 79 ++++++++++ 10 files changed, 383 insertions(+), 111 deletions(-) create mode 100644 sources/shiboken2/tests/samplebinding/pointerprimitivetype_test.py diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml index 9ffc7d376..eb4e502ea 100644 --- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml +++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml @@ -2299,8 +2299,10 @@ + + diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml index 95b85a918..ab40e3953 100644 --- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml +++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml @@ -1117,6 +1117,7 @@ + @@ -1124,6 +1125,7 @@ + @@ -1150,7 +1152,8 @@ - + + @@ -1158,6 +1161,7 @@ + @@ -1165,6 +1169,7 @@ + @@ -1935,6 +1940,7 @@ + @@ -1971,6 +1977,7 @@ + @@ -2007,6 +2014,7 @@ + @@ -2043,6 +2051,7 @@ + @@ -2079,6 +2088,7 @@ + @@ -2115,6 +2125,7 @@ + @@ -2151,6 +2162,7 @@ + @@ -2187,6 +2199,7 @@ + @@ -2267,6 +2280,7 @@ + @@ -2734,7 +2748,7 @@ - + diff --git a/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml b/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml index 27c515170..5e864ca43 100644 --- a/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml +++ b/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml @@ -221,6 +221,7 @@ + @@ -237,6 +238,7 @@ + @@ -445,6 +447,7 @@ + @@ -461,6 +464,7 @@ + diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 455140e59..4ca448d50 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -268,6 +268,15 @@ QString AbstractMetaType::cppSignature() const return m_cachedCppSignature; } +QString AbstractMetaType::pythonSignature() const +{ + // PYSIDE-921: Handle container returntypes correctly. + // This is now a clean reimplementation. + if (m_cachedPythonSignature.isEmpty()) + m_cachedPythonSignature = formatPythonSignature(false); + return m_cachedPythonSignature; +} + AbstractMetaType::TypeUsagePattern AbstractMetaType::determineUsagePattern() const { if (m_typeEntry->isTemplateArgument() || m_referenceType == RValueReference) @@ -2555,6 +2564,58 @@ QString AbstractMetaType::formatSignature(bool minimal) const return result; } +QString AbstractMetaType::formatPythonSignature(bool minimal) const +{ + /* + * This is a version of the above, more suitable for Python. + * We avoid extra keywords that are not needed in Python. + * We prepend the package name, unless it is a primitive type. + * + * Primitive types like 'int', 'char' etc.: + * When we have a primitive with an indirection, we use that '*' + * character for later postprocessing, since those indirections + * need to be modified into a result tuple. + */ + QString result; + if (m_pattern == AbstractMetaType::NativePointerAsArrayPattern) + result += QLatin1String("array "); + // We no longer use the "const" qualifier for heuristics. Instead, + // NativePointerAsArrayPattern indicates when we have in XML. + // if (m_typeEntry->isPrimitive() && isConstant()) + // result += QLatin1String("const "); + if (!m_typeEntry->isPrimitive() && !package().isEmpty()) + result += package() + QLatin1Char('.'); + if (isArray()) { + // Build nested array dimensions a[2][3] in correct order + result += m_arrayElementType->formatPythonSignature(true); + const int arrayPos = result.indexOf(QLatin1Char('[')); + if (arrayPos != -1) + result.insert(arrayPos, formatArraySize(m_arrayElementCount)); + else + result.append(formatArraySize(m_arrayElementCount)); + } else { + result += typeEntry()->qualifiedCppName(); + } + if (!m_instantiations.isEmpty()) { + result += QLatin1Char('['); + for (int i = 0, size = m_instantiations.size(); i < size; ++i) { + if (i > 0) + result += QLatin1String(", "); + result += m_instantiations.at(i)->formatPythonSignature(true); + } + result += QLatin1Char(']'); + } + if (m_typeEntry->isPrimitive()) + for (Indirection i : m_indirections) + result += TypeInfo::indirectionKeyword(i); + // If it is a flags type, we replace it with the full name: + // "PySide2.QtCore.Qt.ItemFlags" instead of "PySide2.QtCore.QFlags" + if (m_typeEntry->isFlags()) + result = fullName(); + result.replace(QLatin1String("::"), QLatin1String(".")); + return result; +} + bool AbstractMetaType::isCppPrimitive() const { return m_pattern == PrimitivePattern && m_typeEntry->isCppPrimitive(); diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index afb4e5fbd..7f0f9fbaa 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -483,6 +483,8 @@ public: QString cppSignature() const; + QString pythonSignature() const; + AbstractMetaType *copy() const; bool applyArrayModification(QString *errorMessage); @@ -540,12 +542,14 @@ public: private: TypeUsagePattern determineUsagePattern() const; QString formatSignature(bool minimal) const; + QString formatPythonSignature(bool minimal) const; const TypeEntry *m_typeEntry = nullptr; AbstractMetaTypeList m_instantiations; QString m_package; mutable QString m_name; mutable QString m_cachedCppSignature; + mutable QString m_cachedPythonSignature; QString m_originalTypeDescription; int m_arrayElementCount = -1; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 412199bee..b86a5eb46 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -4562,23 +4562,6 @@ void CppGenerator::writeMethodDefinition(QTextStream &s, const AbstractMetaFunct s << ',' << endl; } -static QString resolveRetOrArgType(const AbstractMetaType *someType) -{ - QString strRetArg; - if (CppGenerator::isCString(someType)) { - strRetArg = QLatin1String("str"); - } else if (someType->isPrimitive()) { - auto ptp = static_cast(someType->typeEntry()); - while (ptp->referencedTypeEntry()) - ptp = ptp->referencedTypeEntry(); - strRetArg = ptp->name(); - } else { - strRetArg = someType->fullName(); - } - strRetArg.replace(QLatin1String("::"), QLatin1String(".")); - return strRetArg; -} - void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads) { OverloadData overloadData(overloads, this); @@ -4592,11 +4575,7 @@ void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunction QStringList args; const AbstractMetaArgumentList &arguments = f->arguments(); for (const AbstractMetaArgument *arg : arguments) { - AbstractMetaType *argType = getTypeWithoutContainer(arg->type()); - QString strArg = resolveRetOrArgType(arg->type()); - // PYSIDE-921: Handle container returntypes correctly. - if (argType != arg->type()) - strArg += QLatin1Char('[') + resolveRetOrArgType(argType) + QLatin1Char(']'); + QString strArg = arg->type()->pythonSignature(); if (!arg->defaultValueExpression().isEmpty()) { strArg += QLatin1Char('='); QString e = arg->defaultValueExpression(); @@ -4611,12 +4590,8 @@ void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunction if (multiple) s << idx-- << ':'; s << funcName << '(' << args.join(QLatin1Char(',')) << ')'; - AbstractMetaType *returnType = getTypeWithoutContainer(f->type()); - // PYSIDE-921: Handle container returntypes correctly. - if (returnType != f->type()) - s << "->" << resolveRetOrArgType(f->type()) << '[' << resolveRetOrArgType(returnType) << ']'; - else if (returnType) - s << "->" << resolveRetOrArgType(returnType); + if (f->type()) + s << "->" << f->type()->pythonSignature(); s << endl; } } diff --git a/sources/shiboken2/libshiboken/signature_doc.rst b/sources/shiboken2/libshiboken/signature_doc.rst index 9c42c5976..a984de4ce 100644 --- a/sources/shiboken2/libshiboken/signature_doc.rst +++ b/sources/shiboken2/libshiboken/signature_doc.rst @@ -73,8 +73,8 @@ It calls ``GetSignature_Function`` which returns the signature if it is found. Why this Code is Fast --------------------- -It costs a little time (maybe 4 seconds) to run througs every single signature -object, since these are more than 15000 Python objects. But all the signature +It costs a little time (maybe 6 seconds) to run througs every single signature +object, since these are more than 25000 Python objects. But all the signature objects will be rarely accessed but in special applications. The normal case are only a few accesses, and these are working pretty fast. @@ -111,10 +111,6 @@ the ``signature`` Python package. It has the following structure:: shiboken2/files.dir/shibokensupport/ backport_inspect.py - python_minilib_2_7.py - python_minilib_3_5.py - python_minilib_3_6.py - python_minilib_3_7.py signature/ loader.py @@ -125,6 +121,8 @@ the ``signature`` Python package. It has the following structure:: lib/ enum_sig.py + tool.py + Really important are the **parser**, **mapping**, **errorhandler**, **enum_sig**, diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 36b137104..5d6a24016 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -65,7 +65,6 @@ Point = typing.Tuple[float, float] Variant = typing.Any ModelIndexList = typing.List[int] QImageCleanupFunction = typing.Callable -StringList = typing.List[str] _S = TypeVar("_S") @@ -195,6 +194,27 @@ class Default(_NotCalled): class Instance(_NotCalled): pass +# Parameterized primitive variables +class _Parameterized(object): + def __init__(self, type): + self.type = type + self.__name__ = self.__class__.__name__ + + def __repr__(self): + return "{}({})".format( + type(self).__name__, self.type.__name__) + +# Mark the primitive variables to be moved into the result. +class ResultVariable(_Parameterized): + pass + +# Mark the primitive variables to become Sequence, Iterator or List +# (decided in the parser). +class ArrayLikeVariable(_Parameterized): + pass + +StringList = ArrayLikeVariable(str) + class Reloader(object): """ @@ -262,7 +282,7 @@ type_map.update({ "double": float, "float": float, "int": int, - "List": typing.List, + "List": ArrayLikeVariable, "long": int, "PyCallable": typing.Callable, "PyObject": object, @@ -275,7 +295,7 @@ type_map.update({ "qint64": int, "qint8": int, "qintptr": int, - "QList": typing.List, + "QList": ArrayLikeVariable, "qlonglong": int, "QMap": typing.Dict, "QPair": typing.Tuple, @@ -297,17 +317,27 @@ type_map.update({ "short": int, "signed char": Char, "signed long": int, + "std.list": typing.List, + "std.map": typing.Dict, + "std.pair": typing.Tuple, + "std.vector": typing.List, "str": str, "true": True, + "Tuple": typing.Tuple, + "uchar": Char, + "uchar*": str, + "uint": int, + "ulong": int, "ULONG_MAX": ulong_max, - "unsigned char": Char, - "unsigned int": int, # should we define an unsigned type? + "unsigned char": Char, # 5.9 + "unsigned char*": str, + "unsigned int": int, "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 + "ushort": int, "void": int, # be more specific? "WId": WId, "zero(bytes)": b"", @@ -316,6 +346,50 @@ type_map.update({ "zero(int)": 0, "zero(object)": None, "zero(str)": "", + "zero(typing.Any)": None, + }) + +type_map.update({ + # Handling variables declared as array: + "array double*" : ArrayLikeVariable(float), + "array float*" : ArrayLikeVariable(float), + "array GLint*" : ArrayLikeVariable(int), + "array GLuint*" : ArrayLikeVariable(int), + "array int*" : ArrayLikeVariable(int), + "array long long*" : ArrayLikeVariable(int), + "array short*" : ArrayLikeVariable(int), + "array signed char*" : bytes, + "array unsigned char*" : bytes, + "array unsigned int*" : ArrayLikeVariable(int), + "array unsigned short*" : ArrayLikeVariable(int), + }) + +type_map.update({ + # Special cases: + "char*" : bytes, + "QChar*" : bytes, + "quint32*" : int, # only for QRandomGenerator + "quint8*" : bytearray, # only for QCborStreamReader and QCborValue + "uchar*" : bytes, + "unsigned char*": bytes, + }) + +type_map.update({ + # Handling variables that are returned, eventually as Tuples: + "bool*" : ResultVariable(bool), + "float*" : ResultVariable(float), + "int*" : ResultVariable(int), + "long long*" : ResultVariable(int), + "long*" : ResultVariable(int), + "PStr*" : ResultVariable(str), # module sample + "qint32*" : ResultVariable(int), + "qint64*" : ResultVariable(int), + "qreal*" : ResultVariable(float), + "QString*" : ResultVariable(str), + "quint16*" : ResultVariable(int), + "uint*" : ResultVariable(int), + "unsigned int*" : ResultVariable(int), + "QStringList*" : ResultVariable(StringList), }) @@ -340,6 +414,7 @@ def init_sample(): import datetime type_map.update({ "char": Char, + "char**": typing.List[str], "Complex": complex, "double": float, "Foo.HANDLE": int, @@ -355,7 +430,8 @@ def init_sample(): "sample.int": int, "sample.ObjectType": object, "sample.OddBool": bool, - "sample.Photon.TemplateBase": Missing("sample.Photon.TemplateBase"), + "sample.Photon.TemplateBase[Photon.DuplicatorType]": sample.Photon.ValueDuplicator, + "sample.Photon.TemplateBase[Photon.IdentityType]": sample.Photon.ValueIdentity, "sample.Point": Point, "sample.PStr": str, "sample.unsigned char": Char, @@ -390,6 +466,7 @@ def init_smart(): }) return locals() + # The PySide Part def init_PySide2_QtCore(): from PySide2.QtCore import Qt, QUrl, QDir @@ -411,50 +488,18 @@ def init_PySide2_QtCore(): "list of QAbstractAnimation": typing.List[PySide2.QtCore.QAbstractAnimation], "list of QAbstractState": typing.List[PySide2.QtCore.QAbstractState], "long long": int, - "long": int, "NULL": None, # 5.6, MSVC "nullptr": None, # 5.9 "PyByteArray": bytearray, "PyBytes": bytes, - "PyCallable": typing.Callable, - "PyObject": object, - "PySequence": typing.Iterable, # important for numpy - "PySide2.QtCore.bool": bool, - "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.quint16": int, - "PySide2.QtCore.quint32": int, - "PySide2.QtCore.quint64": int, - "PySide2.QtCore.quint8": int, + "PySide2.QtCore.QCborStreamReader.StringResult[PySide2.QtCore.QByteArray]": + PySide2.QtCore.QCborStringResultByteArray, + "PySide2.QtCore.QCborStreamReader.StringResult[QString]": + PySide2.QtCore.QCborStringResultString, "PySide2.QtCore.QUrl.ComponentFormattingOptions": PySide2.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why??? - "PySide2.QtCore.QVariant": Variant, - "PySide2.QtCore.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.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)"), "QDir.SortFlags(Name | IgnoreCase)": Instance( @@ -465,24 +510,21 @@ def init_PySide2_QtCore(): "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC "QGenericArgument(nullptr)": ellipsis, # 5.10 "QGenericArgument(Q_NULLPTR)": ellipsis, - "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, + "QModelIndexList": ModelIndexList, "QString()": "", - "QStringList": StringList, "QStringList()": [], "QStringRef": str, - "Qt.HANDLE": int, # be more explicit with some consts? - "quintptr": int, + "QStringRef": str, + "Qt.HANDLE": int, # be more explicit with some constants? "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], + "QVariantMap": typing.Dict[str, Variant], }) try: type_map.update({ @@ -502,16 +544,12 @@ def init_PySide2_QtGui(): "GL_COLOR_BUFFER_BIT": GL_COLOR_BUFFER_BIT, "GL_NEAREST": GL_NEAREST, "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 + "QPlatformSurface*": int, # a handle "QVector< QTextLayout.FormatRange >()": [], # do we need more structure? "uint32_t": int, "uint8_t": int, "USHRT_MAX": ushort_max, - "WId": WId, }) return locals() @@ -522,7 +560,6 @@ 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, @@ -545,9 +582,12 @@ def init_PySide2_QtSql(): def init_PySide2_QtNetwork(): + best_structure = typing.OrderedDict if getattr(typing, "OrderedDict", None) else typing.Dict type_map.update({ - "QMultiMap": MultiMap, + "QMultiMap[PySide2.QtNetwork.QSsl.AlternativeNameEntryType, QString]": + best_structure[PySide2.QtNetwork.QSsl.AlternativeNameEntryType, typing.List[str]], }) + del best_structure return locals() @@ -566,6 +606,7 @@ def init_PySide2_QtMultimedia(): check_module(PySide2.QtMultimediaWidgets) type_map.update({ "QGraphicsVideoItem": PySide2.QtMultimediaWidgets.QGraphicsVideoItem, + "qint64": int, "QVideoWidget": PySide2.QtMultimediaWidgets.QVideoWidget, }) return locals() @@ -578,26 +619,23 @@ def init_PySide2_QtOpenGL(): "GLfloat": float, # 5.6, MSVC 15 "GLint": int, "GLuint": int, - "PySide2.QtOpenGL.GLint": int, - "PySide2.QtOpenGL.GLuint": int, }) return locals() def init_PySide2_QtQml(): type_map.update({ - "PySide2.QtQml.bool volatile": bool, "QJSValueList()": [], - "QVariantHash()": typing.Dict[str, Variant], # XXX sorted? + "QVariantHash()": typing.Dict[str, Variant], # from 5.9 }) return locals() def init_PySide2_QtQuick(): type_map.update({ - "PySide2.QtCore.uint": int, - "PySide2.QtQuick.QSharedPointer": int, - "T": int, + "PySide2.QtQuick.QSharedPointer[PySide2.QtQuick.QQuickItemGrabResult]": + PySide2.QtQuick.QQuickItemGrabResult, + "UnsignedShortType": int, }) return locals() @@ -611,6 +649,7 @@ def init_PySide2_QtScript(): def init_PySide2_QtTest(): type_map.update({ + "PySide2.QtTest.QTest.PySideQTouchEventSequence": PySide2.QtTest.QTest.QTouchEventSequence, "PySide2.QtTest.QTouchEventSequence": PySide2.QtTest.QTest.QTouchEventSequence, }) return locals() @@ -631,6 +670,10 @@ def init_PySide2_QtDataVisualization(): QtDataVisualization.QSurfaceDataArray = typing.List[QtDataVisualization.QSurfaceDataRow] type_map.update({ "100.0f": 100.0, + "QtDataVisualization.QBarDataArray": QtDataVisualization.QBarDataArray, + "QtDataVisualization.QBarDataArray*": QtDataVisualization.QBarDataArray, + "QtDataVisualization.QSurfaceDataArray": QtDataVisualization.QSurfaceDataArray, + "QtDataVisualization.QSurfaceDataArray*": QtDataVisualization.QSurfaceDataArray, }) return locals() diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 6109bceee..204f37384 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -46,7 +46,7 @@ import types import keyword import functools from shibokensupport.signature.mapping import (type_map, update_mapping, - namespace, typing, _NotCalled) + namespace, typing, _NotCalled, ResultVariable, ArrayLikeVariable) from shibokensupport.signature.lib.tool import (SimpleNamespace, build_brace_pattern) @@ -169,7 +169,9 @@ def _resolve_value(thing, valtype, line): if thing in ("0", "None") and valtype: if valtype.startswith("PySide2."): return None - name = type_map[valtype].__name__ + map = type_map[valtype] + # typing.Any: '_SpecialForm' object has no attribute '__name__' + name = map.__name__ if hasattr(map, "__name__") else str(map) thing = "zero({})".format(name) if thing in type_map: return type_map[thing] @@ -216,8 +218,6 @@ def to_string(thing): 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" @@ -235,7 +235,7 @@ def lno(level): """ -def _resolve_type(thing, line, level): +def _resolve_type(thing, line, level, var_handler): # Capture total replacements, first. Happens in # "PySide2.QtCore.QCborStreamReader.StringResult[PySide2.QtCore.QByteArray]" if thing in type_map: @@ -250,13 +250,13 @@ def _resolve_type(thing, line, level): # Special case: Handle the generic matrices. if contr == matrix_pattern: return handle_matrix(thing) - contr = _resolve_type(contr, line, level+1) + contr = var_handler(_resolve_type(contr, line, level+1, var_handler)) 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) + part = var_handler(_resolve_type(part, line, level+1, var_handler)) if isinstance(part, _NotCalled): # fix the tag (i.e. "Missing") by repr part = repr(part) @@ -267,6 +267,46 @@ def _resolve_type(thing, line, level): return _resolve_value(thing, None, line) +def _handle_generic(obj, repl): + """ + Assign repl if obj is an ArrayLikeVariable + + This is a neat trick. Example: + + obj repl result + ---------------------- -------- --------- + ArrayLikeVariable List List + ArrayLikeVariable(str) List List[str] + ArrayLikeVariable Sequence Sequence + ArrayLikeVariable(str) Sequence Sequence[str] + """ + if isinstance(obj, ArrayLikeVariable): + return repl[obj.type] + if isinstance(obj, type) and issubclass(obj, ArrayLikeVariable): + # was "if obj is ArrayLikeVariable" + return repl + return obj + + +def handle_argvar(obj): + """ + Decide how array-like variables are resolved in arguments + + Currently, the best approximation is types.Sequence. + We want to change that to types.Iterable in the near future. + """ + return _handle_generic(obj, typing.Sequence) + + +def handle_retvar(obj): + """ + Decide how array-like variables are resolved in results + + This will probably stay typing.List forever. + """ + return _handle_generic(obj, typing.List) + + def calculate_props(line): parsed = SimpleNamespace(**_parse_line(line.strip())) arglist = parsed.arglist @@ -280,14 +320,14 @@ def calculate_props(line): ann = 'NULL' # maps to None tup = name, ann arglist[idx] = tup - annotations[name] = _resolve_type(ann, line, 0) + annotations[name] = _resolve_type(ann, line, 0, handle_argvar) if len(tup) == 3: default = _resolve_value(tup[2], ann, line) _defaults.append(default) defaults = tuple(_defaults) returntype = parsed.returntype if returntype is not None: - annotations["return"] = _resolve_type(returntype, line, 0) + annotations["return"] = _resolve_type(returntype, line, 0, handle_retvar) props = SimpleNamespace() props.defaults = defaults props.kwdefaults = {} @@ -298,9 +338,61 @@ def calculate_props(line): shortname = funcname[funcname.rindex(".")+1:] props.name = shortname props.multi = parsed.multi + fix_variables(props, line) return vars(props) +def fix_variables(props, line): + annos = props.annotations + if not any(isinstance(ann, (ResultVariable, ArrayLikeVariable)) + for ann in annos.values()): + return + retvar = annos.get("return", None) + if retvar and isinstance(retvar, (ResultVariable, ArrayLikeVariable)): + # Special case: a ResultVariable which is the result will always be an array! + annos["return"] = retvar = typing.List[retvar.type] + fullname = props.fullname + varnames = list(props.varnames) + defaults = list(props.defaults) + diff = len(varnames) - len(defaults) + + safe_annos = annos.copy() + retvars = [retvar] if retvar else [] + deletions = [] + for idx, name in enumerate(varnames): + ann = safe_annos[name] + if isinstance(ann, ArrayLikeVariable): + ann = typing.Sequence[ann.type] + annos[name] = ann + if not isinstance(ann, ResultVariable): + continue + # We move the variable to the end and remove it. + retvars.append(ann.type) + deletions.append(idx) + del annos[name] + for idx in reversed(deletions): + # varnames: 0 1 2 3 4 5 6 7 + # defaults: 0 1 2 3 4 + # diff: 3 + del varnames[idx] + if idx >= diff: + del defaults[idx - diff] + else: + diff -= 1 + if retvars: + rvs = [] + retvars = list(handle_retvar(rv) if isinstance(rv, ArrayLikeVariable) else rv + for rv in retvars) + if len(retvars) == 1: + returntype = retvars[0] + else: + typestr = "typing.Tuple[{}]".format(", ".join(map(to_string, retvars))) + returntype = eval(typestr, namespace) + props.annotations["return"] = returntype + props.varnames = tuple(varnames) + props.defaults = tuple(defaults) + + def fixup_multilines(lines): """ Multilines can collapse when certain distinctions between C++ types diff --git a/sources/shiboken2/tests/samplebinding/pointerprimitivetype_test.py b/sources/shiboken2/tests/samplebinding/pointerprimitivetype_test.py new file mode 100644 index 000000000..c40770862 --- /dev/null +++ b/sources/shiboken2/tests/samplebinding/pointerprimitivetype_test.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +""" +pointerprimitivetype_test.py + +check that the primitive types are correctly mapped by the signature module. + +Mapping +------- +IntArray2(const int*) -- +getMargins(int*,int*,int*,int*)const -- typing.Tuple[int, int, int, int]> + +We explicitly check only against typing.Iterable in the first test, +because typing.Sequence is a subclass, but we will generalize this +to typing.Iterable in the future. +""" + +import unittest +from sample import IntArray2, VirtualMethods + +import shiboken2 +type.__signature__ # trigger init, which does not happen in tests +from shibokensupport.signature import typing + + +class PointerPrimitiveTypeTest(unittest.TestCase): + + def testArraySignature(self): + # signature="IntArray2(const int*)" + found = False + for sig in IntArray2.__signature__: + if "data" in sig.parameters: + found = True + break + self.assertTrue(found) + ann = sig.parameters["data"].annotation + self.assertEqual(ann.__args__, (int,)) + # un-specify this class (forget "int") by setting the _special + # flag, so we can check using issubclass (undocumented feature). + ann._special = True + self.assertTrue(issubclass(ann, typing.Iterable)) + + def testReturnVarSignature(self): + # signature="getMargins(int*,int*,int*,int*)const"> + ann = VirtualMethods.getMargins.__signature__.return_annotation + self.assertEqual(ann, typing.Tuple[int, int, int, int]) + + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.3