diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-03-01 08:19:26 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-03-01 08:19:26 +0100 |
commit | 639c5df6ad397ba98ca727c744278372087b2d46 (patch) | |
tree | 31e84eab330cb8a0a1f1236c85c8fad968918ceb | |
parent | b97ccab8c268d260f704e11b33d64fa31f74e258 (diff) | |
parent | 099f3f46ca9ec1362f211278df4b3e4949b0a339 (diff) |
Merge remote-tracking branch 'origin/5.12' into dev
Change-Id: I0f3c2de6195b863dc60ecbb988e4e14182e3ad03
30 files changed, 949 insertions, 96 deletions
diff --git a/sources/pyside2/PySide2/QtPrintSupport/CMakeLists.txt b/sources/pyside2/PySide2/QtPrintSupport/CMakeLists.txt index 3482c68fe..74d3dfb88 100644 --- a/sources/pyside2/PySide2/QtPrintSupport/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtPrintSupport/CMakeLists.txt @@ -13,6 +13,9 @@ ${QtPrintSupport_GEN_DIR}/qprintpreviewwidget_wrapper.cpp ${QtPrintSupport_GEN_DIR}/qtprintsupport_module_wrapper.cpp ) +configure_file("${QtPrintSupport_SOURCE_DIR}/typesystem_printsupport.xml.in" + "${QtPrintSupport_BINARY_DIR}/typesystem_printsupport.xml" @ONLY) + set(QtPrintSupport_include_dirs ${QtPrintSupport_SOURCE_DIR} ${QtPrintSupport_BINARY_DIR} ${Qt5Core_INCLUDE_DIRS} @@ -36,4 +39,5 @@ create_pyside_module(NAME QtPrintSupport LIBRARIES QtPrintSupport_libraries DEPS QtPrintSupport_deps TYPESYSTEM_PATH QtPrintSupport_SOURCE_DIR - SOURCES QtPrintSupport_SRC) + SOURCES QtPrintSupport_SRC + TYPESYSTEM_NAME ${QtPrintSupport_BINARY_DIR}/typesystem_printsupport.xml) diff --git a/sources/pyside2/PySide2/QtPrintSupport/typesystem_printsupport.xml.in b/sources/pyside2/PySide2/QtPrintSupport/typesystem_printsupport.xml.in new file mode 100644 index 000000000..7949b2daa --- /dev/null +++ b/sources/pyside2/PySide2/QtPrintSupport/typesystem_printsupport.xml.in @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ +--> +<typesystem package="PySide2.QtPrintSupport"> + <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/> + <load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/> + <load-typesystem name="QtPrintSupport/typesystem_printsupport_common.xml" generate="yes"/> +</typesystem> diff --git a/sources/pyside2/PySide2/QtPrintSupport/typesystem_printsupport.xml b/sources/pyside2/PySide2/QtPrintSupport/typesystem_printsupport_common.xml index f85aadc79..5026997c1 100644 --- a/sources/pyside2/PySide2/QtPrintSupport/typesystem_printsupport.xml +++ b/sources/pyside2/PySide2/QtPrintSupport/typesystem_printsupport_common.xml @@ -40,7 +40,6 @@ ****************************************************************************/ --> <typesystem package="PySide2.QtPrintSupport"> - <load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/> <object-type name="QPageSetupDialog"> <modify-function signature="exec()" rename="exec_" allow-thread="yes"/> @@ -105,6 +104,12 @@ <extra-includes> <include file-name="QPrinterInfo" location="global"/> </extra-includes> + <!-- fixme: Check if this is still required in Qt 6: + bool QPagedPaintDevice::setPageSize(QPageSize) + void QPagedPaintDevice::setPageSize(QPagedPaintDevice::PageSize) --> + <add-function signature="setPageSize(const QPageSize&)" return-type="bool"> + <inject-code file="../glue/qtprintsupport.cpp" snippet="setpagesize" /> + </add-function> </object-type> <object-type name="QPrintPreviewDialog"/> diff --git a/sources/pyside2/PySide2/QtUiTools/typesystem_uitools.xml b/sources/pyside2/PySide2/QtUiTools/typesystem_uitools.xml index 8086da01e..2cbe490aa 100644 --- a/sources/pyside2/PySide2/QtUiTools/typesystem_uitools.xml +++ b/sources/pyside2/PySide2/QtUiTools/typesystem_uitools.xml @@ -90,18 +90,21 @@ <modify-function signature="createAction(QObject*,const QString&)"> <modify-argument index="return"> <parent index="1" action="add"/> + <define-ownership class="target" owner="default"/> </modify-argument> </modify-function> <modify-function signature="createActionGroup(QObject*,const QString&)"> <modify-argument index="return"> <parent index="1" action="add"/> + <define-ownership class="target" owner="default"/> </modify-argument> </modify-function> <modify-function signature="createLayout(const QString&,QObject*,const QString&)"> <modify-argument index="return"> <parent index="2" action="add"/> + <define-ownership class="target" owner="default"/> </modify-argument> </modify-function> diff --git a/sources/pyside2/PySide2/QtWebEngineWidgets/CMakeLists.txt b/sources/pyside2/PySide2/QtWebEngineWidgets/CMakeLists.txt index 555be9c41..214ebc56b 100644 --- a/sources/pyside2/PySide2/QtWebEngineWidgets/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtWebEngineWidgets/CMakeLists.txt @@ -34,6 +34,7 @@ set(QtWebEngineWidgets_include_dirs ${QtNetwork_GEN_DIR} ${QtWebChannel_GEN_DIR} ${QtWebEngineCore_GEN_DIR} + ${QtPrintSupport_GEN_DIR} ) set(QtWebEngineWidgets_libraries pyside2 ${Qt5WebEngineWidgets_LIBRARIES} @@ -42,8 +43,9 @@ set(QtWebEngineWidgets_libraries pyside2 ${Qt5Widgets_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Core_LIBRARIES} + ${Qt5PrintSupport_LIBRARIES} ) -set(QtWebEngineWidgets_deps QtGui QtWidgets QtNetwork QtWebChannel) +set(QtWebEngineWidgets_deps QtGui QtWidgets QtNetwork QtWebChannel QtPrintSupport) create_pyside_module(NAME QtWebEngineWidgets INCLUDE_DIRS QtWebEngineWidgets_include_dirs LIBRARIES QtWebEngineWidgets_libraries diff --git a/sources/pyside2/PySide2/QtWebEngineWidgets/typesystem_webenginewidgets.xml b/sources/pyside2/PySide2/QtWebEngineWidgets/typesystem_webenginewidgets.xml index e51303c42..cd4cd8a91 100644 --- a/sources/pyside2/PySide2/QtWebEngineWidgets/typesystem_webenginewidgets.xml +++ b/sources/pyside2/PySide2/QtWebEngineWidgets/typesystem_webenginewidgets.xml @@ -46,6 +46,7 @@ <load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/> <load-typesystem name="QtWebChannel/typesystem_webchannel.xml" generate="no"/> <load-typesystem name="QtWebEngineCore/typesystem_webenginecore.xml" generate="no"/> + <load-typesystem name="QtPrintSupport/typesystem_printsupport.xml" generate="no"/> <object-type name="QWebEngineCertificateError"> diff --git a/sources/pyside2/PySide2/glue/qtprintsupport.cpp b/sources/pyside2/PySide2/glue/qtprintsupport.cpp new file mode 100644 index 000000000..300a498c0 --- /dev/null +++ b/sources/pyside2/PySide2/glue/qtprintsupport.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// @snippet setpagesize +bool out = %CPPSELF.setPageSize(%1); +%PYARG_0 = %CONVERTTOPYTHON[bool](out); +// @snippet setpagesize diff --git a/sources/pyside2/PySide2/support/generate_pyi.py b/sources/pyside2/PySide2/support/generate_pyi.py index a92ee76f0..21ef841fe 100644 --- a/sources/pyside2/PySide2/support/generate_pyi.py +++ b/sources/pyside2/PySide2/support/generate_pyi.py @@ -53,6 +53,13 @@ import re import subprocess import argparse import glob +# PYSIDE-953: Use a newer contextlib for Python 3.5 +skip_creation = False +if sys.version_info[:2] == (3, 5): + try: + import PySide2.support.signature # gets new contextlib + except: + skip_creation = True from contextlib import contextmanager from textwrap import dedent @@ -272,6 +279,9 @@ def single_process(lockdir): def generate_all_pyi(outpath, options): + if skip_creation: + logger.warn("Sorry, we cannot create .pyi files with Python 3.5 while PySide") + logger.warn(" is not installed. Please run it by hand!") ps = os.pathsep if options.sys_path: # make sure to propagate the paths from sys_path to subprocesses diff --git a/sources/pyside2/doc/index.rst b/sources/pyside2/doc/index.rst index 474449a69..7f3b8e4a7 100644 --- a/sources/pyside2/doc/index.rst +++ b/sources/pyside2/doc/index.rst @@ -1,12 +1,12 @@ |project| ************* -The |project| product enables the use of Qt5 APIs in Python applications. It -lets Python developers utilize the full potential of Qt, using the |pymodname| module. -The |pymodname| module provides access to the individual Qt modules such as QtCore, -QtGui, and so on. |project| also comes with the :doc:`Shiboken2 <shiboken2:index>` -CPython binding code generator, which can be used to generate Python bindings for -your C or C++ code. +|project| offers Python bindings for Qt, enabling the use of Qt5 APIs in Python +applications. It lets Python developers utilize the full potential of Qt, using +the |pymodname| module. The |pymodname| module provides access to the individual +Qt modules such as QtCore, QtGui, and so on. |project| also comes with the +:doc:`Shiboken2 <shiboken2:index>` CPython binding code generator, which can be +used to generate Python bindings for your C or C++ code. .. toctree:: :name: mastertoc diff --git a/sources/pyside2/tests/registry/init_platform.py b/sources/pyside2/tests/registry/init_platform.py index a324c36a2..6031dd2ad 100644 --- a/sources/pyside2/tests/registry/init_platform.py +++ b/sources/pyside2/tests/registry/init_platform.py @@ -55,7 +55,8 @@ shiboken and pysidetest projects. import sys import os import re -from contextlib import contextmanager +# PYSIDE-953: Use a newer contextlib for Python 3.5 +# from contextlib import contextmanager from textwrap import dedent script_dir = os.path.normpath(os.path.join(__file__, *".. .. .. .. ..".split())) @@ -117,6 +118,9 @@ sys.path[:0] = [os.path.join(shiboken_build_dir, "shibokenmodule"), pyside_build_dir] import PySide2 +# PYSIDE-953: Use a newer contextlib for Python 3.5 +import PySide2.support.signature # new contextlib +from contextlib import contextmanager all_modules = list("PySide2." + x for x in PySide2.__all__) diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 15700e91e..37ff3b72c 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -1871,40 +1871,12 @@ bool AbstractMetaBuilderPrivate::setArrayArgumentType(AbstractMetaFunction *func return true; } -static bool generateExceptionHandling(const AbstractMetaFunction *func, - ExceptionSpecification spec, - TypeSystem::ExceptionHandling handling) -{ - switch (func->functionType()) { - case AbstractMetaFunction::CopyConstructorFunction: - case AbstractMetaFunction::MoveConstructorFunction: - case AbstractMetaFunction::AssignmentOperatorFunction: - case AbstractMetaFunction::MoveAssignmentOperatorFunction: - case AbstractMetaFunction::DestructorFunction: - return false; - default: - break; - } - switch (handling) { - case TypeSystem::ExceptionHandling::On: - return true; - case TypeSystem::ExceptionHandling::AutoDefaultToOn: - return spec != ExceptionSpecification::NoExcept; - case TypeSystem::ExceptionHandling::AutoDefaultToOff: - return spec == ExceptionSpecification::Throws; - default: - break; - } - return false; -} - AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem) { if (functionItem->isDeleted() || !functionItem->templateParameters().isEmpty()) return nullptr; QString functionName = functionItem->name(); QString className; - TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; if (m_currentClass) { // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT // and overridden metaObject(), QGADGET helpers @@ -1913,7 +1885,6 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio return nullptr; } className = m_currentClass->typeEntry()->qualifiedCppName(); - exceptionHandling = m_currentClass->typeEntry()->exceptionHandling(); if (functionName == QLatin1String("metaObject") && className != QLatin1String("QObject")) return nullptr; } @@ -2085,16 +2056,12 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio const FunctionModificationList functionMods = metaFunction->modifications(m_currentClass); for (const FunctionModification &mod : functionMods) { - if (mod.exceptionHandling() != TypeSystem::ExceptionHandling::Unspecified) { - exceptionHandling = mod.exceptionHandling(); - break; - } + if (mod.exceptionHandling() != TypeSystem::ExceptionHandling::Unspecified) + metaFunction->setExceptionHandlingModification(mod.exceptionHandling()); + else if (mod.allowThread() != TypeSystem::AllowThread::Unspecified) + metaFunction->setAllowThreadModification(mod.allowThread()); } - metaFunction->setGenerateExceptionHandling(generateExceptionHandling(metaFunction, - functionItem->exceptionSpecification(), - exceptionHandling)); - // Find the correct default values for (int i = 0, size = metaArguments.size(); i < size; ++i) { const ArgumentModelItem &arg = arguments.at(i); diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 73f1bf621..a10a15b08 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -72,6 +72,26 @@ MetaClass *findByName(QVector<MetaClass *> haystack, QStringView needle) return nullptr; } +// Helper for recursing the base classes of an AbstractMetaClass. +// Returns the class for which the predicate is true. +template <class Predicate> +const AbstractMetaClass *recurseClassHierarchy(const AbstractMetaClass *klass, + Predicate pred) +{ + if (pred(klass)) + return klass; + if (auto base = klass->baseClass()) { + if (auto r = recurseClassHierarchy(base, pred)) + return r; + } + const auto interfaces = klass->interfaces(); + for (auto i : interfaces) { + if (auto r = recurseClassHierarchy(i, pred)) + return r; + } + return nullptr; +} + /******************************************************************************* * AbstractMetaVariable */ @@ -411,8 +431,7 @@ AbstractMetaFunction::AbstractMetaFunction() m_userAdded(false), m_explicit(false), m_pointerOperator(false), - m_isCallOperator(false), - m_generateExceptionHandling(false) + m_isCallOperator(false) { } @@ -531,7 +550,8 @@ AbstractMetaFunction *AbstractMetaFunction::copy() const cpy->setType(type()->copy()); cpy->setConstant(isConstant()); cpy->setExceptionSpecification(m_exceptionSpecification); - cpy->setGenerateExceptionHandling(m_generateExceptionHandling); + cpy->setAllowThreadModification(m_allowThreadModification); + cpy->setExceptionHandlingModification(m_exceptionHandlingModification); for (AbstractMetaArgument *arg : m_arguments) cpy->addArgument(arg->copy()); @@ -754,28 +774,40 @@ bool AbstractMetaFunction::autoDetectAllowThread() const return !maybeGetter; } -bool AbstractMetaFunction::allowThread() const +static inline TypeSystem::AllowThread allowThreadMod(const AbstractMetaClass *klass) { - using AllowThread = TypeSystem::AllowThread; + return klass->typeEntry()->allowThread(); +} - if (m_cachedAllowThread < 0) { - AllowThread allowThread = AllowThread::Auto; - // Find a modification that specifies allowThread - const FunctionModificationList &modifications = this->modifications(declaringClass()); - for (const FunctionModification &modification : modifications) { - if (modification.allowThread() != AllowThread::Unspecified) { - allowThread = modification.allowThread(); - break; - } - } +static inline bool hasAllowThreadMod(const AbstractMetaClass *klass) +{ + return allowThreadMod(klass) != TypeSystem::AllowThread::Unspecified; +} - m_cachedAllowThread = allowThread == AllowThread::Allow - || (allowThread == AllowThread::Auto && autoDetectAllowThread()) ? 1 : 0; +bool AbstractMetaFunction::allowThread() const +{ + auto allowThreadModification = m_allowThreadModification; + // If there is no modification on the function, check for a base class. + if (m_class && allowThreadModification == TypeSystem::AllowThread::Unspecified) { + if (auto base = recurseClassHierarchy(m_class, hasAllowThreadMod)) + allowThreadModification = allowThreadMod(base); + } - if (m_cachedAllowThread == 0) - qCDebug(lcShiboken).noquote() << msgDisallowThread(this); + bool result = true; + switch (allowThreadModification) { + case TypeSystem::AllowThread::Disallow: + result = false; + break; + case TypeSystem::AllowThread::Allow: + break; + case TypeSystem::AllowThread::Auto: + case TypeSystem::AllowThread::Unspecified: + result = autoDetectAllowThread(); + break; } - return m_cachedAllowThread > 0; + if (!result) + qCDebug(lcShiboken).noquote() << msgDisallowThread(this); + return result; } TypeSystem::Ownership AbstractMetaFunction::ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int key) const @@ -975,6 +1007,54 @@ void AbstractMetaFunction::setExceptionSpecification(ExceptionSpecification e) m_exceptionSpecification = e; } +static inline TypeSystem::ExceptionHandling exceptionMod(const AbstractMetaClass *klass) +{ + return klass->typeEntry()->exceptionHandling(); +} + +static inline bool hasExceptionMod(const AbstractMetaClass *klass) +{ + return exceptionMod(klass) != TypeSystem::ExceptionHandling::Unspecified; +} + +bool AbstractMetaFunction::generateExceptionHandling() const +{ + switch (m_functionType) { + case AbstractMetaFunction::CopyConstructorFunction: + case AbstractMetaFunction::MoveConstructorFunction: + case AbstractMetaFunction::AssignmentOperatorFunction: + case AbstractMetaFunction::MoveAssignmentOperatorFunction: + case AbstractMetaFunction::DestructorFunction: + return false; + default: + break; + } + + auto exceptionHandlingModification = m_exceptionHandlingModification; + // If there is no modification on the function, check for a base class. + if (m_class && exceptionHandlingModification == TypeSystem::ExceptionHandling::Unspecified) { + if (auto base = recurseClassHierarchy(m_class, hasExceptionMod)) + exceptionHandlingModification = exceptionMod(base); + } + + bool result = false; + switch (exceptionHandlingModification) { + case TypeSystem::ExceptionHandling::On: + result = true; + break; + case TypeSystem::ExceptionHandling::AutoDefaultToOn: + result = m_exceptionSpecification != ExceptionSpecification::NoExcept; + break; + case TypeSystem::ExceptionHandling::AutoDefaultToOff: + result = m_exceptionSpecification == ExceptionSpecification::Throws; + break; + case TypeSystem::ExceptionHandling::Unspecified: + case TypeSystem::ExceptionHandling::Off: + break; + } + return result; +} + bool AbstractMetaFunction::isOperatorOverload(const QString& funcName) { if (isConversionOperator(funcName)) @@ -1159,8 +1239,8 @@ void AbstractMetaFunction::formatDebugVerbose(QDebug &d) const d << " throw(...)"; break; } - if (m_generateExceptionHandling) - d << "[generate-exception-handling]"; + if (m_exceptionHandlingModification != TypeSystem::ExceptionHandling::Unspecified) + d << " exeption-mod " << int(m_exceptionHandlingModification); d << '('; for (int i = 0, count = m_arguments.size(); i < count; ++i) { if (i) diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index 1ab3049b2..074adbe00 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -851,8 +851,7 @@ public: ExceptionSpecification exceptionSpecification() const; void setExceptionSpecification(ExceptionSpecification e); - bool generateExceptionHandling() const { return m_generateExceptionHandling; } - void setGenerateExceptionHandling(bool g) { m_generateExceptionHandling = g; } + bool generateExceptionHandling() const; bool isConversionOperator() const { @@ -1096,6 +1095,12 @@ public: static AbstractMetaFunction * find(const AbstractMetaFunctionList &haystack, const QString &needle); + // for the meta builder only + void setAllowThreadModification(TypeSystem::AllowThread am) + { m_allowThreadModification = am; } + void setExceptionHandlingModification(TypeSystem::ExceptionHandling em) + { m_exceptionHandlingModification = em; } + #ifndef QT_NO_DEBUG_STREAM void formatDebugVerbose(QDebug &d) const; #endif @@ -1123,9 +1128,9 @@ private: uint m_explicit : 1; uint m_pointerOperator : 1; uint m_isCallOperator : 1; - uint m_generateExceptionHandling: 1; - mutable int m_cachedAllowThread = -1; ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown; + TypeSystem::AllowThread m_allowThreadModification = TypeSystem::AllowThread::Unspecified; + TypeSystem::ExceptionHandling m_exceptionHandlingModification = TypeSystem::ExceptionHandling::Unspecified; }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::CompareResult) diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst index c3180ae88..ce66f77fe 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst @@ -11,7 +11,7 @@ typesystem .. code-block:: xml - <typesystem package="..." default-superclass="..." exception-handling="..."> + <typesystem package="..." default-superclass="..." allow-thread="..." exception-handling="..."> </typesystem> The **package** attribute is a string describing the package to be used, @@ -19,8 +19,9 @@ typesystem The *optional* **default-superclass** attribute is the canonical C++ base class name of all objects, e.g., "object". - The *optional* **exception-handling** attribute specifies the default exception - handling mode of all objects (see :ref:`modify-function`). + The *optional* attributes **allow-thread** and **exception-handling** + specify the default handling for the corresponding function modification + (see :ref:`modify-function`). load-typesystem ^^^^^^^^^^^^^^^ @@ -219,6 +220,7 @@ value-type <typesystem> <value-type name="..." since="..." copyable="yes | no" + allow-thread="..." exception-handling="..." hash-function="..." stream="yes | no" @@ -247,8 +249,9 @@ value-type The **revision** attribute can be used to specify a revision for each type, easing the production of ABI compatible bindings. - The *optional* **exception-handling** attribute specifies the default exception - handling mode of all functions (see :ref:`modify-function`). + The *optional* attributes **allow-thread** and **exception-handling** + specify the default handling for the corresponding function modification + (see :ref:`modify-function`). .. _object-type: @@ -265,6 +268,7 @@ object-type <object-type name="..." since="..." copyable="yes | no" + allow-thread="..." exception-handling="..." hash-function="..." stream="yes | no" @@ -286,8 +290,9 @@ object-type The **revision** attribute can be used to specify a revision for each type, easing the production of ABI compatible bindings. - The *optional* **exception-handling** attribute specifies the default exception - handling mode of all functions (see :ref:`modify-function`). + The *optional* attributes **allow-thread** and **exception-handling** + specify the default handling for the corresponding function modification + (see :ref:`modify-function`). interface-type ^^^^^^^^^^^^^^ diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp index af24689fe..922f1c23f 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp @@ -222,6 +222,8 @@ void TestModifyFunction::testWithApiVersion() QVERIFY(func->ownership(func->ownerClass(), TypeSystem::TargetLangCode, 0) != TypeSystem::CppOwnership); } +// Modifications on class/typesystem level are tested below +// in testScopedModifications(). void TestModifyFunction::testAllowThread() { const char cppCode[] =R"CPP(\ @@ -315,23 +317,130 @@ void TestModifyFunction::testGlobalFunctionModification() QCOMPARE(arg->defaultValueExpression(), QLatin1String("A()")); } -void TestModifyFunction::testExceptionSpecification() +// Tests modifications of exception handling and allow-thread +// on various levels. +void TestModifyFunction::testScopedModifications_data() { - const char cppCode[] = R"CPP( -struct A { + QTest::addColumn<QByteArray>("cppCode"); + QTest::addColumn<QByteArray>("xmlCode"); + QTest::addColumn<bool>("expectedGenerateUnspecified"); + QTest::addColumn<bool>("expectedGenerateNonThrowing"); + QTest::addColumn<bool>("expectedGenerateThrowing"); + QTest::addColumn<bool>("expectedAllowThread"); + + const QByteArray cppCode = R"CPP( +struct Base { +}; + +struct A : public Base { void unspecified(); void nonThrowing() noexcept; void throwing() throw(int); }; )CPP"; - const char xmlCode[] = R"XML( -<typesystem package="Foo"> + + // Default: Off + QTest::newRow("none") + << cppCode + << QByteArray(R"XML( +<typesystem package= 'Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A'/> +</typesystem>)XML") + << false << false << false // exception + << true; // allowthread + + // Modify one function + QTest::newRow("modify-function1") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> <primitive-type name='int'/> + <object-type name='Base'/> <object-type name='A'> <modify-function signature='throwing()' exception-handling='auto-on'/> </object-type> -</typesystem>)XML"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); +</typesystem>)XML") + << false << false << true // exception + << true; // allowthread + + // Flip defaults by modifying functions + QTest::newRow("modify-function2") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A'> + <modify-function signature='unspecified()' exception-handling='auto-on'/> + <modify-function signature='throwing()' exception-handling='off'/> + </object-type> +</typesystem>)XML") + << true << false << false // exception + << true; // allowthread + + // Activate on type system level + QTest::newRow("typesystem-on") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo' exception-handling='auto-on' allow-thread='no'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A'/> +</typesystem>)XML") + << true << false << true // exception + << false; // allowthread + + // Activate on class level + QTest::newRow("class-on") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A' exception-handling='auto-on' allow-thread='no'/> +</typesystem>)XML") + << true << false << true // exception + << false; // allowthread + + // Activate on base class level + QTest::newRow("baseclass-on") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base' exception-handling='auto-on' allow-thread='no'/> + <object-type name='A'/> +</typesystem>)XML") + << true << false << true // exception + << false; // allowthread + + // Override value on class level + QTest::newRow("override-class-on") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A' exception-handling='auto-on'> + <modify-function signature='throwing()' exception-handling='no'/> + </object-type> +</typesystem>)XML") + << true << false << false // exception + << true; // allowthread +} + +void TestModifyFunction::testScopedModifications() +{ + QFETCH(QByteArray, cppCode); + QFETCH(QByteArray, xmlCode); + QFETCH(bool, expectedGenerateUnspecified); + QFETCH(bool, expectedGenerateNonThrowing); + QFETCH(bool, expectedGenerateThrowing); + QFETCH(bool, expectedAllowThread); + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode.constData(), xmlCode.constData(), false)); QVERIFY(!builder.isNull()); const AbstractMetaClass *classA = AbstractMetaClass::findClass(builder->classes(), QLatin1String("A")); @@ -340,17 +449,18 @@ struct A { const AbstractMetaFunction *f = classA->findFunction(QStringLiteral("unspecified")); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown); - QVERIFY(!f->generateExceptionHandling()); + QCOMPARE(f->generateExceptionHandling(), expectedGenerateUnspecified); + QCOMPARE(f->allowThread(), expectedAllowThread); f = classA->findFunction(QStringLiteral("nonThrowing")); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept); - QVERIFY(!f->generateExceptionHandling()); + QCOMPARE(f->generateExceptionHandling(), expectedGenerateNonThrowing); f = classA->findFunction(QStringLiteral("throwing")); QVERIFY(f); QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws); - QVERIFY(f->generateExceptionHandling()); + QCOMPARE(f->generateExceptionHandling(), expectedGenerateThrowing); } QTEST_APPLESS_MAIN(TestModifyFunction) diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h index 494f31991..375111e03 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h +++ b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h @@ -42,7 +42,8 @@ class TestModifyFunction : public QObject void testRenameArgument(); void invalidateAfterUse(); void testGlobalFunctionModification(); - void testExceptionSpecification(); + void testScopedModifications_data(); + void testScopedModifications(); }; #endif diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index ff4f74d8c..204253777 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -1281,6 +1281,7 @@ void Handler::applyComplexTypeAttributes(const QXmlStreamReader &reader, bool generate = true; ctype->setCopyable(ComplexTypeEntry::Unknown); auto exceptionHandling = m_exceptionHandling; + auto allowThread = m_allowThread; QString package = m_defaultPackage; for (int i = attributes->size() - 1; i >= 0; --i) { @@ -1316,6 +1317,15 @@ void Handler::applyComplexTypeAttributes(const QXmlStreamReader &reader, qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } + } else if (name == allowThreadAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = allowThreadFromAttribute(attribute.value()); + if (v != TypeSystem::AllowThread::Unspecified) { + allowThread = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } } else if (name == QLatin1String("held-type")) { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeWarning(reader, name))); @@ -1337,6 +1347,8 @@ void Handler::applyComplexTypeAttributes(const QXmlStreamReader &reader, if (exceptionHandling != TypeSystem::ExceptionHandling::Unspecified) ctype->setExceptionHandling(exceptionHandling); + if (allowThread != TypeSystem::AllowThread::Unspecified) + ctype->setAllowThread(allowThread); // The generator code relies on container's package being empty. if (ctype->type() != TypeEntry::ContainerType) @@ -1483,6 +1495,15 @@ TypeSystemTypeEntry *Handler::parseRootElement(const QXmlStreamReader &, qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } + } else if (name == allowThreadAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = allowThreadFromAttribute(attribute.value()); + if (v != TypeSystem::AllowThread::Unspecified) { + m_allowThread = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } } } diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h index b0144923a..36a75c599 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.h +++ b/sources/shiboken2/ApiExtractor/typesystem.h @@ -1395,6 +1395,9 @@ public: TypeSystem::ExceptionHandling exceptionHandling() const { return m_exceptionHandling; } void setExceptionHandling(TypeSystem::ExceptionHandling e) { m_exceptionHandling = e; } + TypeSystem::AllowThread allowThread() const { return m_allowThread; } + void setAllowThread(TypeSystem::AllowThread allowThread) { m_allowThread = allowThread; } + QString defaultConstructor() const; void setDefaultConstructor(const QString& defaultConstructor); bool hasDefaultConstructor() const; @@ -1433,6 +1436,7 @@ private: const ComplexTypeEntry* m_baseContainerType = nullptr; // For class functions TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; + TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified; }; class TypedefEntry : public ComplexTypeEntry diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h index a617110d6..a119b2a97 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_p.h +++ b/sources/shiboken2/ApiExtractor/typesystem_p.h @@ -246,6 +246,7 @@ private: QString m_defaultPackage; QString m_defaultSuperclass; TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; + TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified; QString m_error; const TypeEntry::CodeGeneration m_generate; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 3ce07cf93..f2112e34f 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -5535,6 +5535,8 @@ bool CppGenerator::finishGeneration() // cleanup staticMetaObject attribute if (usePySideExtensions()) { s << "void cleanTypesAttributes(void) {" << endl; + s << INDENT << "if (PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03060000)" << endl; + s << INDENT << " return; // testbinding crashes in Python 3.5 when hasattr touches types!" << endl; s << INDENT << "for (int i = 0, imax = SBK_" << moduleName() << "_IDX_COUNT; i < imax; i++) {" << endl; { Indentation indentation(INDENT); diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index ca5dab54e..ec0d466f7 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -2683,7 +2683,7 @@ void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const s << " = " << defaultCtor; return; } - if (isCppPrimitive(type)) + if (isCppPrimitive(type) || type->isSmartPointer()) return; const auto ctor = minimalConstructor(type); if (ctor.isValid()) { diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index d6c26ac79..61a097771 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -169,7 +169,7 @@ GetClassOfFunc(PyObject *ob) { if (PyType_Check(ob)) return ob; - if (Py_TYPE(ob) == &PyCFunction_Type) + if (PyType_IsSubtype(Py_TYPE(ob), &PyCFunction_Type)) return _get_class_of_cf(ob); if (Py_TYPE(ob) == PepStaticMethod_TypePtr) return _get_class_of_sm(ob); @@ -703,7 +703,7 @@ get_signature(PyObject *self, PyObject *args) if (Py_TYPE(ob) == PepFunction_TypePtr) Py_RETURN_NONE; - if (Py_TYPE(ob) == &PyCFunction_Type) + if (PyType_IsSubtype(Py_TYPE(ob), &PyCFunction_Type)) return pyside_cf_get___signature__(ob, modifier); if (Py_TYPE(ob) == PepStaticMethod_TypePtr) return pyside_sm_get___signature__(ob, modifier); diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index b37d0c941..952d31994 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -70,6 +70,10 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/__init__.py" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/enum_sig.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/enum_sig.py" COPYONLY) if (PYTHON_VERSION_MAJOR EQUAL 3) + if (PYTHON_VERSION_MINOR EQUAL 5) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/contextlib36.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/contextlib36.py" COPYONLY) + endif() else() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/backport_inspect.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/backport_inspect.py" COPYONLY) diff --git a/sources/shiboken2/shibokenmodule/support/signature/contextlib36.py b/sources/shiboken2/shibokenmodule/support/signature/contextlib36.py new file mode 100644 index 000000000..d416eef27 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/contextlib36.py @@ -0,0 +1,472 @@ +# This Python file uses the following encoding: utf-8 +# It has been edited by fix-complaints.py . + +############################################################################# +## +## 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$ +## +############################################################################# + +""" +PSF LICENSE AGREEMENT FOR PYTHON 3.7.0 + +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.7.0 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.7.0 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright © 2001-2018 Python Software Foundation; All Rights + Reserved" are retained in Python 3.7.0 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.7.0 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.7.0. + +4. PSF is making Python 3.7.0 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.7.0 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.0 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.0, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.7.0, Licensee agrees + to be bound by the terms and conditions of this License Agreement. +""" + +"""Utilities for with-statement contexts. See PEP 343.""" +import abc +import sys +import _collections_abc +from collections import deque +from functools import wraps + +__all__ = ["contextmanager", "closing", "AbstractContextManager", + "ContextDecorator", "ExitStack", "redirect_stdout", + "redirect_stderr", "suppress"] + + +class AbstractContextManager(abc.ABC): + + """An abstract base class for context managers.""" + + def __enter__(self): + """Return `self` upon entering the runtime context.""" + return self + + @abc.abstractmethod + def __exit__(self, exc_type, exc_value, traceback): + """Raise any exception triggered within the runtime context.""" + return None + + @classmethod + def __subclasshook__(cls, C): + if cls is AbstractContextManager: + return _collections_abc._check_methods(C, "__enter__", "__exit__") + return NotImplemented + + +class ContextDecorator(object): + "A base class or mixin that enables context managers to work as decorators." + + def _recreate_cm(self): + """Return a recreated instance of self. + + Allows an otherwise one-shot context manager like + _GeneratorContextManager to support use as + a decorator via implicit recreation. + + This is a private interface just for _GeneratorContextManager. + See issue #11647 for details. + """ + return self + + def __call__(self, func): + @wraps(func) + def inner(*args, **kwds): + with self._recreate_cm(): + return func(*args, **kwds) + return inner + + +class _GeneratorContextManager(ContextDecorator, AbstractContextManager): + """Helper for @contextmanager decorator.""" + + def __init__(self, func, args, kwds): + self.gen = func(*args, **kwds) + self.func, self.args, self.kwds = func, args, kwds + # Issue 19330: ensure context manager instances have good docstrings + doc = getattr(func, "__doc__", None) + if doc is None: + doc = type(self).__doc__ + self.__doc__ = doc + # Unfortunately, this still doesn't provide good help output when + # inspecting the created context manager instances, since pydoc + # currently bypasses the instance docstring and shows the docstring + # for the class instead. + # See http://bugs.python.org/issue19404 for more details. + + def _recreate_cm(self): + # _GCM instances are one-shot context managers, so the + # CM must be recreated each time a decorated function is + # called + return self.__class__(self.func, self.args, self.kwds) + + def __enter__(self): + try: + return next(self.gen) + except StopIteration: + raise RuntimeError("generator didn't yield") from None + + def __exit__(self, type, value, traceback): + if type is None: + try: + next(self.gen) + except StopIteration: + return False + else: + raise RuntimeError("generator didn't stop") + else: + if value is None: + # Need to force instantiation so we can reliably + # tell if we get the same exception back + value = type() + try: + self.gen.throw(type, value, traceback) + except StopIteration as exc: + # Suppress StopIteration *unless* it's the same exception that + # was passed to throw(). This prevents a StopIteration + # raised inside the "with" statement from being suppressed. + return exc is not value + except RuntimeError as exc: + # Don't re-raise the passed in exception. (issue27122) + if exc is value: + return False + # Likewise, avoid suppressing if a StopIteration exception + # was passed to throw() and later wrapped into a RuntimeError + # (see PEP 479). + if type is StopIteration and exc.__cause__ is value: + return False + raise + except: + # only re-raise if it's *not* the exception that was + # passed to throw(), because __exit__() must not raise + # an exception unless __exit__() itself failed. But throw() + # has to raise the exception to signal propagation, so this + # fixes the impedance mismatch between the throw() protocol + # and the __exit__() protocol. + # + if sys.exc_info()[1] is value: + return False + raise + raise RuntimeError("generator didn't stop after throw()") + + +def contextmanager(func): + """@contextmanager decorator. + + Typical usage: + + @contextmanager + def some_generator(<arguments>): + <setup> + try: + yield <value> + finally: + <cleanup> + + This makes this: + + with some_generator(<arguments>) as <variable>: + <body> + + equivalent to this: + + <setup> + try: + <variable> = <value> + <body> + finally: + <cleanup> + + """ + @wraps(func) + def helper(*args, **kwds): + return _GeneratorContextManager(func, args, kwds) + return helper + + +class closing(AbstractContextManager): + """Context to automatically close something at the end of a block. + + Code like this: + + with closing(<module>.open(<arguments>)) as f: + <block> + + is equivalent to this: + + f = <module>.open(<arguments>) + try: + <block> + finally: + f.close() + + """ + def __init__(self, thing): + self.thing = thing + def __enter__(self): + return self.thing + def __exit__(self, *exc_info): + self.thing.close() + + +class _RedirectStream(AbstractContextManager): + + _stream = None + + def __init__(self, new_target): + self._new_target = new_target + # We use a list of old targets to make this CM re-entrant + self._old_targets = [] + + def __enter__(self): + self._old_targets.append(getattr(sys, self._stream)) + setattr(sys, self._stream, self._new_target) + return self._new_target + + def __exit__(self, exctype, excinst, exctb): + setattr(sys, self._stream, self._old_targets.pop()) + + +class redirect_stdout(_RedirectStream): + """Context manager for temporarily redirecting stdout to another file. + + # How to send help() to stderr + with redirect_stdout(sys.stderr): + help(dir) + + # How to write help() to a file + with open('help.txt', 'w') as f: + with redirect_stdout(f): + help(pow) + """ + + _stream = "stdout" + + +class redirect_stderr(_RedirectStream): + """Context manager for temporarily redirecting stderr to another file.""" + + _stream = "stderr" + + +class suppress(AbstractContextManager): + """Context manager to suppress specified exceptions + + After the exception is suppressed, execution proceeds with the next + statement following the with statement. + + with suppress(FileNotFoundError): + os.remove(somefile) + # Execution still resumes here if the file was already removed + """ + + def __init__(self, *exceptions): + self._exceptions = exceptions + + def __enter__(self): + pass + + def __exit__(self, exctype, excinst, exctb): + # Unlike isinstance and issubclass, CPython exception handling + # currently only looks at the concrete type hierarchy (ignoring + # the instance and subclass checking hooks). While Guido considers + # that a bug rather than a feature, it's a fairly hard one to fix + # due to various internal implementation details. suppress provides + # the simpler issubclass based semantics, rather than trying to + # exactly reproduce the limitations of the CPython interpreter. + # + # See http://bugs.python.org/issue12029 for more details + return exctype is not None and issubclass(exctype, self._exceptions) + + +# Inspired by discussions on http://bugs.python.org/issue13585 +class ExitStack(AbstractContextManager): + """Context manager for dynamic management of a stack of exit callbacks + + For example: + + with ExitStack() as stack: + files = [stack.enter_context(open(fname)) for fname in filenames] + # All opened files will automatically be closed at the end of + # the with statement, even if attempts to open files later + # in the list raise an exception + + """ + def __init__(self): + self._exit_callbacks = deque() + + def pop_all(self): + """Preserve the context stack by transferring it to a new instance""" + new_stack = type(self)() + new_stack._exit_callbacks = self._exit_callbacks + self._exit_callbacks = deque() + return new_stack + + def _push_cm_exit(self, cm, cm_exit): + """Helper to correctly register callbacks to __exit__ methods""" + def _exit_wrapper(*exc_details): + return cm_exit(cm, *exc_details) + _exit_wrapper.__self__ = cm + self.push(_exit_wrapper) + + def push(self, exit): + """Registers a callback with the standard __exit__ method signature + + Can suppress exceptions the same way __exit__ methods can. + + Also accepts any object with an __exit__ method (registering a call + to the method instead of the object itself) + """ + # We use an unbound method rather than a bound method to follow + # the standard lookup behavior for special methods + _cb_type = type(exit) + try: + exit_method = _cb_type.__exit__ + except AttributeError: + # Not a context manager, so assume its a callable + self._exit_callbacks.append(exit) + else: + self._push_cm_exit(exit, exit_method) + return exit # Allow use as a decorator + + def callback(self, callback, *args, **kwds): + """Registers an arbitrary callback and arguments. + + Cannot suppress exceptions. + """ + def _exit_wrapper(exc_type, exc, tb): + callback(*args, **kwds) + # We changed the signature, so using @wraps is not appropriate, but + # setting __wrapped__ may still help with introspection + _exit_wrapper.__wrapped__ = callback + self.push(_exit_wrapper) + return callback # Allow use as a decorator + + def enter_context(self, cm): + """Enters the supplied context manager + + If successful, also pushes its __exit__ method as a callback and + returns the result of the __enter__ method. + """ + # We look up the special methods on the type to match the with statement + _cm_type = type(cm) + _exit = _cm_type.__exit__ + result = _cm_type.__enter__(cm) + self._push_cm_exit(cm, _exit) + return result + + def close(self): + """Immediately unwind the context stack""" + self.__exit__(None, None, None) + + def __exit__(self, *exc_details): + received_exc = exc_details[0] is not None + + # We manipulate the exception state so it behaves as though + # we were actually nesting multiple with statements + frame_exc = sys.exc_info()[1] + def _fix_exception_context(new_exc, old_exc): + # Context may not be correct, so find the end of the chain + while 1: + exc_context = new_exc.__context__ + if exc_context is old_exc: + # Context is already set correctly (see issue 20317) + return + if exc_context is None or exc_context is frame_exc: + break + new_exc = exc_context + # Change the end of the chain to point to the exception + # we expect it to reference + new_exc.__context__ = old_exc + + # Callbacks are invoked in LIFO order to match the behavior of + # nested context managers + suppressed_exc = False + pending_raise = False + while self._exit_callbacks: + cb = self._exit_callbacks.pop() + try: + if cb(*exc_details): + suppressed_exc = True + pending_raise = False + exc_details = (None, None, None) + except: + new_exc_details = sys.exc_info() + # simulate the stack of exceptions by setting the context + _fix_exception_context(new_exc_details[1], exc_details[1]) + pending_raise = True + exc_details = new_exc_details + if pending_raise: + try: + # bare "raise exc_details[1]" replaces our carefully + # set-up context + fixed_ctx = exc_details[1].__context__ + raise exc_details[1] + except BaseException: + exc_details[1].__context__ = fixed_ctx + raise + return received_exc and suppressed_exc diff --git a/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py b/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py index e078ef1ab..ef18beb67 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py +++ b/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2018 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. @@ -49,7 +49,7 @@ you are changing messages (what I did, of course :-) . import os -patched_modules = "backport_inspect typing27" +patched_modules = "backport_inspect typing27 contextlib36" offending_words = { "behavio""ur": "behavior", diff --git a/sources/shiboken2/shibokenmodule/support/signature/loader.py b/sources/shiboken2/shibokenmodule/support/signature/loader.py index 458759845..749229c3b 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/support/signature/loader.py @@ -158,6 +158,10 @@ with ensure_import_support(): import typing import inspect inspect.formatannotation = formatannotation + if sys.version_info[:2] == (3, 5): + # PYSIDE-953: Use a newer contextlib. + from support.signature import contextlib36 as contextlib + sys.modules["contextlib"] = contextlib else: import inspect namespace = inspect.__dict__ diff --git a/sources/shiboken2/tests/libsmart/smart.cpp b/sources/shiboken2/tests/libsmart/smart.cpp index 4132c2fc8..8d85d67a1 100644 --- a/sources/shiboken2/tests/libsmart/smart.cpp +++ b/sources/shiboken2/tests/libsmart/smart.cpp @@ -62,6 +62,15 @@ SharedPtr<Obj> Obj::giveSharedPtrToObj() return o; } +std::vector<SharedPtr<Obj> > Obj::giveSharedPtrToObjList(int size) +{ + std::vector<SharedPtr<Obj> > r; + for (int i=0; i < size; i++) + r.push_back(giveSharedPtrToObj()); + return r; +} + + SharedPtr<Integer> Obj::giveSharedPtrToInteger() { SharedPtr<Integer> o(new Integer); diff --git a/sources/shiboken2/tests/libsmart/smart.h b/sources/shiboken2/tests/libsmart/smart.h index 502187a11..2e3c96406 100644 --- a/sources/shiboken2/tests/libsmart/smart.h +++ b/sources/shiboken2/tests/libsmart/smart.h @@ -207,6 +207,7 @@ public: void printObj(); Integer takeInteger(Integer val); SharedPtr<Obj> giveSharedPtrToObj(); + std::vector<SharedPtr<Obj> > giveSharedPtrToObjList(int size); SharedPtr<Integer> giveSharedPtrToInteger(); SharedPtr<Smart::Integer2> giveSharedPtrToInteger2(); int takeSharedPtrToObj(SharedPtr<Obj> pObj); diff --git a/sources/shiboken2/tests/smartbinding/smart_pointer_test.py b/sources/shiboken2/tests/smartbinding/smart_pointer_test.py index 62bfc0500..e07856e61 100644 --- a/sources/shiboken2/tests/smartbinding/smart_pointer_test.py +++ b/sources/shiboken2/tests/smartbinding/smart_pointer_test.py @@ -156,5 +156,24 @@ class SmartPointerTests(unittest.TestCase): integer = ptrToInteger.data() self.assertTrue(integer) + def testListOfSmartPointers(self): + # Create the main object + o = Obj() + + # Create a list of shared objects + ptrToObjList = o.giveSharedPtrToObjList(10) + self.assertEqual(len(ptrToObjList), 10) + self.assertEqual(objCount(), 11) + + # Remove one from the list + ptrToObjList.pop() + self.assertEqual(len(ptrToObjList), 9) + self.assertEqual(objCount(), 10) + + # clear and delete all objects in the list + ptrToObjList.clear() + self.assertEqual(len(ptrToObjList), 0) + self.assertEqual(objCount(), 1) + if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken2/tests/smartbinding/typesystem_smart.xml b/sources/shiboken2/tests/smartbinding/typesystem_smart.xml index b2deb18cb..aea1c2f73 100644 --- a/sources/shiboken2/tests/smartbinding/typesystem_smart.xml +++ b/sources/shiboken2/tests/smartbinding/typesystem_smart.xml @@ -5,6 +5,36 @@ <primitive-type name="float" /> <primitive-type name="bool" /> + <template name="cpplist_to_pylist_convertion"> + PyObject *%out = PyList_New(int(%in.size())); + int idx = 0; + for (const auto &cppItem : %in) + PyList_SET_ITEM(%out, idx++, %CONVERTTOPYTHON[%INTYPE_0](cppItem)); + return %out; + </template> + <template name="pyseq_to_cpplist_convertion"> + Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0)); + for (int i = 0, size = PySequence_Fast_GET_SIZE(seq.object()); i < size; ++i) { + PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), i); + %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem); + %out.push_back(cppItem); + } + </template> + <container-type name="std::vector" type="list"> + <include file-name="list" location="global"/> + <conversion-rule> + <native-to-target> + <insert-template name="cpplist_to_pylist_convertion"/> + </native-to-target> + <target-to-native> + <add-conversion type="PySequence"> + <insert-template name="pyseq_to_cpplist_convertion"/> + </add-conversion> + </target-to-native> + </conversion-rule> + </container-type> + + <!-- Used in tests to check what C++ objects are allocated. --> <object-type name="Registry" /> |