aboutsummaryrefslogtreecommitdiffstats
path: root/sources
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2017-09-30 12:43:22 +0200
committerChristian Tismer <tismer@stackless.com>2017-10-27 03:44:44 +0000
commite30e0c161b2b4d50484314bf006e9e5e8ff6b380 (patch)
treee0aa129fdda7a0a580bc200b8b9a1e758230979d /sources
parent8ff047b26dd7e928bd35c519fb9e18db4d1b9a94 (diff)
Support the qApp macro correctly, final version incl. debug
For short the new features: - there is a qApp in QtCore, QtGui and QtWidgets for compatibility, and also in __builtins__ for a true macro-like experience. - if you delete any qApp variable, the Q*Application is reset and you can start over. Long description: There is a qApp macro in Qt5 which is equivalent to Q*Application.instance() . Python does not have macros. Both PyQt5 and PySide2 have an according structure in QtWidgets. In the case of PySide2, the qApp variable is first initialized to None and later to QApplication(). This does not reflect the original sense of the qApp macro, because - it only handles QApplication, - it does not handle destruction. This "macro" should live in QtCore, but both PyQt5 and PySide2 decided to put this in QtWidgets. As a compromize, I propose to put qApp into all three modules, and into __builtins__ as well, so wherever you create an application, you find this "macro" in place. While changing the code, I stumbled over the template set_qapp_parent_for_orphan. I tried to make sense out of it and finally removed it. There were no side effects but bug PYSIDE-85 is gone, now. With some extra effort, I created a singleton qApp that changes itself. This way, a true macro was simulated. Note that this was not possible with a garbage collected variable, and I had to make shiboken aware of this. As the final optimization, I turned qApp also into a fuse variable: Delete any qApp variable and Q*Application will finish when there is no extra reference. Task-number: PYSIDE-85 Task-number: PYSIDE-571 Change-Id: I7a56b19858f63349c98b95778759a6a6de856938 Reviewed-by: Christian Tismer <tismer@stackless.com>
Diffstat (limited to 'sources')
-rw-r--r--sources/pyside2/PySide2/QtCore/glue/qcoreapplication_init.cpp22
-rw-r--r--sources/pyside2/PySide2/QtCore/typesystem_core_common.xml19
-rw-r--r--sources/pyside2/PySide2/QtGui/glue/qguiapplication_init.cpp36
-rw-r--r--sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml3
-rw-r--r--sources/pyside2/PySide2/QtWidgets/glue/qapplication_init.cpp46
-rw-r--r--sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml47
-rw-r--r--sources/pyside2/PySide2/typesystem_templates.xml8
-rw-r--r--sources/pyside2/libpyside/pyside.cpp3
-rw-r--r--sources/pyside2/tests/pysidetest/CMakeLists.txt1
-rw-r--r--sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py76
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.cpp31
-rw-r--r--sources/shiboken2/libshiboken/CMakeLists.txt2
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.cpp46
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.h3
-rw-r--r--sources/shiboken2/libshiboken/qapp_macro.cpp184
-rw-r--r--sources/shiboken2/libshiboken/qapp_macro.h (renamed from sources/pyside2/PySide2/QtWidgets/glue/qtwidgets_qapp.cpp)26
16 files changed, 383 insertions, 170 deletions
diff --git a/sources/pyside2/PySide2/QtCore/glue/qcoreapplication_init.cpp b/sources/pyside2/PySide2/QtCore/glue/qcoreapplication_init.cpp
index fec8cf416..b2dfae38f 100644
--- a/sources/pyside2/PySide2/QtCore/glue/qcoreapplication_init.cpp
+++ b/sources/pyside2/PySide2/QtCore/glue/qcoreapplication_init.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of PySide2.
@@ -37,22 +37,14 @@
**
****************************************************************************/
-// Global variables used to store argc and argv values
-static int QCoreApplicationArgCount;
-static char** QCoreApplicationArgValues;
-
-void QCoreApplication_constructor(PyObject* self, PyObject* argv, QCoreApplicationWrapper** cptr)
+static void QCoreApplicationConstructor(PyObject *self, PyObject *pyargv, QCoreApplicationWrapper **cptr)
{
- if (QCoreApplication::instance()) {
- PyErr_SetString(PyExc_RuntimeError, "A QCoreApplication instance already exists.");
- return;
- }
-
- PyObject *stringlist = PyTuple_GET_ITEM(argv, 0);
- if (Shiboken::listToArgcArgv(stringlist, &QCoreApplicationArgCount, &QCoreApplicationArgValues, "PySideApp")) {
- *cptr = new QCoreApplicationWrapper(QCoreApplicationArgCount, QCoreApplicationArgValues);
+ static int argc;
+ static char **argv;
+ PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+ if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
+ *cptr = new QCoreApplicationWrapper(argc, argv);
Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject*>(self));
PySide::registerCleanupFunction(&PySide::destroyQCoreApplication);
- Py_INCREF(self);
}
}
diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
index 03991af38..41a20a617 100644
--- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
+++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml
@@ -3215,7 +3215,7 @@
</inject-documentation>
<add-function signature="QCoreApplication(QStringList)">
<inject-code>
- QCoreApplication_constructor(%PYSELF, args, &amp;%0);
+ QCoreApplicationConstructor(%PYSELF, args, &amp;%0);
</inject-code>
</add-function>
<!-- blocking functions -->
@@ -3226,23 +3226,24 @@
<modify-function signature="sendPostedEvents(QObject*, int)" allow-thread="yes"/>
<modify-function signature="instance()">
<inject-code class="target">
- QCoreApplication* app = QCoreApplication::instance();
- PyObject* pyApp = Py_None;
+ QCoreApplication *app = QCoreApplication::instance();
+ PyObject *pyApp = Py_None;
if (app) {
- pyApp = reinterpret_cast&lt;PyObject*&gt;(Shiboken::BindingManager::instance().retrieveWrapper(app));
+ pyApp = reinterpret_cast&lt;PyObject*&gt;(
+ Shiboken::BindingManager::instance().retrieveWrapper(app));
if (!pyApp)
- pyApp = %CONVERTTOPYTHON[QCoreApplication*](app); // this will keep app live after python exit (extra ref)
+ pyApp = %CONVERTTOPYTHON[QCoreApplication*](app);
+ // this will keep app live after python exit (extra ref)
}
+ // PYSIDE-571: make sure that we return the singleton "None"
+ if (pyApp == Py_None)
+ Py_DECREF(MakeSingletonQAppWrapper(0)); // here qApp and instance() diverge
%PYARG_0 = pyApp;
Py_XINCREF(%PYARG_0);
</inject-code>
</modify-function>
<modify-function signature="exec()" rename="exec_" allow-thread="yes"/>
- <!-- ### Obsolete
- <modify-function signature="argc()" remove="all"/>
- <modify-function signature="argv()" remove="all"/>
- -->
<modify-function signature="notify(QObject*,QEvent*)" allow-thread="yes">
<modify-argument index="2" invalidate-after-use="yes"/>
</modify-function>
diff --git a/sources/pyside2/PySide2/QtGui/glue/qguiapplication_init.cpp b/sources/pyside2/PySide2/QtGui/glue/qguiapplication_init.cpp
index 014e409b5..38a4c1ccb 100644
--- a/sources/pyside2/PySide2/QtGui/glue/qguiapplication_init.cpp
+++ b/sources/pyside2/PySide2/QtGui/glue/qguiapplication_init.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of PySide2.
@@ -37,34 +37,14 @@
**
****************************************************************************/
-// Borrowed reference to QtGui module
-extern PyObject* moduleQtGui;
-
-static int QGuiApplicationArgCount;
-static char** QGuiApplicationArgValues;
-
-bool QGuiApplicationConstructorStart(PyObject* argv)
-{
- if (QGuiApplication::instance()) {
- PyErr_SetString(PyExc_RuntimeError, "A QGuiApplication instance already exists.");
- return false;
- }
-
- return Shiboken::listToArgcArgv(argv, &QGuiApplicationArgCount, &QGuiApplicationArgValues, "PySideApp");
-}
-
-void QGuiApplicationConstructorEnd(PyObject* self)
-{
- PySide::registerCleanupFunction(&PySide::destroyQCoreApplication);
- Py_INCREF(self);
-}
-
-static void QGuiApplicationConstructor(PyObject* self, PyObject* argv, QGuiApplicationWrapper** cptr)
+static void QGuiApplicationConstructor(PyObject *self, PyObject *pyargv, QGuiApplicationWrapper **cptr)
{
- PyObject *stringlist = PyTuple_GET_ITEM(argv, 0);
- if (QGuiApplicationConstructorStart(stringlist)) {
- *cptr = new QGuiApplicationWrapper(QGuiApplicationArgCount, QGuiApplicationArgValues, 0);
+ static int argc;
+ static char **argv;
+ PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+ if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
+ *cptr = new QGuiApplicationWrapper(argc, argv, 0);
Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject*>(self));
- QGuiApplicationConstructorEnd(self);
+ PySide::registerCleanupFunction(&PySide::destroyQCoreApplication);
}
}
diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
index 0dad0b455..55bc438be 100644
--- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
+++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
@@ -3220,10 +3220,7 @@
<!-- Qt5: not sure if this needs support, skipped for now -->
<rejection class="QWindow" function-name="nativeEvent"/>"
- <!-- Qt5: here the new QGuiApplication and related things -->
<object-type name="QGuiApplication">
- <!-- Qt5: gone <enum-type name="ColorSpec"/> -->
- <!-- Qt5: gone <enum-type name="Type"/> -->
<extra-includes>
<include file-name="QBasicTimer" location="global"/>
<include file-name="QFont" location="global"/>
diff --git a/sources/pyside2/PySide2/QtWidgets/glue/qapplication_init.cpp b/sources/pyside2/PySide2/QtWidgets/glue/qapplication_init.cpp
index f1f1f84a6..1419f5755 100644
--- a/sources/pyside2/PySide2/QtWidgets/glue/qapplication_init.cpp
+++ b/sources/pyside2/PySide2/QtWidgets/glue/qapplication_init.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of PySide2.
@@ -37,44 +37,14 @@
**
****************************************************************************/
-// Borrowed reference to QtWidgets module
-extern PyObject* moduleQtWidgets;
-
-static int QApplicationArgCount;
-static char** QApplicationArgValues;
-static const char QAPP_MACRO[] = "qApp";
-
-bool QApplicationConstructorStart(PyObject* argv)
-{
- if (QApplication::instance()) {
- PyErr_SetString(PyExc_RuntimeError, "A QApplication instance already exists.");
- return false;
- }
-
- return Shiboken::listToArgcArgv(argv, &QApplicationArgCount, &QApplicationArgValues, "PySideApp");
-}
-
-void QApplicationConstructorEnd(PyObject* self)
-{
- // Verify if qApp is in main module
- PyObject* globalsDict = PyEval_GetGlobals();
- if (globalsDict) {
- PyObject* qAppObj = PyDict_GetItemString(globalsDict, QAPP_MACRO);
- if (qAppObj)
- PyDict_SetItemString(globalsDict, QAPP_MACRO, self);
- }
-
- PyObject_SetAttrString(moduleQtWidgets, QAPP_MACRO, self);
- PySide::registerCleanupFunction(&PySide::destroyQCoreApplication);
- Py_INCREF(self);
-}
-
-static void QApplicationConstructor(PyObject* self, PyObject* argv, QApplicationWrapper** cptr)
+static void QApplicationConstructor(PyObject *self, PyObject *pyargv, QApplicationWrapper **cptr)
{
- PyObject *stringlist = PyTuple_GET_ITEM(argv, 0);
- if (QApplicationConstructorStart(stringlist)) {
- *cptr = new QApplicationWrapper(QApplicationArgCount, QApplicationArgValues, 0);
+ static int argc;
+ static char **argv;
+ PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+ if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
+ *cptr = new QApplicationWrapper(argc, argv, 0);
Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject*>(self));
- QApplicationConstructorEnd(self);
+ PySide::registerCleanupFunction(&PySide::destroyQCoreApplication);
}
}
diff --git a/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml b/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml
index 5de077181..9d3e70be2 100644
--- a/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml
+++ b/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml
@@ -3127,11 +3127,6 @@
</modify-function>
</object-type>
- <!-- qApp macro -->
- <inject-code class="native" position="beginning">
- PyObject* moduleQtWidgets;
- </inject-code>
- <inject-code class="target" file="glue/qtwidgets_qapp.cpp" position="end" />
<object-type name="QApplication">
<enum-type name="ColorSpec"/>
<extra-includes>
@@ -3151,48 +3146,6 @@
</add-function>
<modify-function signature="exec()" rename="exec_" allow-thread="yes"/>
<inject-code class="native" file="glue/qapplication_init.cpp" position="beginning" />
-
- <!-- ownership control transfer to qApp -->
- <modify-function signature="setStyle(QStyle*)">
- <inject-code class="target" position="end">
- Shiboken::Object::setParent(%CONVERTTOPYTHON[QApplication*](qApp), %PYARG_1);
- </inject-code>
- </modify-function>
- <modify-function signature="style()">
- <inject-code class="target" position="end">
- <insert-template name="set_qapp_parent_for_orphan"/>
- </inject-code>
- </modify-function>
- <modify-function signature="desktop()">
- <inject-code class="target" position="end">
- <insert-template name="set_qapp_parent_for_orphan"/>
- </inject-code>
- </modify-function>
- <modify-function signature="focusWidget()">
- <inject-code class="target" position="end">
- <insert-template name="set_qapp_parent_for_orphan"/>
- </inject-code>
- </modify-function>
- <modify-function signature="topLevelAt(const QPoint&amp;)">
- <inject-code class="target" position="end">
- <insert-template name="set_qapp_parent_for_orphan"/>
- </inject-code>
- </modify-function>
- <modify-function signature="topLevelAt(int, int)">
- <inject-code class="target" position="end">
- <insert-template name="set_qapp_parent_for_orphan"/>
- </inject-code>
- </modify-function>
- <modify-function signature="widgetAt(const QPoint&amp;)">
- <inject-code class="target" position="end">
- <insert-template name="set_qapp_parent_for_orphan"/>
- </inject-code>
- </modify-function>
- <modify-function signature="widgetAt(int, int)">
- <inject-code class="target" position="end">
- <insert-template name="set_qapp_parent_for_orphan"/>
- </inject-code>
- </modify-function>
</object-type>
<object-type name="QCommandLinkButton"/>
diff --git a/sources/pyside2/PySide2/typesystem_templates.xml b/sources/pyside2/PySide2/typesystem_templates.xml
index d9258ba88..7ac4ac158 100644
--- a/sources/pyside2/PySide2/typesystem_templates.xml
+++ b/sources/pyside2/PySide2/typesystem_templates.xml
@@ -297,13 +297,7 @@
PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG5_TYPE](%5));
</template>
- <template name="set_qapp_parent_for_orphan">
- if (%PYARG_0 &amp;&amp; (%PYARG_0 != Py_None)) {
- SbkObject* _pySelf = reinterpret_cast&lt;SbkObject*&gt;(%PYARG_0);
- if (!Shiboken::Object::hasParentInfo(_pySelf))
- Shiboken::Object::setParent(%CONVERTTOPYTHON[QApplication*](qApp), %PYARG_0);
- }
- </template>
+
<!-- templates for __repr__ -->
<template name="repr_code">
QString format = QString().sprintf("%s(%REPR_FORMAT)", ((PyObject*)%PYSELF)->ob_type->tp_name, %REPR_ARGS);
diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp
index 7d05f45a5..d4e867c61 100644
--- a/sources/pyside2/libpyside/pyside.cpp
+++ b/sources/pyside2/libpyside/pyside.cpp
@@ -50,6 +50,7 @@
#include "dynamicqmetaobject.h"
#include "destroylistener.h"
+#include <qapp_macro.h>
#include <basewrapper.h>
#include <conversions.h>
#include <sbkconverter.h>
@@ -174,6 +175,8 @@ void destroyQCoreApplication()
Py_BEGIN_ALLOW_THREADS
delete app;
Py_END_ALLOW_THREADS
+ // PYSIDE-571: make sure to create a singleton deleted qApp.
+ MakeSingletonQAppWrapper(NULL);
}
struct TypeUserData {
diff --git a/sources/pyside2/tests/pysidetest/CMakeLists.txt b/sources/pyside2/tests/pysidetest/CMakeLists.txt
index d46498f77..821b6f819 100644
--- a/sources/pyside2/tests/pysidetest/CMakeLists.txt
+++ b/sources/pyside2/tests/pysidetest/CMakeLists.txt
@@ -124,3 +124,4 @@ PYSIDE_TEST(mixin_signal_slots_test.py)
PYSIDE_TEST(signal_slot_warning.py)
PYSIDE_TEST(all_modules_load_test.py)
PYSIDE_TEST(signature_test.py)
+PYSIDE_TEST(qapp_like_a_macro_test.py)
diff --git a/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py b/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py
new file mode 100644
index 000000000..6205748ec
--- /dev/null
+++ b/sources/pyside2/tests/pysidetest/qapp_like_a_macro_test.py
@@ -0,0 +1,76 @@
+#############################################################################
+##
+## Copyright (C) 2017 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the test suite of PySide2.
+##
+## $QT_BEGIN_LICENSE:GPL-EXCEPT$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 3 as published by the Free Software
+## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import unittest
+import PySide2
+
+# This test tests the new "macro" feature of qApp.
+# It also uses the qApp variable to finish the instance and start over.
+
+class qAppMacroTest(unittest.TestCase):
+ def test_qApp_is_like_a_macro_and_can_restart(self):
+ from PySide2 import QtCore
+ try:
+ from PySide2 import QtGui, QtWidgets
+ except ImportError:
+ QtWidgets = QtGui = QtCore
+ # qApp is in the builtins
+ qApp
+ # and also in certain PySide modules
+ QtCore.qApp, QtGui.qApp, QtWidgets.qApp
+ # and they are all the same
+ self.assertTrue(qApp is QtCore.qApp is QtGui.qApp is QtWidgets.qApp)
+ # and the type is NoneType, but it is not None (cannot work)
+ self.assertTrue(type(qApp) is type(None))
+ self.assertTrue(qApp is not None)
+ # now we create an application for all cases
+ classes = (QtCore.QCoreApplication,
+ QtGui.QGuiApplication,
+ QtWidgets.QApplication)
+ for klass in classes:
+ print("created", klass([]))
+ del __builtins__.qApp
+ print("deleted qApp")
+ # creating without deletion raises:
+ QtCore.QCoreApplication([])
+ with self.assertRaises(RuntimeError):
+ QtCore.QCoreApplication([])
+ # assigning qApp is obeyed
+ QtCore.qApp = 42
+ del __builtins__.qApp
+ self.assertEqual(QtCore.qApp, 42)
+ self.assertNotEqual(__builtins__, 42)
+ # delete it and it re-appears
+ del QtCore.qApp
+ QtCore.QCoreApplication([])
+ self.assertEqual(QtCore.QCoreApplication.instance(), QtCore.qApp)
+ # and they are again all the same
+ self.assertTrue(qApp is QtCore.qApp is QtGui.qApp is QtWidgets.qApp)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
index 9133b05ab..6cc148140 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
@@ -231,6 +231,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext)
s << "#include <pysideproperty.h>" << endl;
s << "#include <pyside.h>" << endl;
s << "#include <destroylistener.h>" << endl;
+ s << "#include <qapp_macro.h>" << endl;
}
s << "#include <typeresolver.h>" << endl;
@@ -3619,23 +3620,29 @@ void CppGenerator::writeClassDefinition(QTextStream &s,
bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor();
+ const AbstractMetaClass *qCoreApp = AbstractMetaClass::findClass(classes(), QLatin1String("QCoreApplication"));
+ const bool isQApp = qCoreApp != Q_NULLPTR && metaClass->inheritsFrom(qCoreApp);
+
if (metaClass->isNamespace() || metaClass->hasPrivateDestructor()) {
- tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_GC");
+ tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES");
tp_dealloc = metaClass->hasPrivateDestructor() ?
QLatin1String("SbkDeallocWrapperWithPrivateDtor") : QLatin1String("0");
tp_init = QLatin1String("0");
} else {
if (onlyPrivCtor)
- tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_GC");
+ tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES");
else
- tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_GC");
+ tp_flags = QLatin1String("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES");
QString deallocClassName;
if (shouldGenerateCppWrapper(metaClass))
deallocClassName = wrapperName(metaClass);
else
deallocClassName = cppClassName;
- tp_dealloc = QLatin1String("&SbkDeallocWrapper");
+ if (isQApp)
+ tp_dealloc = QLatin1String("&SbkDeallocQAppWrapper");
+ else
+ tp_dealloc = QLatin1String("&SbkDeallocWrapper");
// avoid constFirst to stay Qt 5.5 compatible
tp_init = (onlyPrivCtor || ctors.isEmpty()) ? QLatin1String("0") : cpythonFunctionName(ctors.first());
}
@@ -3652,10 +3659,17 @@ void CppGenerator::writeClassDefinition(QTextStream &s,
tp_setattro = cpythonSetattroFunctionName(metaClass);
}
- if (metaClass->hasPrivateDestructor() || onlyPrivCtor)
+ if (metaClass->hasPrivateDestructor() || onlyPrivCtor) {
tp_new = QLatin1String("0");
- else
+ tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC"));
+ }
+ else if (isQApp) {
+ tp_new = QLatin1String("SbkQAppTpNew"); // PYSIDE-571: need singleton app
+ }
+ else {
tp_new = QLatin1String("SbkObjectTpNew");
+ tp_flags.append(QLatin1String("|Py_TPFLAGS_HAVE_GC"));
+ }
QString tp_richcompare = QString(QLatin1Char('0'));
if (metaClass->hasComparisonOperatorOverload())
@@ -5239,6 +5253,7 @@ bool CppGenerator::finishGeneration()
s << includeQDebug;
s << "#include <pyside.h>" << endl;
s << "#include <signature.h>" << endl;
+ s << "#include <qapp_macro.h>" << endl;
}
s << "#include \"" << getModuleHeaderFileName() << '"' << endl << endl;
@@ -5558,7 +5573,9 @@ bool CppGenerator::finishGeneration()
s << ';' << endl;
// finish the rest of __signature__ initialization.
s << INDENT << "FinishSignatureInitialization(module, " << moduleName()
- << "_SignaturesString);" << endl << endl;
+ << "_SignaturesString);" << endl;
+ // initialize the qApp module.
+ s << INDENT << "NotifyModuleForQApp(module);" << endl << endl;
}
s << "SBK_MODULE_INIT_FUNCTION_END" << endl;
diff --git a/sources/shiboken2/libshiboken/CMakeLists.txt b/sources/shiboken2/libshiboken/CMakeLists.txt
index 3ea7a9f3b..0c954aa07 100644
--- a/sources/shiboken2/libshiboken/CMakeLists.txt
+++ b/sources/shiboken2/libshiboken/CMakeLists.txt
@@ -38,6 +38,7 @@ threadstatesaver.cpp
typeresolver.cpp
shibokenbuffer.cpp
signature.cpp
+qapp_macro.cpp
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
@@ -71,6 +72,7 @@ install(FILES
shibokenbuffer.h
sbkpython.h
signature.h
+ qapp_macro.h
"${CMAKE_CURRENT_BINARY_DIR}/sbkversion.h"
DESTINATION include/shiboken2${shiboken2_SUFFIX})
install(TARGETS libshiboken EXPORT shiboken2
diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp
index 3f3bcc7f5..0d8758cee 100644
--- a/sources/shiboken2/libshiboken/basewrapper.cpp
+++ b/sources/shiboken2/libshiboken/basewrapper.cpp
@@ -52,6 +52,7 @@
#include <algorithm>
#include "threadstatesaver.h"
#include "signature.h"
+#include "qapp_macro.h"
namespace {
void _destroyParentInfo(SbkObject* obj, bool keepReference);
@@ -243,7 +244,9 @@ static void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete)
// be invoked and it trying to delete this object while it is still in
// progress from the first time around, resulting in a double delete and a
// crash.
- PyObject_GC_UnTrack(pyObj);
+ // PYSIDE-571: Some objects do not use GC, so check this!
+ if (PyObject_IS_GC(pyObj))
+ PyObject_GC_UnTrack(pyObj);
// Check that Python is still initialized as sometimes this is called by a static destructor
// after Python interpeter is shutdown.
@@ -278,6 +281,13 @@ void SbkDeallocWrapper(PyObject* pyObj)
SbkDeallocWrapperCommon(pyObj, true);
}
+void SbkDeallocQAppWrapper(PyObject* pyObj)
+{
+ SbkDeallocWrapper(pyObj);
+ // PYSIDE-571: make sure to create a singleton deleted qApp.
+ MakeSingletonQAppWrapper(NULL);
+}
+
void SbkDeallocWrapperWithPrivateDtor(PyObject* self)
{
SbkDeallocWrapperCommon(self, false);
@@ -375,9 +385,8 @@ PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* k
return reinterpret_cast<PyObject*>(newType);
}
-PyObject* SbkObjectTpNew(PyTypeObject* subtype, PyObject*, PyObject*)
+static PyObject *_setupNew(SbkObject *self, PyTypeObject *subtype)
{
- SbkObject* self = PyObject_GC_New(SbkObject, subtype);
Py_INCREF(reinterpret_cast<PyObject*>(subtype));
SbkObjectPrivate* d = new SbkObjectPrivate;
@@ -394,10 +403,35 @@ PyObject* SbkObjectTpNew(PyTypeObject* subtype, PyObject*, PyObject*)
self->ob_dict = 0;
self->weakreflist = 0;
self->d = d;
- PyObject_GC_Track(reinterpret_cast<PyObject*>(self));
return reinterpret_cast<PyObject*>(self);
}
+PyObject* SbkObjectTpNew(PyTypeObject *subtype, PyObject *, PyObject *)
+{
+ SbkObject *self = PyObject_GC_New(SbkObject, subtype);
+ PyObject *res = _setupNew(self, subtype);
+ PyObject_GC_Track(reinterpret_cast<PyObject*>(self));
+ return res;
+}
+
+PyObject* SbkQAppTpNew(PyTypeObject* subtype, PyObject *, PyObject *)
+{
+ // PYSIDE-571:
+ // For qApp, we need to create a singleton Python object.
+ // We cannot track this with the GC, because it is a static variable!
+
+ // Python2 has a weird handling of flags in derived classes that Python3
+ // does not have. Observed with bug_307.py.
+ // But it could theoretically also happen with Python3.
+ // Therefore we enforce that there is no GC flag, ever!
+ if (PyType_HasFeature(subtype, Py_TPFLAGS_HAVE_GC)) {
+ subtype->tp_flags &= ~Py_TPFLAGS_HAVE_GC;
+ subtype->tp_free = PyObject_Del;
+ }
+ SbkObject* self = reinterpret_cast<SbkObject*>(MakeSingletonQAppWrapper(subtype));
+ return self == 0 ? 0 : _setupNew(self, subtype);
+}
+
} //extern "C"
@@ -1372,7 +1406,9 @@ void deallocData(SbkObject* self, bool cleanup)
}
delete self->d; // PYSIDE-205: always delete d.
Py_XDECREF(self->ob_dict);
- Py_TYPE(self)->tp_free(self);
+ // PYSIDE-571: qApp is no longer allocated.
+ if (PyObject_IS_GC((PyObject*)self))
+ Py_TYPE(self)->tp_free(self);
}
void setTypeUserData(SbkObject* wrapper, void* userData, DeleteUserDataFunc d_func)
diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h
index a230c1337..bd2d6820f 100644
--- a/sources/shiboken2/libshiboken/basewrapper.h
+++ b/sources/shiboken2/libshiboken/basewrapper.h
@@ -66,6 +66,7 @@ struct LIBSHIBOKEN_API SbkObject
/// Dealloc the python object \p pyObj and the C++ object represented by it.
LIBSHIBOKEN_API void SbkDeallocWrapper(PyObject* pyObj);
+LIBSHIBOKEN_API void SbkDeallocQAppWrapper(PyObject* pyObj);
LIBSHIBOKEN_API void SbkDeallocWrapperWithPrivateDtor(PyObject* self);
struct SbkObjectType;
@@ -105,6 +106,8 @@ struct LIBSHIBOKEN_API SbkObjectType
};
LIBSHIBOKEN_API PyObject* SbkObjectTpNew(PyTypeObject* subtype, PyObject*, PyObject*);
+// the special case of a switchable singleton
+LIBSHIBOKEN_API PyObject* SbkQAppTpNew(PyTypeObject *subtype, PyObject *args, PyObject *kwds);
} // extern "C"
diff --git a/sources/shiboken2/libshiboken/qapp_macro.cpp b/sources/shiboken2/libshiboken/qapp_macro.cpp
new file mode 100644
index 000000000..b31e98d06
--- /dev/null
+++ b/sources/shiboken2/libshiboken/qapp_macro.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $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$
+**
+****************************************************************************/
+
+#include "basewrapper.h"
+
+extern "C"
+{
+
+#include "qapp_macro.h"
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Support for the qApp macro.
+//
+// qApp is a macro in Qt5. In Python, we simulate that a little by a
+// variable that monitors Q*Application.instance().
+// This variable is also able to destroy the app by deleting qApp.
+//
+static int
+qApp_module_index(PyObject *module)
+{
+ const char *name = PyModule_GetName(module);
+ int ret = 0;
+
+ if (strcmp(name, "PySide2.QtCore") == 0)
+ ret = 1;
+ else if (strcmp(name, "PySide2.QtGui") == 0)
+ ret = 2;
+ else if (strcmp(name, "PySide2.QtWidgets") == 0)
+ ret = 3;
+ return ret;
+}
+
+#define Py_NONE_TYPE Py_TYPE(Py_None)
+
+#if PYTHON_IS_PYTHON3
+# define BRACE_OPEN {
+# define BRACE_CLOSE }
+#else
+# define BRACE_OPEN
+# define BRACE_CLOSE
+#endif
+
+static SbkObject _Py_ChameleonQAppWrapper_Struct = {
+ BRACE_OPEN
+ _PyObject_EXTRA_INIT
+ 1, Py_NONE_TYPE
+ BRACE_CLOSE
+};
+
+static PyObject *qApp_var = NULL;
+static PyObject *qApp_content = (PyObject *)&_Py_ChameleonQAppWrapper_Struct;
+static PyObject *qApp_moduledicts[5] = {0, 0, 0, 0, 0};
+static int qApp_var_ref = 0;
+static int qApp_content_ref = 0;
+
+static int
+reset_qApp_var()
+{
+ PyObject **mod_ptr;
+
+ for (mod_ptr = qApp_moduledicts; *mod_ptr != NULL; mod_ptr++) {
+ // We respect whatever the user may have set.
+ if (PyDict_GetItem(*mod_ptr, qApp_var) == NULL)
+ if (PyDict_SetItem(*mod_ptr, qApp_var, qApp_content) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+
+PyObject *
+MakeSingletonQAppWrapper(PyTypeObject *type)
+{
+ if (type == NULL)
+ type = Py_NONE_TYPE;
+ if (!(type == Py_NONE_TYPE || Py_TYPE(qApp_content) == Py_NONE_TYPE)) {
+ const char *res_name = strrchr(Py_TYPE(qApp_content)->tp_name, '.')+1;
+ const char *type_name = strrchr(type->tp_name, '.')+1;
+ PyErr_Format(PyExc_RuntimeError, "Please destroy the %s singleton before"
+ " creating a new %s instance.", res_name, type_name);
+ return NULL;
+ }
+ if (reset_qApp_var() < 0)
+ return NULL;
+ // always know the max of the refs
+ if (Py_REFCNT(qApp_var) > qApp_var_ref)
+ qApp_var_ref = Py_REFCNT(qApp_var);
+ if (Py_REFCNT(qApp_content) > qApp_content_ref)
+ qApp_content_ref = Py_REFCNT(qApp_content);
+
+ if (Py_TYPE(qApp_content) != Py_NONE_TYPE)
+ Py_REFCNT(qApp_var) = 1; // fuse is armed...
+ if (type == Py_NONE_TYPE) {
+ // Debug mode showed that we need to do more than just remove the
+ // reference. To keep everything in the right order, it is easiest
+ // to do a full shutdown, using QtCore.__moduleShutdown().
+ PyObject *__moduleShutdown = PyDict_GetItemString(qApp_moduledicts[1],
+ "__moduleShutdown");
+ if (__moduleShutdown != NULL) {
+ Py_DECREF(PyObject_CallFunction(__moduleShutdown, (char *)"()"));
+ }
+ // restore the "None-state"
+ Py_TYPE(qApp_content) = Py_NONE_TYPE;
+ Py_REFCNT(qApp_var) = qApp_var_ref;
+ Py_REFCNT(qApp_content) = qApp_content_ref;
+ }
+ else
+ (void)PyObject_INIT(qApp_content, type);
+ Py_INCREF(qApp_content);
+ return qApp_content;
+}
+
+static int
+setup_qApp_var(PyObject *module)
+{
+ int module_index;
+ static int init_done = 0;
+
+ if (!init_done) {
+ qApp_var = Py_BuildValue("s", "qApp");
+ if (qApp_var == NULL)
+ return -1;
+ qApp_moduledicts[0] = PyEval_GetBuiltins();
+ init_done = 1;
+ }
+
+ // Initialize qApp. We insert it into __dict__ for "import *" and also
+ // into __builtins__, to let it appear like a real macro.
+ module_index = qApp_module_index(module);
+ if (module_index) {
+ qApp_moduledicts[module_index] = PyModule_GetDict(module);
+ if (reset_qApp_var() < 0)
+ return -1;
+ }
+ return 0;
+}
+
+void
+NotifyModuleForQApp(PyObject *module)
+{
+ setup_qApp_var(module);
+}
+
+
+} //extern "C"
+
+// end of module
diff --git a/sources/pyside2/PySide2/QtWidgets/glue/qtwidgets_qapp.cpp b/sources/shiboken2/libshiboken/qapp_macro.h
index e041c7680..98f0ec629 100644
--- a/sources/pyside2/PySide2/QtWidgets/glue/qtwidgets_qapp.cpp
+++ b/sources/shiboken2/libshiboken/qapp_macro.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of PySide2.
@@ -37,13 +37,17 @@
**
****************************************************************************/
-// Init qApp macro to None.
-if (qApp) {
- PyObject* pyApp = %CONVERTTOPYTHON[QApplication*](qApp);
- Py_INCREF(pyApp);
- PyModule_AddObject(module, "qApp", pyApp);
-} else {
- Py_INCREF(Py_None);
- PyModule_AddObject(module, "qApp", Py_None);
-}
-moduleQtWidgets = module;
+#ifndef QAPP_MACRO_H
+#define QAPP_MACRO_H
+
+#include "sbkpython.h"
+
+extern "C"
+{
+
+LIBSHIBOKEN_API PyObject *MakeSingletonQAppWrapper(PyTypeObject *type);
+LIBSHIBOKEN_API void NotifyModuleForQApp(PyObject *module);
+
+} // extern "C"
+
+#endif // QAPP_MACRO_H