diff options
51 files changed, 1053 insertions, 229 deletions
diff --git a/build_scripts/main.py b/build_scripts/main.py index 796ea2040..461e5a712 100644 --- a/build_scripts/main.py +++ b/build_scripts/main.py @@ -805,6 +805,8 @@ class PysideBuild(_build, DistUtilsCommandMixin): # Add source location for generating documentation cmake_src_dir = OPTION["QT_SRC"] if OPTION["QT_SRC"] else qt_src_dir cmake_cmd.append("-DQT_SRC_DIR={}".format(cmake_src_dir)) + if OPTION['SKIP_DOCS']: + cmake_cmd.append("-DSKIP_DOCS=yes") log.info("Qt Source dir: {}".format(cmake_src_dir)) if self.build_type.lower() == 'debug': @@ -905,15 +907,18 @@ class PysideBuild(_build, DistUtilsCommandMixin): cmake_cmd.append("-DCMAKE_C_COMPILER=cl.exe") cmake_cmd.append("-DCMAKE_CXX_COMPILER=cl.exe") - if OPTION["DOC_BUILD_ONLINE"]: - log.info("Output format will be HTML") - cmake_cmd.append("-DDOC_OUTPUT_FORMAT=html") - else: - log.info("Output format will be qthelp") - cmake_cmd.append("-DDOC_OUTPUT_FORMAT=qthelp") + if not OPTION["SKIP_DOCS"]: + # Build the whole documentation (rst + API) by default + cmake_cmd.append("-DFULLDOCSBUILD=1") - # Build the whole documentation (rst + API) by default - cmake_cmd.append("-DFULLDOCSBUILD=1") + if OPTION["DOC_BUILD_ONLINE"]: + log.info("Output format will be HTML") + cmake_cmd.append("-DDOC_OUTPUT_FORMAT=html") + else: + log.info("Output format will be qthelp") + cmake_cmd.append("-DDOC_OUTPUT_FORMAT=qthelp") + else: + cmake_cmd.append("-DSKIP_DOCS=1") if not OPTION["SKIP_CMAKE"]: log.info("Configuring module {} ({})...".format(extension, module_src_dir)) @@ -930,6 +935,13 @@ class PysideBuild(_build, DistUtilsCommandMixin): if run_process(cmd_make) != 0: raise DistutilsSetupError("Error compiling {}".format(extension)) + if sys.version_info == (3, 6) and sys.platform == "darwin": + # Python 3.6 has a Sphinx problem because of docutils 0.17 . + # Instead of pinning v0.16, setting the default encoding fixes that. + # Since other platforms are not affected, we restrict this to macOS. + if "UTF-8" not in os.environ.get("LC_ALL", ""): + os.environ["LC_ALL"] = "en_US.UTF-8" + if not OPTION["SKIP_DOCS"]: if extension.lower() == "shiboken2": try: @@ -944,6 +956,7 @@ class PysideBuild(_build, DistUtilsCommandMixin): log.info("Sphinx not found, skipping documentation build") else: log.info("Skipped documentation generation") + cmake_cmd.append("-DSKIP_DOCS=1") if not OPTION["SKIP_MAKE_INSTALL"]: log.info("Installing module {}...".format(extension)) diff --git a/coin/dependencies.yaml b/coin/dependencies.yaml new file mode 100644 index 000000000..c2717bc08 --- /dev/null +++ b/coin/dependencies.yaml @@ -0,0 +1,46 @@ +product_dependency: + ../../qt/tqtc-qt5.git: + ref: "ce5066de40100b04dee7087c7b860693b379c990" +dependency_source: supermodule +dependencies: [ + "../../qt/qt3d", + "../../qt/qtactiveqt", + "../../qt/qtandroidextras", + "../../qt/qtbase", + "../../qt/qtcharts", + "../../qt/qtconnectivity", + "../../qt/qtdatavis3d", + "../../qt/qtdeclarative", + "../../qt/qtdoc", + "../../qt/qtgamepad", + "../../qt/qtgraphicaleffects", + "../../qt/qtimageformats", + "../../qt/qtlocation", + "../../qt/qtlottie", + "../../qt/qtmacextras", + "../../qt/qtmultimedia", + "../../qt/qtnetworkauth", + "../../qt/qtpurchasing", + "../../qt/qtqa", + "../../qt/qtquick3d", + "../../qt/qtquickcontrols2", + "../../qt/qtquicktimeline", + "../../qt/qtremoteobjects", + "../../qt/qtscxml", + "../../qt/qtsensors", + "../../qt/qtserialbus", + "../../qt/qtserialport", + "../../qt/qtspeech", + "../../qt/qtsvg", + "../../qt/qttools", + "../../qt/qttranslations", + "../../qt/qtvirtualkeyboard", + "../../qt/qtwayland", + "../../qt/qtwebchannel", + "../../qt/qtwebengine", + "../../qt/qtwebglplugin", + "../../qt/qtwebsockets", + "../../qt/qtwebview", + "../../qt/qtwinextras", + "../../qt/qtx11extras" + ] diff --git a/dist/changes-5.15.4 b/dist/changes-5.15.4 new file mode 100644 index 000000000..a97be5dc6 --- /dev/null +++ b/dist/changes-5.15.4 @@ -0,0 +1,38 @@ +Qt for Python 5.15.4 is a bug-fix release. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qtforpython/ + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* PySide2 * +**************************************************************************** + + - [PYSIDE-955] QOpenGLContext.versionFunctions() have been implemented. + Class QOpenGLVersionFunctionsFactory (from Qt 6) has also + been added. + - [PYSIDE-1409] signature: The decision heuristics of result tuples have + been refined. + - [PYSIDE-1438] Crashes in Qt Datavisualization's QBar/SurfaceDataProxy + add/set/insertRow() members have been fixed. + - [PYSIDE-1502] Import errors will now be properly reported. + - [PYSIDE-1513] Documentation on properties has been added. + - [PYSIDE-1538] signature: Unrecognized items will no longer raise + exceptions. + - [PYSIDE-1540] The performance of QPainter::drawPoints(QPolygon) has been + improved. + +**************************************************************************** +* Shiboken2 * +**************************************************************************** + + - [PYSIDE-1529] Crashes when registering static fields have been fixed. diff --git a/examples/widgets/richtext/textobject/textobject.py b/examples/widgets/richtext/textobject/textobject.py index b828ea3d0..e82ea95dc 100644 --- a/examples/widgets/richtext/textobject/textobject.py +++ b/examples/widgets/richtext/textobject/textobject.py @@ -42,13 +42,17 @@ """PySide2 port of the widgets/richtext/textobject example from Qt v5.x""" +import os + from PySide2 import QtCore, QtGui, QtWidgets, QtSvg -class SvgTextObject(QtCore.QObject, QtGui.QTextObjectInterface): +class SvgTextObject(QtGui.QPyTextObject): + def __init__(self, parent=None): + super(SvgTextObject, self).__init__(parent) def intrinsicSize(self, doc, posInDocument, format): - renderer = QtSvg.QSvgRenderer(format.property(Window.SvgData).toByteArray()) + renderer = QtSvg.QSvgRenderer(format.property(Window.SvgData)) size = renderer.defaultSize() if size.height() > 25: @@ -57,7 +61,7 @@ class SvgTextObject(QtCore.QObject, QtGui.QTextObjectInterface): return QtCore.QSizeF(size) def drawObject(self, painter, rect, doc, posInDocument, format): - renderer = QtSvg.QSvgRenderer(format.property(Window.SvgData).toByteArray()) + renderer = QtSvg.QSvgRenderer(format.property(Window.SvgData)) renderer.render(painter, rect) @@ -80,8 +84,10 @@ class Window(QtWidgets.QWidget): file = QtCore.QFile(fileName) if not file.open(QtCore.QIODevice.ReadOnly): - QtWidgets.QMessageBox.warning(self, self.tr("Error Opening File"), - self.tr("Could not open '%1'").arg(fileName)) + reason = file.errorString() + message = "Could not open '{}': {}".format(fileName, reason) + QtWidgets.QMessageBox.warning(self, "Error Opening File", + message.arg(fileName)) svgData = file.readAll() @@ -90,7 +96,7 @@ class Window(QtWidgets.QWidget): svgCharFormat.setProperty(Window.SvgData, svgData) cursor = self.textEdit.textCursor() - cursor.insertText(u"\uFFFD", svgCharFormat) + cursor.insertText(chr(0xfffc), svgCharFormat) self.textEdit.setTextCursor(cursor) def setupTextObject(self): @@ -102,8 +108,9 @@ class Window(QtWidgets.QWidget): self.fileNameLineEdit = QtWidgets.QLineEdit() insertTextObjectButton = QtWidgets.QPushButton(self.tr("Insert Image")) - self.fileNameLineEdit.setText('./files/heart.svg') - QtCore.QObject.connect(insertTextObjectButton, QtCore.SIGNAL('clicked()'), self.insertTextObject) + file = os.path.join(os.path.dirname(__file__), 'files', 'heart.svg') + self.fileNameLineEdit.setText(file) + insertTextObjectButton.clicked.connect(self.insertTextObject) bottomLayout = QtWidgets.QHBoxLayout() bottomLayout.addWidget(fileNameLabel) diff --git a/product_dependencies.yaml b/product_dependencies.yaml deleted file mode 100644 index ab9fb943e..000000000 --- a/product_dependencies.yaml +++ /dev/null @@ -1,3 +0,0 @@ -dependencies: - ../../qt/tqtc-qt5.git: - ref: "06ddf5f2dd73cf63ec554a104b7e5e2a56920239" diff --git a/sources/pyside2/CMakeLists.txt b/sources/pyside2/CMakeLists.txt index dc2beded0..4336d6cc4 100644 --- a/sources/pyside2/CMakeLists.txt +++ b/sources/pyside2/CMakeLists.txt @@ -268,7 +268,7 @@ endif () find_program(SPHINX_BUILD sphinx-build) find_program(DOT_EXEC dot) -if (QT_SRC_DIR AND SPHINX_BUILD AND DOT_EXEC) +if (QT_SRC_DIR AND SPHINX_BUILD AND DOT_EXEC AND NOT SKIP_DOCS) add_subdirectory(doc) else () set(DOCS_TARGET_DISABLED_MESSAGE "apidoc generation targets disabled.") diff --git a/sources/pyside2/PySide2/QtDataVisualization/typesystem_datavisualization.xml b/sources/pyside2/PySide2/QtDataVisualization/typesystem_datavisualization.xml index f10aeea3e..5dc87a013 100644 --- a/sources/pyside2/PySide2/QtDataVisualization/typesystem_datavisualization.xml +++ b/sources/pyside2/PySide2/QtDataVisualization/typesystem_datavisualization.xml @@ -119,56 +119,43 @@ <parent index="this" action="add"/> </modify-argument> </modify-function> - <modify-function signature="addRow(QVector<QtDataVisualization::QBarDataItem>*)"> - <modify-argument index="1"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="addRow(QVector<QtDataVisualization::QBarDataItem>*, const QString&)"> - <modify-argument index="1"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="addRows(const QtDataVisualization::QBarDataArray&)"> - <modify-argument index="1"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="addRows(const QtDataVisualization::QBarDataArray&, const QStringList&)"> - <modify-argument index="1"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="insertRow(int, QVector<QtDataVisualization::QBarDataItem>*)"> - <modify-argument index="2"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="insertRow(int, QVector<QtDataVisualization::QBarDataItem>*, const QString&)"> - <modify-argument index="2"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="insertRows(int, const QtDataVisualization::QBarDataArray&)"> - <modify-argument index="2"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="insertRows(int, const QtDataVisualization::QBarDataArray&, const QStringList&)"> - <modify-argument index="2"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="setRow(int, QVector<QtDataVisualization::QBarDataItem>*)"> - <modify-argument index="2"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="setRow(int, QVector<QtDataVisualization::QBarDataItem>*, const QString&)"> - <modify-argument index="2"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> + + <!-- PYSIDE-1438: Replace all add/set/insertRow() taking a 'QVector*' by overloads + taking 'const QVector &' since an allocated list needs to be passed. --> + <modify-function signature="addRow(QVector<QtDataVisualization::QBarDataItem>*)" remove="all"/> + <add-function signature="addRow(const QVector<QtDataVisualization::QBarDataItem>&)" return-type="int"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-addrow"/> + </add-function> + <modify-function signature="addRow(QVector<QtDataVisualization::QBarDataItem>*,const QString&)" remove="all"/> + <add-function signature="addRow(const QVector<QtDataVisualization::QBarDataItem>&,const QString&)" + return-type="int"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-addrow-string"/> + </add-function> + + <modify-function signature="insertRow(int,QVector<QtDataVisualization::QBarDataItem>*)" remove="all"/> + <add-function signature="insertRow(int,const QVector<QtDataVisualization::QBarDataItem>&)"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-insertrow"/> + </add-function> + <modify-function signature="insertRow(int,QVector<QtDataVisualization::QBarDataItem>*,const QString&)" remove="all"/> + <add-function signature="insertRow(int,const QVector<QtDataVisualization::QBarDataItem>&, const QString&)"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-insertrow-string"/> + </add-function> + + <modify-function signature="setRow(int,QVector<QtDataVisualization::QBarDataItem>*)" remove="all"/> + <add-function signature="setRow(int,const QVector<QtDataVisualization::QBarDataItem>&)"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-setrow"/> + </add-function> + <modify-function signature="setRow(int,QVector<QtDataVisualization::QBarDataItem>*,const QString&)" remove="all"/> + <add-function signature="setRow(int,const QVector<QtDataVisualization::QBarDataItem>&,const QString&)"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-setrow-string"/> + </add-function> + <modify-function signature="setRows(int, const QtDataVisualization::QBarDataArray&)"> <modify-argument index="2"> <parent index="this" action="add"/> @@ -182,7 +169,13 @@ </object-type> <object-type name="QCustom3DItem"/> <object-type name="QCustom3DLabel"/> - <object-type name="QCustom3DVolume"/> + <object-type name="QCustom3DVolume"> + <modify-function signature="setTextureData(QVector<uchar>*)" remove="all"/> + <add-function signature="setTextureData(const QVector<uchar>&)"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="qcustom3dvolume-settexturedata"/> + </add-function> + </object-type> <object-type name="QHeightMapSurfaceDataProxy"/> <object-type name="QItemModelBarDataProxy"> <enum-type name="MultiMatchBehavior"/> @@ -251,21 +244,27 @@ </object-type> <value-type name="QSurfaceDataItem"/> <object-type name="QSurfaceDataProxy"> - <modify-function signature="addRow(QVector<QtDataVisualization::QSurfaceDataItem>*)"> - <modify-argument index="1"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="insertRow(int, QVector<QtDataVisualization::QSurfaceDataItem>*)"> - <modify-argument index="2"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> - <modify-function signature="setRow(int, QVector<QtDataVisualization::QSurfaceDataItem>*)"> - <modify-argument index="2"> - <parent index="this" action="add"/> - </modify-argument> - </modify-function> + + <!-- PYSIDE-1438: Replace all add/set/insertRow() taking a 'QVector*' by overloads + taking 'const QVector &' since an allocated list needs to be passed. --> + <modify-function signature="addRow(QVector<QtDataVisualization::QSurfaceDataItem>*)" remove="all"/> + <add-function signature="addRow(const QVector<QtDataVisualization::QSurfaceDataItem>&)" return-type="int"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-addrow"/> + </add-function> + + <modify-function signature="insertRow(int,QVector<QtDataVisualization::QSurfaceDataItem>*)" remove="all"/> + <add-function signature="insertRow(int,const QVector<QtDataVisualization::QSurfaceDataItem>&)"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-insertrow"/> + </add-function> + + <modify-function signature="setRow(int,QVector<QtDataVisualization::QSurfaceDataItem>*)" remove="all"/> + <add-function signature="setRow(int,const QVector<QtDataVisualization::QSurfaceDataItem>&)"> + <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp" + snippet="dataproxy-setrow"/> + </add-function> + <modify-function signature="resetArray(QtDataVisualization::QSurfaceDataArray*)"> <modify-argument index="1"> <parent index="this" action="add"/> diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml index aa8391f8f..b0c61fe64 100644 --- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml +++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml @@ -1871,18 +1871,23 @@ <modify-function signature="drawRects(const QRect*,int)" remove="all"/> <modify-function signature="drawRects(const QRectF*,int)" remove="all"/> <!-- ### --> + <!-- PYSIDE-1540: Preferably use the QPolygon overloads first to avoid + a costly sequence type check on QPolygon. --> <modify-function signature="drawPoints(const QPoint*,int)" remove="all"/> - <add-function signature="drawPoints(QVector<QPoint>)"> + <add-function signature="drawPoints(QVector<QPoint>)" overload-number="2"> <inject-code> <insert-template name="qpainter_drawlist"/> </inject-code> </add-function> <modify-function signature="drawPoints(const QPointF*,int)" remove="all"/> - <add-function signature="drawPoints(QVector<QPointF>)"> + <add-function signature="drawPoints(QVector<QPointF>)" overload-number="3"> <inject-code> <insert-template name="qpainter_drawlist"/> </inject-code> </add-function> + <modify-function signature="drawPoints(const QPolygon&)" overload-number="0"/> + <modify-function signature="drawPoints(const QPolygonF&)" overload-number="1"/> + <modify-function signature="drawPolygon(const QPoint*,int,Qt::FillRule)" remove="all"/> <add-function signature="drawPolygon(QVector<QPoint>,Qt::FillRule)"> <inject-code file="../glue/qtgui.cpp" snippet="qpainter-drawpolygon"/> @@ -2601,6 +2606,10 @@ </object-type> <object-type name="QOpenGLContext"> <enum-type name="OpenGLModuleType"/> + <modify-function signature="versionFunctions(const QOpenGLVersionProfile&) const"> + <inject-code class="target" position="beginning" file="../glue/qtgui.cpp" + snippet="qopenglcontext-versionfunctions"/> + </modify-function> </object-type> <object-type name="QOpenGLContextGroup" since="5.0"/> <object-type name="QOpenGLDebugLogger" since="5.1"> @@ -2835,7 +2844,8 @@ <modify-argument index="return"> <replace-type modified-type="QString"/> </modify-argument> - <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qstring-return"/> + <inject-code class="target" position="end" file="../glue/qtgui.cpp" + snippet="glgetstring-return"/> </modify-function> </object-type> <object-type name="QOpenGLFunctions" since="5.0"> diff --git a/sources/pyside2/PySide2/QtOpenGLFunctions/CMakeLists.txt b/sources/pyside2/PySide2/QtOpenGLFunctions/CMakeLists.txt index 383afb68f..62ed669cc 100644 --- a/sources/pyside2/PySide2/QtOpenGLFunctions/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtOpenGLFunctions/CMakeLists.txt @@ -42,7 +42,8 @@ else() ${QtOpenGLFunctions_GEN_DIR}/qopenglfunctions_4_4_compatibility_wrapper.cpp ${QtOpenGLFunctions_GEN_DIR}/qopenglfunctions_4_4_core_wrapper.cpp ${QtOpenGLFunctions_GEN_DIR}/qopenglfunctions_4_5_compatibility_wrapper.cpp - ${QtOpenGLFunctions_GEN_DIR}/qopenglfunctions_4_5_core_wrapper.cpp) + ${QtOpenGLFunctions_GEN_DIR}/qopenglfunctions_4_5_core_wrapper.cpp + ${QtOpenGLFunctions_GEN_DIR}/qopenglversionfunctionsfactory_wrapper.cpp) message(STATUS "Qt${QT_MAJOR_VERSION}OpenGLFunctions: Adding Desktop OpenGL classes") endif() @@ -71,3 +72,6 @@ create_pyside_module(NAME QtOpenGLFunctions SOURCES QtOpenGLFunctions_SRC TYPESYSTEM_NAME ${QtOpenGLFunctions_BINARY_DIR}/typesystem_openglfunctions.xml DROPPED_ENTRIES QtOpenGLFunctions_DROPPED_ENTRIES) + +install(FILES ${pyside2_SOURCE_DIR}/qopenglversionfunctionsfactory.h + DESTINATION include/PySide2/QtOpenGLFunctions) diff --git a/sources/pyside2/PySide2/QtOpenGLFunctions/QtOpenGLFunctions_global.post.h.in b/sources/pyside2/PySide2/QtOpenGLFunctions/QtOpenGLFunctions_global.post.h.in index 6c8c77087..e43bc2b81 100644 --- a/sources/pyside2/PySide2/QtOpenGLFunctions/QtOpenGLFunctions_global.post.h.in +++ b/sources/pyside2/PySide2/QtOpenGLFunctions/QtOpenGLFunctions_global.post.h.in @@ -59,3 +59,5 @@ #if QT_CONFIG(opengles2) # include <QtGui/qopenglfunctions_es2.h> #endif + +#include "qopenglversionfunctionsfactory.h" // PySide class diff --git a/sources/pyside2/PySide2/QtOpenGLFunctions/typesystem_openglfunctions.xml b/sources/pyside2/PySide2/QtOpenGLFunctions/typesystem_openglfunctions.xml index 1d5b08edb..edb271362 100644 --- a/sources/pyside2/PySide2/QtOpenGLFunctions/typesystem_openglfunctions.xml +++ b/sources/pyside2/PySide2/QtOpenGLFunctions/typesystem_openglfunctions.xml @@ -404,6 +404,15 @@ &openglfunctions_modifications1_0; &openglfunctions_modifications1_1; </object-type> + + <object-type name="QOpenGLVersionFunctionsFactory"> + <modify-function signature="get(const QOpenGLVersionProfile&,QOpenGLContext*)" + return-type="QAbstractOpenGLFunctions*"> + <inject-code class="target" position="beginning" file="../glue/qtopenglfunctions.cpp" + snippet="qopenglversionfunctionsfactory-get"/> + </modify-function> + </object-type> + <suppress-warning text="^There's no user provided way.*QOpenGLFunctions_\d_\d.*::glIndex.*$"/> </typesystem> diff --git a/sources/pyside2/PySide2/glue/qtdatavisualization.cpp b/sources/pyside2/PySide2/glue/qtdatavisualization.cpp index 119d79a40..ce5aa6932 100644 --- a/sources/pyside2/PySide2/glue/qtdatavisualization.cpp +++ b/sources/pyside2/PySide2/glue/qtdatavisualization.cpp @@ -40,3 +40,40 @@ // @snippet releaseownership Shiboken::Object::releaseOwnership(%PYARG_1); // @snippet releaseownership + +// @snippet qcustom3dvolume-settexturedata +using VectorType = decltype(%1); +%CPPSELF.setTextureData(new VectorType(%1)); +// @snippet qcustom3dvolume-settexturedata + +// @snippet dataproxy-addrow +using VectorType = decltype(%1); +%RETURN_TYPE %0 = %CPPSELF.addRow(new VectorType(%1)); +%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); +// @snippet dataproxy-addrow + +// @snippet dataproxy-addrow-string +using VectorType = decltype(%1); +%RETURN_TYPE %0 = %CPPSELF.addRow(new VectorType(%1), %2); +%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); +// @snippet dataproxy-addrow-string + +// @snippet dataproxy-insertrow +using VectorType = decltype(%2); +%CPPSELF.insertRow(%1, new VectorType(%2)); +// @snippet dataproxy-insertrow + +// @snippet dataproxy-insertrow-string +using VectorType = decltype(%2); +%CPPSELF.insertRow(%1, new VectorType(%2), %3); +// @snippet dataproxy-insertrow-string + +// @snippet dataproxy-setrow +using VectorType = decltype(%2); +%CPPSELF.setRow(%1, new VectorType(%2)); +// @snippet dataproxy-setrow + +// @snippet dataproxy-setrow-string +using VectorType = decltype(%2); +%CPPSELF.setRow(%1, new VectorType(%2), %3); +// @snippet dataproxy-setrow-string diff --git a/sources/pyside2/PySide2/glue/qtgui.cpp b/sources/pyside2/PySide2/glue/qtgui.cpp index e802a9f59..1c74c73d2 100644 --- a/sources/pyside2/PySide2/glue/qtgui.cpp +++ b/sources/pyside2/PySide2/glue/qtgui.cpp @@ -41,6 +41,27 @@ * INJECT CODE ********************************************************************/ +// @snippet qopenglcontext-versionfunctions + +// %CPPSELF.%FUNCTION_NAME(%1, %2); Pretend to shiboken we call the function + +// Import QtOpenGLFunctions and call the factory function +// QOpenGLVersionFunctionsFactory.get() +PyObject *module = PyImport_ImportModule("PySide2.QtOpenGLFunctions"); +if (module == nullptr) + return nullptr; +PyObject *loc = PyModule_GetDict(module); +static PyObject *const factoryName = + Shiboken::String::createStaticString("QOpenGLVersionFunctionsFactory"); +auto factory = PyDict_GetItem(loc, factoryName); +if (factory == nullptr) + return nullptr; + +static PyObject *const getMethod = Shiboken::String::createStaticString("get"); +%PYARG_0 = PyObject_CallMethodObjArgs(factory, getMethod, pyArgs[0], %PYSELF, + nullptr); +// @snippet qopenglcontext-versionfunctions + // @snippet glgetshadersource GLsizei bufSize = 4096; GLsizei length = bufSize - 1; @@ -63,6 +84,10 @@ const char *sources[] = {buffer.constData()}; %CPPSELF->%FUNCTION_NAME(%1, 1, sources, nullptr); // @snippet glshadersource +// @snippet glgetstring-return +%PYARG_0 = %CONVERTTOPYTHON[const char *](%0); +// @snippet glgetstring-return + // @snippet qtransform-quadtoquad QTransform _result; if (QTransform::quadToQuad(%1, %2, _result)) { diff --git a/sources/pyside2/PySide2/glue/qtopenglfunctions.cpp b/sources/pyside2/PySide2/glue/qtopenglfunctions.cpp new file mode 100644 index 000000000..c839ae44b --- /dev/null +++ b/sources/pyside2/PySide2/glue/qtopenglfunctions.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:COMM$ +** +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// @snippet qopenglversionfunctionsfactory-get +QAbstractOpenGLFunctions *af = %CPPSELF.%FUNCTION_NAME(%1, %2); +if (auto *f = dynamic_cast<QOpenGLFunctions_4_5_Core *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_5_Core *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_4_5_Compatibility *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_5_Compatibility *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_4_4_Core *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_4_Core *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_4_4_Compatibility *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_4_Compatibility *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_4_3_Core *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_3_Core *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_4_2_Core *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_2_Core *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_4_1_Core *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_1_Core *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_4_0_Core *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_0_Core *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_4_0_Compatibility *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_4_0_Compatibility *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_3_3_Core *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_3_3_Core *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_3_3_Compatibility *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_3_3_Compatibility *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_3_2_Core *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_3_2_Core *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_3_2_Compatibility *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_3_2_Compatibility *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_3_1 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_3_1 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_3_0 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_3_0 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_2_1 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_2_1 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_2_0 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_2_0 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_1_5 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_1_5 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_1_4 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_1_4 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_1_3 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_1_3 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_1_2 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_1_2 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_1_1 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_1_1 *](f); +} else if (auto *f = dynamic_cast<QOpenGLFunctions_1_0 *>(af)) { + %PYARG_0 = %CONVERTTOPYTHON[QOpenGLFunctions_1_0 *](f); +} else { + QString message; + QDebug(&message) << "No OpenGL functions could be obtained for" << %1; + PyErr_SetString(PyExc_RuntimeError, message.toUtf8().constData()); + %PYARG_0 = Py_None; +} +// @snippet qopenglversionfunctionsfactory-get diff --git a/sources/pyside2/PySide2/qopenglversionfunctionsfactory.h b/sources/pyside2/PySide2/qopenglversionfunctionsfactory.h new file mode 100644 index 000000000..27b8373fa --- /dev/null +++ b/sources/pyside2/PySide2/qopenglversionfunctionsfactory.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:COMM$ +** +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLVERSIONFUNCTIONSFACTORY_H +#define QOPENGLVERSIONFUNCTIONSFACTORY_H + +#include <QtGui/QOpenGLContext> + +QT_BEGIN_NAMESPACE + +class QOpenGLVersionProfile; + +class QOpenGLVersionFunctionsFactory +{ +public: + static QAbstractOpenGLFunctions *get(const QOpenGLVersionProfile &versionProfile, + QOpenGLContext *context) + { + return context + ? context->versionFunctions(versionProfile) + : nullptr; + } +}; + +QT_END_NAMESPACE + +#endif // QOPENGLVERSIONFUNCTIONSFACTORY_H diff --git a/sources/pyside2/PySide2/templates/gui_common.xml b/sources/pyside2/PySide2/templates/gui_common.xml index a139a5fe9..16116877f 100644 --- a/sources/pyside2/PySide2/templates/gui_common.xml +++ b/sources/pyside2/PySide2/templates/gui_common.xml @@ -80,8 +80,8 @@ </template> <template name="qimage_buffer_constructor"> - auto ptr = reinterpret_cast<uchar*>(Shiboken::Buffer::getPointer(%PYARG_1)); - %0 = new %TYPE(ptr, %ARGS); + auto *ptr = reinterpret_cast<uchar *>(Shiboken::Buffer::copyData(%PYARG_1)); + %0 = new %TYPE(ptr, %ARGS, std::free); </template> <template name="qcolor_repr"> diff --git a/sources/pyside2/doc/extras/QtCore.Property.rst b/sources/pyside2/doc/extras/QtCore.Property.rst index 8aa629f6b..6bcfc2655 100644 --- a/sources/pyside2/doc/extras/QtCore.Property.rst +++ b/sources/pyside2/doc/extras/QtCore.Property.rst @@ -9,7 +9,9 @@ Detailed Description The Property function lets you declare properties that behave both as Qt and Python properties, and have their -setters and getters defined as Python functions. +getters and setters defined as Python functions. + +They are equivalent to the ``Q_PROPERTY`` macro in the `Qt Docs`_. Here is an example that illustrates how to use this function: @@ -20,14 +22,14 @@ function: from PySide2.QtCore import QObject, Property class MyObject(QObject): - def __init__(self,startval=42): + def __init__(self, startval=42): QObject.__init__(self) self.ppval = startval def readPP(self): return self.ppval - def setPP(self,val): + def setPP(self, val): self.ppval = val pp = Property(int, readPP, setPP) @@ -36,6 +38,68 @@ function: obj.pp = 47 print(obj.pp) +The full options for ``QtCore.Property`` can be found with ``QtCore.Property.__doc__``: + +.. code-block:: + + Property(self, type: type, + fget: Optional[Callable] = None, + fset: Optional[Callable] = None, + freset: Optional[Callable] = None, + fdel: Optional[Callable] = None, + doc: str = '', notify: Optional[Callable] = None, + designable: bool = True, scriptable: bool = True, + stored: bool = True, user: bool = False, + constant: bool = False, final: bool = False) -> PySide2.QtCore.Property + +Normally, only ``type``, ``fget``and ``fset`` are used. + + +Properties compared with Python properties +------------------------------------------ + +``Python`` has property objects very similar to ``QtCore.Property``. +Despite the fact that the latter has an extra ``freset`` function, the usage +of properties is almost the same. The main difference is that ``QtCore.Property`` +requires a ``type`` parameter. + +In the above example, the following lines would be equivalent properties: + +.. code-block:: + + pp = QtCore.Property(int, readPP, setPP) # PySide version + pp = property(readPP, setPP) # Python version + +As you know from the `Python Docs`_, ``Python`` allows to break the property +creation into multiple steps, using the decorator syntax. We can do this in +``PySide`` as well: + +.. code-block:: + :linenos: + + from PySide2.QtCore import QObject, Property + + class MyObject(QObject): + def __init__(self, startval=42): + QObject.__init__(self) + self.ppval = startval + + @Property(int) + def pp(self): + return self.ppval + + @pp.setter + def pp(self, val): + self.ppval = val + + obj = MyObject() + obj.pp = 47 + print(obj.pp) + +Please be careful here: The two ``Python`` functions have the same name, intentionally. +This is needed to let ``Python`` know that these functions belong to the same property. + + Properties in QML expressions ----------------------------- @@ -61,3 +125,6 @@ example illustrating how to do this: pass name = Property(str, _name, notify=name_changed) + +.. _`Python Docs`: https://docs.python.org/3/library/functions.html?highlight=property#property +.. _`Qt Docs`: https://doc.qt.io/qt-5/properties.html diff --git a/sources/pyside2/doc/quickstart.rst b/sources/pyside2/doc/quickstart.rst index 87471a8a8..db2e93280 100644 --- a/sources/pyside2/doc/quickstart.rst +++ b/sources/pyside2/doc/quickstart.rst @@ -52,12 +52,6 @@ constructs to print version information:: # Prints the Qt version used to compile PySide2 print(PySide2.QtCore.__version__) -.. note:: - - As it happened in 5.14.2, PySide had a couple of new releases to fix - issues in 5.14.2, adding yet another version level. In that case, you - will have different versions being shown for Qt and PySide. - Create a Simple Application --------------------------- diff --git a/sources/pyside2/pyside_version.py b/sources/pyside2/pyside_version.py index 0985e2559..ecc975ddf 100644 --- a/sources/pyside2/pyside_version.py +++ b/sources/pyside2/pyside_version.py @@ -39,7 +39,7 @@ major_version = "5" minor_version = "15" -patch_version = "3" +patch_version = "4" # For example: "a", "b", "rc" # (which means "alpha", "beta", "release candidate"). diff --git a/sources/pyside2/tests/QtCore/bug_686.py b/sources/pyside2/tests/QtCore/bug_686.py index 6e4f8994a..d944cafe8 100644 --- a/sources/pyside2/tests/QtCore/bug_686.py +++ b/sources/pyside2/tests/QtCore/bug_686.py @@ -49,6 +49,7 @@ class MyWriteThread(QThread): self.started = True while not self.lock.tryLockForWrite(): pass + self.lock.unlock() self.canQuit = True class MyReadThread(QThread): @@ -62,6 +63,7 @@ class MyReadThread(QThread): self.started = True while not self.lock.tryLockForRead(): pass + self.lock.unlock() self.canQuit = True class MyMutexedThread(QThread): diff --git a/sources/pyside2/tests/QtCore/qsettings_test.py b/sources/pyside2/tests/QtCore/qsettings_test.py index 639f6d276..a9f42a5d5 100644 --- a/sources/pyside2/tests/QtCore/qsettings_test.py +++ b/sources/pyside2/tests/QtCore/qsettings_test.py @@ -38,7 +38,7 @@ init_test_paths(False) from helper.helper import adjust_filename import py3kcompat as py3k -from PySide2.QtCore import QSettings +from PySide2.QtCore import QDir, QSettings, QTemporaryDir class TestQSettings(unittest.TestCase): def testConversions(self): @@ -59,7 +59,11 @@ class TestQSettings(unittest.TestCase): def testDefaultValueConversion(self): - settings = QSettings('foo.ini', QSettings.IniFormat) + temp_dir = QDir.tempPath() + dir = QTemporaryDir('{}/qsettings_XXXXXX'.format(temp_dir)) + self.assertTrue(dir.isValid()) + file_name = dir.filePath('foo.ini') + settings = QSettings(file_name, QSettings.IniFormat) settings.setValue('zero_value', 0) settings.setValue('empty_list', []) settings.setValue('bool1', False) @@ -67,7 +71,7 @@ class TestQSettings(unittest.TestCase): del settings # Loading values already set - settings = QSettings('foo.ini', QSettings.IniFormat) + settings = QSettings(file_name, QSettings.IniFormat) # Getting value that doesn't exist r = settings.value("variable") diff --git a/sources/pyside2/tests/QtDataVisualization/datavisualization_test.py b/sources/pyside2/tests/QtDataVisualization/datavisualization_test.py index af6e5f5d5..32fd432e8 100644 --- a/sources/pyside2/tests/QtDataVisualization/datavisualization_test.py +++ b/sources/pyside2/tests/QtDataVisualization/datavisualization_test.py @@ -88,5 +88,18 @@ class QtDataVisualizationTestCase(UsesQGuiApplication): QTimer.singleShot(500, self.app.quit) self.app.exec_() + def testBarDataProxy(self): + '''PSYSIDE-1438, crashes in QBarDataProxy.addRow()''' + items = [QtDataVisualization.QBarDataItem(v) for v in [1.0, 2.0]] + data_proxy = QtDataVisualization.QBarDataProxy() + data_proxy.addRow(items) + data_proxy.addRow(items, 'bla') + data_proxy.insertRow(0, items) + data_proxy.insertRow(0, items, 'bla') + data_proxy.setRow(0, items) + data_proxy.setRow(0, items, 'bla') + self.assertTrue(data_proxy.rowCount(), 4) + + if __name__ == '__main__': unittest.main() diff --git a/sources/pyside2/tests/QtGui/qdatastream_gui_operators_test.py b/sources/pyside2/tests/QtGui/qdatastream_gui_operators_test.py index c9b5c16a5..4db0a0a6e 100644 --- a/sources/pyside2/tests/QtGui/qdatastream_gui_operators_test.py +++ b/sources/pyside2/tests/QtGui/qdatastream_gui_operators_test.py @@ -47,7 +47,9 @@ class QPixmapQDatastream(UsesQApplication): def setUp(self): super(QPixmapQDatastream, self).setUp() self.source_pixmap = QPixmap(100, 100) - self.source_pixmap.fill(Qt.red) + # PYSIDE-1533: Use Qt.transparent to force Format_ARGB32_Premultiplied + # when converting to QImage in any case. + self.source_pixmap.fill(Qt.transparent) self.output_pixmap = QPixmap() self.buffer = QByteArray() self.read_stream = QDataStream(self.buffer, QIODevice.ReadOnly) @@ -60,8 +62,8 @@ class QPixmapQDatastream(UsesQApplication): image = self.output_pixmap.toImage() pixel = image.pixel(10,10) - self.assertEqual(pixel, QColor(Qt.red).rgba()) - self.assertEqual(self.source_pixmap.toImage(), self.output_pixmap.toImage()) + self.assertEqual(pixel, QColor(Qt.transparent).rgba()) + self.assertEqual(self.source_pixmap.toImage(), image) if __name__ == '__main__': diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 05f9cf203..d7ae45ae3 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -3088,8 +3088,11 @@ AbstractMetaClassList AbstractMetaBuilderPrivate::classesTopologicalSorted(const // Member fields need to be initialized const AbstractMetaFieldList &fields = clazz->fields(); for (AbstractMetaField *field : fields) { - addClassDependency(field->type()->typeEntry(), clazz, classIndex, - map, &graph); + auto typeEntry = field->type()->typeEntry(); + if (typeEntry->isEnum()) // Enum defined in class? + typeEntry = typeEntry->parent(); + if (typeEntry != nullptr) + addClassDependency(typeEntry, clazz, classIndex, map, &graph); } } diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index f1f01e02c..723a13164 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -2123,6 +2123,12 @@ AbstractMetaField *AbstractMetaClass::findField(const QString &name) const return AbstractMetaField::find(m_fields, name); } +bool AbstractMetaClass::hasStaticFields() const +{ + return std::any_of(m_fields.cbegin(), m_fields.cend(), + [](const AbstractMetaField *f) { return f->isStatic(); }); +} + AbstractMetaEnum *AbstractMetaClass::findEnum(const QString &enumName) { if (AbstractMetaEnum *e = findByName(m_enums, enumName)) @@ -2171,6 +2177,11 @@ void AbstractMetaClass::getFunctionsFromInvisibleNamespacesToBeGenerated(Abstrac } } +QString AbstractMetaClass::fullName() const +{ + return package() + QLatin1Char('.') + m_typeEntry->targetLangName(); +} + static void addExtraIncludeForType(AbstractMetaClass *metaClass, const AbstractMetaType *type) { if (!type) @@ -2742,4 +2753,3 @@ QString AbstractMetaEnum::package() const { return m_typeEntry->targetLangPackage(); } - diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index c100c63a1..8a0363f4c 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -84,6 +84,7 @@ public: Format fmt = Documentation::Native); bool isEmpty() const; + bool hasBrief() const { return m_data.contains(Brief); } QString value(Type t = Documentation::Detailed) const; void setValue(const QString& value, Type t = Documentation::Detailed, @@ -1424,6 +1425,8 @@ public: AbstractMetaField *findField(const QString &name) const; + bool hasStaticFields() const; + const AbstractMetaEnumList &enums() const { return m_enums; } void setEnums(const AbstractMetaEnumList &enums) { @@ -1442,10 +1445,7 @@ public: void getFunctionsFromInvisibleNamespacesToBeGenerated(AbstractMetaFunctionList *funcList) const; - QString fullName() const - { - return package() + QLatin1Char('.') + name(); - } + QString fullName() const; /** * Retrieves the class name without any namespace/scope information. diff --git a/sources/shiboken2/ApiExtractor/qtdocparser.cpp b/sources/shiboken2/ApiExtractor/qtdocparser.cpp index d439b3fd5..1aeab85ed 100644 --- a/sources/shiboken2/ApiExtractor/qtdocparser.cpp +++ b/sources/shiboken2/ApiExtractor/qtdocparser.cpp @@ -41,6 +41,9 @@ #include <QtCore/QXmlStreamReader> #include <QUrl> +static inline QString briefStartElement() { return QStringLiteral("<brief>"); } +static inline QString briefEndElement() { return QStringLiteral("</brief>"); } + Documentation QtDocParser::retrieveModuleDocumentation() { return retrieveModuleDocumentation(packageName()); @@ -206,6 +209,25 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, return result; } +// Extract the <brief> section from a WebXML (class) documentation and remove it +// from the source. +static QString extractBrief(QString *value) +{ + const auto briefStart = value->indexOf(briefStartElement()); + if (briefStart < 0) + return {}; + const auto briefEnd = value->indexOf(briefEndElement(), + briefStart + briefStartElement().size()); + if (briefEnd < briefStart) + return {}; + const auto briefLength = briefEnd + briefEndElement().size() - briefStart; + QString briefValue = value->mid(briefStart, briefLength); + briefValue.insert(briefValue.size() - briefEndElement().size(), + QLatin1String("<rst> More_...</rst>")); + value->remove(briefStart, briefLength); + return briefValue; +} + void QtDocParser::fillDocumentation(AbstractMetaClass* metaClass) { if (!metaClass) @@ -257,9 +279,17 @@ void QtDocParser::fillDocumentation(AbstractMetaClass* metaClass) signedModifs.append(docModif); } - Documentation doc(getDocumentation(xquery, query, classModifs)); - if (doc.isEmpty()) - qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotFindDocumentation(sourceFileName, "class", className, query))); + QString docString = getDocumentation(xquery, query, classModifs); + if (docString.isEmpty()) { + qCWarning(lcShibokenDoc, "%s", + qPrintable(msgCannotFindDocumentation(sourceFileName, "class", className, query))); + } + const QString brief = extractBrief(&docString); + + Documentation doc; + if (!brief.isEmpty()) + doc.setValue(brief, Documentation::Brief); + doc.setValue(docString); metaClass->setDocumentation(doc); //Functions Documentation diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp index 6acac41d5..cf3982a18 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp @@ -72,13 +72,14 @@ R"(<typesystem package="Foo"> docParser.setDocumentationDataDirectory(tempDir.path()); docParser.fillDocumentation(classA); - const QString actualDocSimplified = classA->documentation().value().simplified(); + const Documentation &doc = classA->documentation(); + const QString actualDocSimplified = doc.value(Documentation::Detailed).simplified(); + const QString actualBriefSimplified = doc.value(Documentation::Brief).simplified(); QVERIFY(!actualDocSimplified.isEmpty()); const char expectedDoc[] = R"(<?xml version="1.0"?> <description>oi -<brief>Modified Brief</brief> <para>Paragraph number 1</para> <para>Paragraph number 2</para> <para>Some changed contents here</para> @@ -86,7 +87,7 @@ R"(<?xml version="1.0"?> )"; const QString expectedDocSimplified = QString::fromLatin1(expectedDoc).simplified(); // Check whether the first modification worked. - QVERIFY(actualDocSimplified.contains(QLatin1String("Modified Brief"))); + QVERIFY(actualBriefSimplified.contains(QLatin1String("Modified Brief"))); #ifndef HAVE_LIBXSLT // QtXmlPatterns is unable to handle para[3] in style sheets, diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt index 3de5d3223..8b626af09 100644 --- a/sources/shiboken2/CMakeLists.txt +++ b/sources/shiboken2/CMakeLists.txt @@ -197,6 +197,35 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") endif() ###################################################################### +## Define the Python files involved in the build process. +## +## They are installed into the file system (see shibokenmodule) +## and embedded into the libshiboken binary through a .zip file. +###################################################################### + +set(shiboken_python_files + "signature/lib/__init__.py" + "signature/lib/enum_sig.py" + "signature/lib/tool.py" + "signature/__init__.py" + "signature/errorhandler.py" + "signature/importhandler.py" + "signature/layout.py" + "signature/loader.py" + "signature/mapping.py" + "signature/parser.py" + "__init__.py" + "feature.py" + ) + +if (PYTHON_VERSION_MAJOR LESS 3) + list(APPEND shiboken_python_files + "backport_inspect.py" + "typing27.py" + ) +endif() + +###################################################################### # Adding sub directories to build ###################################################################### add_subdirectory(ApiExtractor) diff --git a/sources/shiboken2/doc/typesystem_codeinjection.rst b/sources/shiboken2/doc/typesystem_codeinjection.rst index b0d5f3851..836609508 100644 --- a/sources/shiboken2/doc/typesystem_codeinjection.rst +++ b/sources/shiboken2/doc/typesystem_codeinjection.rst @@ -112,7 +112,8 @@ Below is the example C++ class for whom wrapper code will be generated. .. code-block:: c++ - class InjectCode { + class InjectCode + { public: InjectCode(); double overloadedMethod(int arg); @@ -124,6 +125,10 @@ From the C++ class, |project| will generate a ``injectcode_wrapper.cpp`` file with the binding code. The next section will use a simplified version of the generated wrapper code with the injection spots marked with comments. +There are a number of placeholders indicated by a percent sign ``%``, which +will be expanded when inserting the code. For a list, see +:ref:`typesystemvariables`. + Noteworthy Cases ---------------- @@ -196,7 +201,7 @@ class is polymorphic. int InjectCodeWrapper::virtualMethod(int arg) { - PyObject* method = BindingManager::instance().getOverride(this, "virtualMethod"); + PyObject *method = BindingManager::instance().getOverride(this, "virtualMethod"); if (!py_override) return this->InjectCode::virtualMethod(arg); @@ -228,10 +233,9 @@ own ``beginning`` and ``end`` code injections. .. code-block:: c++ - static PyObject* - PyInjectCode_overloadedMethod(PyObject* self, PyObject* arg) + static PyObject *PyInjectCode_overloadedMethod(PyObject *self, PyObject *arg) { - PyObject* py_result = 0; + PyObject* py_result{}; if (PyFloat_Check(arg)) { double cpp_arg0 = Shiboken::Converter<double >::toCpp(arg); @@ -250,13 +254,13 @@ own ``beginning`` and ``end`` code injections. } else goto PyInjectCode_overloadedMethod_TypeError; if (PyErr_Occurred() || !py_result) - return 0; + return {}; return py_result; PyInjectCode_overloadedMethod_TypeError: PyErr_SetString(PyExc_TypeError, "'overloadedMethod()' called with wrong parameters."); - return 0; + return {}; } @@ -371,7 +375,7 @@ to prevent bad custom code to pass unnoticed. // INJECT-CODE: <typesystem><inject-code class="target" position="beginning"> // Uses: do something before the module is created. - PyObject* module = Py_InitModule("MODULENAME", MODULENAME_methods); + PyObject *module = Py_InitModule("MODULENAME", MODULENAME_methods); (... initialization of wrapped classes, namespaces, functions and enums ...) diff --git a/sources/shiboken2/doc/typesystem_conversionrule.rst b/sources/shiboken2/doc/typesystem_conversionrule.rst index 27e7a72de..fc87a85c9 100644 --- a/sources/shiboken2/doc/typesystem_conversionrule.rst +++ b/sources/shiboken2/doc/typesystem_conversionrule.rst @@ -32,6 +32,10 @@ conversion-rule </conversion-rule> </value-type> + The code can be inserted directly, via ``add-conversion`` (providing snippet + functionality) or via ``insert-template`` (XML template, + see :ref:`using-code-templates`). + The example above show the structure of a complete conversion rule. Each of the child tags comprising the conversion rule are described in their own sections below. diff --git a/sources/shiboken2/doc/typesystem_variables.rst b/sources/shiboken2/doc/typesystem_variables.rst index 73d4dd12c..3d4638253 100644 --- a/sources/shiboken2/doc/typesystem_variables.rst +++ b/sources/shiboken2/doc/typesystem_variables.rst @@ -1,3 +1,5 @@ +.. _typesystemvariables: + ********************* Type System Variables ********************* @@ -24,9 +26,9 @@ Variables .. _arg_number: -**%#** +**%<number>** - Replaced by the name of a C++ argument in the position indicated by ``#``. + Replaced by the name of a C++ argument in the position indicated by ``<number>``. The argument counting starts with ``%1``, since ``%0`` represents the return variable name. If the number indicates a variable that was removed in the type system description, but there is a default value for it, this value will @@ -214,13 +216,13 @@ Variables .. _pyarg: -**%PYARG_#** +**%PYARG_<number>** - Similar to ``%#``, but is replaced by the Python arguments (PyObjects) + Similar to ``%<number>``, but is replaced by the Python arguments (PyObjects) received by the Python wrapper method. If used in the context of a native code injection, i.e. in a virtual method - override, ``%PYARG_#`` will be translated to one item of the Python tuple + override, ``%PYARG_<number>`` will be translated to one item of the Python tuple holding the arguments that should be passed to the Python override for this virtual method. diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp index 5e75cbf87..d330d8c18 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp @@ -1588,30 +1588,6 @@ static void writeInheritedByList(QTextStream& s, const AbstractMetaClass* metaCl s << classes.join(QLatin1String(", ")) << Qt::endl << Qt::endl; } -// Extract the <brief> section from a WebXML (class) documentation and remove it -// from the source. -static bool extractBrief(Documentation *sourceDoc, Documentation *brief) -{ - if (sourceDoc->format() != Documentation::Native) - return false; - QString value = sourceDoc->value(); - const int briefStart = value.indexOf(briefStartElement()); - if (briefStart < 0) - return false; - const int briefEnd = value.indexOf(briefEndElement(), briefStart + briefStartElement().size()); - if (briefEnd < briefStart) - return false; - const int briefLength = briefEnd + briefEndElement().size() - briefStart; - brief->setFormat(Documentation::Native); - QString briefValue = value.mid(briefStart, briefLength); - briefValue.insert(briefValue.size() - briefEndElement().size(), - QLatin1String("<rst> More_...</rst>")); - brief->setValue(briefValue); - value.remove(briefStart, briefLength); - sourceDoc->setValue(value); - return true; -} - void QtDocGenerator::generateClass(QTextStream &s, const GeneratorContext &classContext) { const AbstractMetaClass *metaClass = classContext.metaClass(); @@ -1630,9 +1606,8 @@ void QtDocGenerator::generateClass(QTextStream &s, const GeneratorContext &class s << Pad('*', className.count()) << Qt::endl << Qt::endl; auto documentation = metaClass->documentation(); - Documentation brief; - if (extractBrief(&documentation, &brief)) - writeFormattedText(s, brief.value(), metaClass); + if (documentation.hasBrief()) + writeFormattedText(s, documentation.value(Documentation::Brief), metaClass); s << ".. inheritance-diagram:: " << metaClass->fullName() << Qt::endl << " :parts: 2" << Qt::endl << Qt::endl; @@ -1659,7 +1634,7 @@ void QtDocGenerator::generateClass(QTextStream &s, const GeneratorContext &class writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass, nullptr); if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass, nullptr)) - writeFormattedText(s, documentation.value(), metaClass); + writeFormattedText(s, documentation.value(Documentation::Detailed), metaClass); if (!metaClass->isNamespace()) writeConstructors(s, metaClass); @@ -1972,7 +1947,7 @@ bool QtDocGenerator::writeInjectDocumentation(QTextStream& s, continue; doc.setValue(mod.code(), Documentation::Detailed, fmt); - writeFormattedText(s, doc.value(), cppClass); + writeFormattedText(s, doc, cppClass); didSomething = true; } } diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index c84557180..9739dfef6 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -762,6 +762,9 @@ void CppGenerator::generateClass(QTextStream &s, const GeneratorContext &classCo writeConverterFunctions(s, metaClass, classContext); writeClassRegister(s, metaClass, classContext, signatureStream); + if (metaClass->hasStaticFields()) + writeStaticFieldInitialization(s, metaClass); + // class inject-code native/end if (!metaClass->typeEntry()->codeSnips().isEmpty()) { writeClassCodeSnips(s, metaClass->typeEntry()->codeSnips(), @@ -5281,6 +5284,12 @@ QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *me return initFunctionName; } +QString CppGenerator::getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClass *metaClass) const +{ + return QLatin1String("init_") + getSimpleClassInitFunctionName(metaClass) + + QLatin1String("StaticFields"); +} + QString CppGenerator::getInitFunctionName(const GeneratorContext &context) const { return !context.forSmartPointer() @@ -5483,18 +5492,6 @@ void CppGenerator::writeClassRegister(QTextStream &s, if (metaClass->hasSignals()) writeSignalInitialization(s, metaClass); - // Write static fields - const AbstractMetaFieldList &fields = metaClass->fields(); - for (const AbstractMetaField *field : fields) { - if (!field->isStatic()) - continue; - s << INDENT << QLatin1String("PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(") + cpythonTypeName(metaClass) + QLatin1String(")->tp_dict, \""); - s << field->name() << "\", "; - writeToPythonConversion(s, field->type(), metaClass, metaClass->qualifiedCppName() + QLatin1String("::") + field->name()); - s << ");\n"; - } - s << Qt::endl; - // class inject-code target/end if (!classTypeEntry->codeSnips().isEmpty()) { s << Qt::endl; @@ -5524,6 +5521,29 @@ void CppGenerator::writeClassRegister(QTextStream &s, s << "}\n"; } +void CppGenerator::writeStaticFieldInitialization(QTextStream &s, + const AbstractMetaClass *metaClass) +{ + s << "\nvoid " << getSimpleClassStaticFieldsInitFunctionName(metaClass) + << "()\n{\n" << INDENT << "auto dict = reinterpret_cast<PyTypeObject *>(" + << cpythonTypeName(metaClass) << ")->tp_dict;\n"; + const auto &fields = metaClass->fields(); + for (const AbstractMetaField *field : fields) { + if (field->isStatic()) { + QString cppName = field->originalName(); + if (cppName.isEmpty()) + cppName = field->name(); + const QString name = field->enclosingClass()->qualifiedCppName() + + QLatin1String("::") + cppName; + s << INDENT << "PyDict_SetItemString(dict, \"" << field->name() + << "\",\n" << INDENT << " "; + writeToPythonConversion(s, field->type(), metaClass, name); + s << ");\n"; + } + } + s << "\n}\n"; +} + void CppGenerator::writeInitQtMetaTypeFunctionBody(QTextStream &s, const GeneratorContext &context) const { const AbstractMetaClass *metaClass = context.metaClass(); @@ -5934,11 +5954,18 @@ bool CppGenerator::finishGeneration() } const AbstractMetaClassList lst = classesTopologicalSorted(additionalDependencies); + QVector<const AbstractMetaClass *> classesWithStaticFields; + for (const AbstractMetaClass *cls : lst){ if (shouldGenerate(cls)) { writeInitFunc(s_classInitDecl, s_classPythonDefines, INDENT, getSimpleClassInitFunctionName(cls), cls->typeEntry()->targetLangEnclosingEntry()); + if (cls->hasStaticFields()) { + s_classInitDecl << "void " + << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n"; + classesWithStaticFields.append(cls); + } } } @@ -6240,6 +6267,14 @@ bool CppGenerator::finishGeneration() s << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");\n"; s << INDENT << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");\n"; + // Static fields are registered last since they may use converter functions + // of the previously registered types (PYSIDE-1529). + if (!classesWithStaticFields.isEmpty()) { + s << "\n// Static field initialization\n"; + for (auto cls : qAsConst(classesWithStaticFields)) + s << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n"; + } + s << '\n' << INDENT << "if (PyErr_Occurred()) {\n" << indent(INDENT) << INDENT << "PyErr_Print();\n" << INDENT << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n" diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.h b/sources/shiboken2/generator/shiboken2/cppgenerator.h index 25bb51ef5..64396d61f 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.h +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.h @@ -257,6 +257,7 @@ private: QString getInitFunctionName(const GeneratorContext &context) const; QString getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) const; + QString getSimpleClassStaticFieldsInitFunctionName(const AbstractMetaClass *metaClass) const; void writeSignatureStrings(QTextStream &s, QTextStream &signatureStream, const QString &arrayName, @@ -265,6 +266,8 @@ private: const AbstractMetaClass *metaClass, const GeneratorContext &classContext, QTextStream &signatureStream); + void writeStaticFieldInitialization(QTextStream &s, + const AbstractMetaClass *metaClass); void writeClassDefinition(QTextStream &s, const AbstractMetaClass *metaClass, const GeneratorContext &classContext); diff --git a/sources/shiboken2/libshiboken/CMakeLists.txt b/sources/shiboken2/libshiboken/CMakeLists.txt index 45b41fd13..96effd280 100644 --- a/sources/shiboken2/libshiboken/CMakeLists.txt +++ b/sources/shiboken2/libshiboken/CMakeLists.txt @@ -29,13 +29,35 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sbkversion.h.in" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/embed/signature_bootstrap.py" "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_bootstrap.py" @ONLY) +# Variable from enclosing scope. +# list(TRANSFORM shiboken_python_files +# PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/../shibokenmodule/files.dir/shibokensupport/" +# OUTPUT_VARIABLE embedded_shiboken_files) +# Replacement for CMake version < 3.12: +set(embedded_shiboken_files "") +foreach(item IN LISTS shiboken_python_files) + list(APPEND embedded_shiboken_files + "${CMAKE_CURRENT_SOURCE_DIR}/../shibokenmodule/files.dir/shibokensupport/${item}") +endforeach() + +if (QUIET_BUILD) + set(embedding_option "--quiet") +else() + set(embedding_option "") +endif() + add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_bootstrap_inc.h" OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_inc.h" COMMAND ${PYTHON_EXECUTABLE} -E "${CMAKE_CURRENT_SOURCE_DIR}/embed/embedding_generator.py" --cmake-dir "${CMAKE_CURRENT_BINARY_DIR}/embed" - --limited-api ${PYTHON_LIMITED_API}) + --limited-api ${PYTHON_LIMITED_API} + ${embedding_option} + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/embed/embedding_generator.py" + "${CMAKE_CURRENT_SOURCE_DIR}/embed/signature_bootstrap.py" + ${embedded_shiboken_files} + ) set(libshiboken_MAJOR_VERSION ${shiboken_MAJOR_VERSION}) set(libshiboken_MINOR_VERSION ${shiboken_MINOR_VERSION}) diff --git a/sources/shiboken2/libshiboken/embed/embedding_generator.py b/sources/shiboken2/libshiboken/embed/embedding_generator.py index 15f63649b..a9a58ee07 100644 --- a/sources/shiboken2/libshiboken/embed/embedding_generator.py +++ b/sources/shiboken2/libshiboken/embed/embedding_generator.py @@ -82,7 +82,7 @@ def runpy(cmd, **kw): subprocess.call([sys.executable, '-E'] + cmd.split(), **kw) -def create_zipfile(limited_api): +def create_zipfile(limited_api, quiet): """ Collect all Python files, compile them, create a zip file and make a chunked base64 encoded file from it. @@ -129,11 +129,28 @@ def create_zipfile(limited_api): with open(inc_name, "w") as inc: _embed_file(tmp, inc) tmp.close() + # also generate a simple embeddable .pyc file for signature_bootstrap.pyc boot_name = "signature_bootstrap.py" if limited_api else "signature_bootstrap.pyc" with open(boot_name, "rb") as ldr, open("signature_bootstrap_inc.h", "w") as inc: _embed_bytefile(ldr, inc, limited_api) os.chdir(cur_dir) + if quiet: + return + + # have a look at our populated folder unless quiet option + def list_files(startpath): + for root, dirs, files in os.walk(startpath): + level = root.replace(startpath, '').count(os.sep) + indent = ' ' * 4 * (level) + print('+ {}{}/'.format(indent, os.path.basename(root))) + subindent = ' ' * 4 * (level + 1) + for f in files: + print('+ {}{}'.format(subindent, f)) + + print("++++ Current contents of") + list_files(work_dir) + print("++++") def _embed_file(fin, fout): @@ -236,7 +253,8 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('--cmake-dir', nargs="?") parser.add_argument('--limited-api', type=str2bool) + parser.add_argument('--quiet', action='store_true') args = parser.parse_args() if args.cmake_dir: work_dir = os.path.abspath(args.cmake_dir) - create_zipfile(args.limited_api) + create_zipfile(args.limited_api, args.quiet) diff --git a/sources/shiboken2/libshiboken/sbkarrayconverter.cpp b/sources/shiboken2/libshiboken/sbkarrayconverter.cpp index fd09efdae..b828aa8e0 100644 --- a/sources/shiboken2/libshiboken/sbkarrayconverter.cpp +++ b/sources/shiboken2/libshiboken/sbkarrayconverter.cpp @@ -90,7 +90,6 @@ SbkArrayConverter *createArrayConverter(IsArrayConvertibleToCppFunc toCppCheckFu static PythonToCppFunc unimplementedArrayCheck(PyObject *, int, int) { - warning(PyExc_RuntimeWarning, 0, "SbkConverter: Unimplemented C++ array type."); return nullptr; } diff --git a/sources/shiboken2/libshiboken/shibokenbuffer.cpp b/sources/shiboken2/libshiboken/shibokenbuffer.cpp index dd6e46320..8d44878ac 100644 --- a/sources/shiboken2/libshiboken/shibokenbuffer.cpp +++ b/sources/shiboken2/libshiboken/shibokenbuffer.cpp @@ -74,6 +74,29 @@ void *Shiboken::Buffer::getPointer(PyObject *pyObj, Py_ssize_t *size) return const_cast<void *>(buffer); } +void *Shiboken::Buffer::copyData(PyObject *pyObj, Py_ssize_t *sizeIn) +{ + void *result = nullptr; + Py_ssize_t size = 0; + + Py_buffer view; + if (PyObject_GetBuffer(pyObj, &view, PyBUF_ND) == 0) { + size = view.len; + if (size) { + result = std::malloc(size); + if (result != nullptr) + std::memcpy(result, view.buf, size); + else + size = 0; + } + PyBuffer_Release(&view); + } + + if (sizeIn != nullptr) + *sizeIn = size; + return result; +} + PyObject *Shiboken::Buffer::newObject(void *memory, Py_ssize_t size, Type type) { if (size == 0) diff --git a/sources/shiboken2/libshiboken/shibokenbuffer.h b/sources/shiboken2/libshiboken/shibokenbuffer.h index dc9f8d89f..512d9db4d 100644 --- a/sources/shiboken2/libshiboken/shibokenbuffer.h +++ b/sources/shiboken2/libshiboken/shibokenbuffer.h @@ -79,6 +79,14 @@ namespace Buffer */ LIBSHIBOKEN_API void *getPointer(PyObject *pyObj, Py_ssize_t *size = nullptr); + /** + * Returns a copy of the buffer data which should be free'd. + * + * If the \p pyObj is a non-contiguous buffer a Python error is set. + * nullptr is returned for empty buffers. + */ + LIBSHIBOKEN_API void *copyData(PyObject *pyObj, Py_ssize_t *size = nullptr); + } // namespace Buffer } // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/signature/signature.cpp b/sources/shiboken2/libshiboken/signature/signature.cpp index 4c251af5b..601df4730 100644 --- a/sources/shiboken2/libshiboken/signature/signature.cpp +++ b/sources/shiboken2/libshiboken/signature/signature.cpp @@ -499,7 +499,7 @@ static PyObject *adjustFuncName(const char *func_name) } // Finally, generate the correct path expression. - char _buf[200 + 1] = {}; + char _buf[250 + 1] = {}; if (is_prop) { auto _prop_name = String::toCString(prop_name); if (is_class_prop) diff --git a/sources/shiboken2/shiboken_version.py b/sources/shiboken2/shiboken_version.py index 0985e2559..ecc975ddf 100644 --- a/sources/shiboken2/shiboken_version.py +++ b/sources/shiboken2/shiboken_version.py @@ -39,7 +39,7 @@ major_version = "5" minor_version = "15" -patch_version = "3" +patch_version = "4" # For example: "a", "b", "rc" # (which means "alpha", "beta", "release candidate"). diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index b14de5c9e..9b2b58528 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -42,37 +42,12 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in" "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/__feature__.py" - "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/__feature__.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/__init__.py" - "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/__init__.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/__init__.py" - "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/__init__.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/errorhandler.py" - "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/errorhandler.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/layout.py" - "${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" - "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/signature/parser.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/lib/__init__.py" - "${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" - "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/backport_inspect.py" COPYONLY) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/typing27.py" - "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/typing27.py" COPYONLY) -endif() +# Variable from enclosing scope. +foreach(item IN LISTS shiboken_python_files) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/${item}" + "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/${item}" COPYONLY) +endforeach() + install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/files.dir" DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/__feature__.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/feature.py index ece3d2edb..ece3d2edb 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/__feature__.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/feature.py diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py index 21c284f88..088a93aa4 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py @@ -103,8 +103,6 @@ class ExactEnumerator(object): self.fmt.class_name = None for class_name, klass in members: ret.update(self.klass(class_name, klass)) - if isinstance(klass, EnumMeta): - raise SystemError("implement enum instances at module level") for func_name, func in functions: ret.update(self.function(func_name, func)) return ret diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py index a6c3e420d..a509ecf07 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py @@ -114,7 +114,7 @@ def finish_import(module): import signature_bootstrap -from shibokensupport import signature, __feature__ +from shibokensupport import signature, feature as __feature__ signature.get_signature = signature_bootstrap.get_signature # PYSIDE-1019: Publish the __feature__ dictionary. __feature__.pyside_feature_dict = signature_bootstrap.pyside_feature_dict @@ -195,8 +195,9 @@ def move_into_pyside_package(): try: import PySide2.support except ModuleNotFoundError: - PySide2.support = types.ModuleType("PySide2.support") - put_into_package(PySide2.support, __feature__) + # This can happen in the embedding case. + put_into_package(PySide2, shibokensupport, "support") + put_into_package(PySide2.support, __feature__, "__feature__") put_into_package(PySide2.support, signature) put_into_package(PySide2.support.signature, mapping) put_into_package(PySide2.support.signature, errorhandler) @@ -220,16 +221,18 @@ from shibokensupport.signature.lib import enum_sig if "PySide2" in sys.modules: # We publish everything under "PySide2.support.signature", again. move_into_pyside_package() + # PYSIDE-1502: Make sure that support can be imported. + try: + import PySide2.support + except ModuleNotFoundError as e: + print("PySide2.support could not be imported. " + "This is a serious configuration error.", file=sys.stderr) + raise # PYSIDE-1019: Modify `__import__` to be `__feature__` aware. # __feature__ is already in sys.modules, so this is actually no import - try: - import PySide2.support.__feature__ - sys.modules["__feature__"] = PySide2.support.__feature__ - PySide2.support.__feature__.original_import = __builtins__["__import__"] - __builtins__["__import__"] = PySide2.support.__feature__._import - # Maybe we should optimize that and change `__import__` from C, instead? - except ModuleNotFoundError: - print("__feature__ could not be imported. " - "This is an unsolved PyInstaller problem.", file=sys.stderr) + import PySide2.support.__feature__ + sys.modules["__feature__"] = PySide2.support.__feature__ + PySide2.support.__feature__.original_import = __builtins__["__import__"] + __builtins__["__import__"] = PySide2.support.__feature__._import # end of file diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 4c9f02dc2..92511df32 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -351,6 +351,10 @@ type_map.update({ "self" : "self", }) +# PYSIDE-1538: We need to treat "std::optional" accordingly. +type_map.update({ + "std.optional": typing.Optional, + }) # The Shiboken Part def init_Shiboken(): diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 20c791cc1..a1cb58074 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -196,7 +196,7 @@ def _resolve_value(thing, valtype, line): if res is not None: type_map[thing] = res return res - warnings.warn("""pyside_type_init: + warnings.warn("""pyside_type_init:_resolve_value UNRECOGNIZED: {!r} OFFENDING LINE: {!r} @@ -277,7 +277,15 @@ def _resolve_type(thing, line, level, var_handler): pieces.append(to_string(part)) thing = ", ".join(pieces) result = "{contr}[{thing}]".format(**locals()) - return eval(result, namespace) + # PYSIDE-1538: Make sure that the eval does not crash. + try: + return eval(result, namespace) + except Exception as e: + warnings.warn("""pyside_type_init:_resolve_type + + UNRECOGNIZED: {!r} + OFFENDING LINE: {!r} + """.format(result, line), RuntimeWarning) return _resolve_value(thing, None, line) @@ -380,7 +388,9 @@ def fix_variables(props, line): if not isinstance(ann, ResultVariable): continue # We move the variable to the end and remove it. - retvars.append(ann.type) + # PYSIDE-1409: If the variable was the first arg, we move it to the front. + # XXX This algorithm should probably be replaced by more introspection. + retvars.insert(0 if idx == 0 else len(retvars), ann.type) deletions.append(idx) del annos[name] for idx in reversed(deletions): diff --git a/tools/license_changer.py b/tools/license_changer.py new file mode 100644 index 000000000..0c3443b83 --- /dev/null +++ b/tools/license_changer.py @@ -0,0 +1,85 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python project. +## +## $QT_BEGIN_LICENSE:COMM$ +## +## 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. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from argparse import ArgumentParser, RawTextHelpFormatter +import os +from pathlib import Path +import subprocess +import sys + + +DESC = """ +Tool to adapt licenses to a commercial LTS branch +Requires the qtsdk/tqtc-qtsdk and qtqa repos to be checked out as siblings. +""" + + +REPO_DIR = Path(__file__).resolve().parents[1] + + +EXCLUSIONS = ['/build_scripts/', '/coin/', '/doc/', '/examples/', + '/testing/', '/tests/', + '/coin_build_instructions.py', '/coin_test_instructions.py', + '/ez_setup.py', '/setup.py', '/testrunner.py'] + + +if __name__ == '__main__': + argument_parser = ArgumentParser(description=DESC, + formatter_class=RawTextHelpFormatter) + argument_parser.add_argument('--dry-run', '-d', action='store_true', + help='Dry run, print commands') + options = argument_parser.parse_args() + dry_run = options.dry_run + + license_changer = (REPO_DIR.parent / 'tqtc-qtsdk' / 'packaging-tools' + / 'release_tools' / 'license_changer.pl') + print('Checking ', license_changer) + if not license_changer.is_file: + print('Not found, please clone the qtsdk/tqtc-qtsdk repo') + sys.exit(1) + template = (REPO_DIR.parent / 'qtqa' / 'tests' / 'prebuild' + / 'license' / 'templates' / 'header.COMM') + print('Checking ', template) + if not template.is_file(): + print('Not found, please clone the qtqa repo') + sys.exit(1) + + os.chdir(REPO_DIR) + fixed_cmd = [str(license_changer), '--path', str(REPO_DIR), + '--headerfile', str(template)] + for e in EXCLUSIONS: + fixed_cmd.append('--exclude') + fixed_cmd.append(e) + + for license in ['GPL-EXCEPT', 'GPL', 'LGPL']: + log = f'license_{license.lower()}_log.txt' + cmd = fixed_cmd + cmd.extend(['--replacehdr', license, '--errorlog', log]) + cmds = ' '.join(cmd) + print('Running: ', cmds) + if not dry_run: + ex = subprocess.call(cmd) + if ex != 0: + print('FAIL! ', cmds) + sys.exit(1) + + if not dry_run: + subprocess.call(['git', 'diff']) diff --git a/tools/license_check.py b/tools/license_check.py new file mode 100644 index 000000000..052c41ca5 --- /dev/null +++ b/tools/license_check.py @@ -0,0 +1,70 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python project. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import os +from pathlib import Path +import subprocess +import sys + + +"""Tool to run a license check + +Requires the qtqa repo to be checked out as sibling. +""" + + +REPO_DIR = Path(__file__).resolve().parents[1] + + +if __name__ == '__main__': + license_check = (REPO_DIR.parent / 'qtqa' / 'tests' / 'prebuild' + / 'license' / 'tst_licenses.pl') + print('Checking ', license_check) + if not license_check.is_file(): + print('Not found, please clone the qtqa repo') + sys.exit(1) + + os.environ['QT_MODULE_TO_TEST'] = str(REPO_DIR) + cmd = [str(license_check), '-m', 'pyside-setup'] + cmds = ' '.join(cmd) + print('Running: ', cmds) + ex = subprocess.call(cmd) + if ex != 0: + print('FAIL! ', cmds) + sys.exit(1) diff --git a/tools/uic_test.py b/tools/uic_test.py new file mode 100644 index 000000000..6c1f2b888 --- /dev/null +++ b/tools/uic_test.py @@ -0,0 +1,123 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python project. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import os +import re +import subprocess +import sys +import tempfile +from argparse import ArgumentParser, RawTextHelpFormatter +from pathlib import Path +from textwrap import dedent +from typing import Optional, Tuple + + +VERSION = 2 + + +DESC = """Runs uic on a set of UI files and displays the resulting widgets.""" + + +TEMP_DIR = Path(tempfile.gettempdir()) + + +def get_class_name(file: Path) -> Tuple[Optional[str], Optional[str]]: + """Return class name and widget name of UI file.""" + pattern = re.compile('^\s*<widget class="(\w+)" name="(\w+)"\s*>.*$') + for l in Path(file).read_text().splitlines(): + match = pattern.match(l) + if match: + return (match.group(1), match.group(2)) + return (None, None) + + +def test_file(file: str, uic: bool=False) -> bool: + """Run uic on a UI file and show the resulting UI.""" + path = Path(file) + (klass, name) = get_class_name(path) + if not klass: + print(f'{file} does not appear to be a UI file', file=sys.stderr) + return False + py_klass = f'Ui_{name}' + py_file_basename = py_klass.lower() + py_file = TEMP_DIR / (py_file_basename + '.py') + py_main = TEMP_DIR / 'main.py' + cmd = ['uic', '-g', 'python'] if uic else [f'pyside{VERSION}-uic'] + cmd.extend(['-o', os.fspath(py_file), file]) + try: + subprocess.call(cmd) + except FileNotFoundError as e: + print(str(e) + " (try -u for uic)", file=sys.stderr) + return False + main_source = dedent(f'''\ + import sys + from PySide{VERSION}.QtWidgets import QApplication, {klass} + from {py_file_basename} import {py_klass} + + if __name__ == "__main__": + app = QApplication(sys.argv) + ui = {py_klass}() + widget = {klass}() + ui.setupUi(widget) + widget.show() + sys.exit(app.exec_())''') + py_main.write_text(main_source) + exit_code = subprocess.call([sys.executable, os.fspath(py_main)]) + py_main.unlink() + py_file.unlink() + return exit_code == 0 + + +if __name__ == '__main__': + argument_parser = ArgumentParser(description=DESC, + formatter_class=RawTextHelpFormatter) + argument_parser.add_argument('--uic', '-u', action='store_true', + help='Use uic instead of pyside-uic') + argument_parser.add_argument("files", help="UI Files", + nargs='+', type=str) + options = argument_parser.parse_args() + failed = 0 + count = len(options.files) + for i, file in enumerate(options.files): + print(f'{i+1}/{count} {file}') + if not test_file(file, options.uic): + failed += 1 + if failed != 0: + print(f'{failed}/{count} failed.') + sys.exit(failed) |