aboutsummaryrefslogtreecommitdiffstats
path: root/sources
diff options
context:
space:
mode:
authorAndreas Beckermann <beckermann@monument-software.de>2019-09-12 15:10:46 +0200
committerAndreas Beckermann <beckermann@monument-software.de>2019-10-25 07:37:34 +0200
commite52ebf296e8fcd84fa4055c80a3c489ae1431c9e (patch)
tree106510917095522f07c1f16d57b84f078cbfa9e3 /sources
parentb332456c38241d663d1eef3636129ea82fd00564 (diff)
Add support for __repr__ in QObject derived classes
Currently shiboken generates classes without __repr__ for QObject derived classes. However for all non-QObject classes that have an operator<<() for QDebug, it *does* add a valid repr implementation. Extend this behavior to QObject classes as well. In order for this to become more useful, also check for the indirection of operator<<(): If operator<<(QDebug, Foo*) is available, use the current non-value-type behavior, (i.e. provide cppSelf to operator<<()), but if operator<<(QDebug, const Foo&) is available instead, use the same behavior as for value-types, i.e. provide *cppSelf. This greatly increases the number of classes where operator<<() provides useful results. Also make sure to check for operator<<() in namespaces (recursively), not just at global scope. Change-Id: Ief9158455a25e332f07169f09692cafb8097078b Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'sources')
-rw-r--r--sources/pyside2/tests/QtCore/attr_cache_py3k.py6
-rw-r--r--sources/pyside2/tests/QtWidgets/bug_862.py33
-rw-r--r--sources/pyside2/tests/pysidetest/CMakeLists.txt2
-rw-r--r--sources/pyside2/tests/pysidetest/repr_test.py83
-rw-r--r--sources/pyside2/tests/pysidetest/testobject.cpp22
-rw-r--r--sources/pyside2/tests/pysidetest/testobject.h12
-rw-r--r--sources/pyside2/tests/pysidetest/typesystem_pysidetest.xml7
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp17
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h1
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetalang.h9
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.cpp11
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.h2
12 files changed, 185 insertions, 20 deletions
diff --git a/sources/pyside2/tests/QtCore/attr_cache_py3k.py b/sources/pyside2/tests/QtCore/attr_cache_py3k.py
index ec0575b02..f9761a9d3 100644
--- a/sources/pyside2/tests/QtCore/attr_cache_py3k.py
+++ b/sources/pyside2/tests/QtCore/attr_cache_py3k.py
@@ -56,9 +56,9 @@ class A(QObject):
def test(cls):
cls.instance
cls.instance = cls()
- assert "<__main__.A object " in repr(cls.__dict__['instance'])
- assert "<__main__.A object " in repr(cls.instance)
- assert "<__main__.A object " in repr(type.__getattribute__(cls, 'instance'))
+ assert "<__main__.A(0x" in repr(cls.__dict__['instance'])
+ assert "<__main__.A(0x" in repr(cls.instance)
+ assert "<__main__.A(0x" in repr(type.__getattribute__(cls, 'instance'))
if __name__ == "__main__":
diff --git a/sources/pyside2/tests/QtWidgets/bug_862.py b/sources/pyside2/tests/QtWidgets/bug_862.py
index ac0325536..4621fc3b4 100644
--- a/sources/pyside2/tests/QtWidgets/bug_862.py
+++ b/sources/pyside2/tests/QtWidgets/bug_862.py
@@ -26,6 +26,29 @@
##
#############################################################################
+
+#
+# Test for bug 862, original description was:
+#
+# print seems to be broken at least for QGraphicsItems-derived objects. The
+# attached code shows:
+#
+# <__main__.MyQObject object at 0xf99f38>
+# <__main__.MyQWidget object at 0xf99f38>
+# <PySide.QtGui.MyQGraphicsObject (this = 0x11c0d60 , parent = 0x0 , pos =
+# QPointF(0, 0) , z = 0 , flags = ( ) ) at 0xf99f38>
+# <PySide.QtGui.QGraphicsItem (this = 0x11c2e60 , parent = 0x0 , pos = QPointF(0,
+# 0) , z = 0 , flags = ( ) ) at 0xf99f38>
+#
+# Where it should be showing something like:
+#
+# <__main__.MyQObject object at 0x7f55cf226c20>
+# <__main__.MyQWidget object at 0x7f55cf226c20>
+# <__main__.MyQGraphicsObject object at 0x7f55cf226c20>
+# <__main__.MyQGraphicsItem object at 0x7f55cf226c20>
+#
+
+
from PySide2.QtCore import QObject
from PySide2.QtWidgets import *
import PySide2.QtCore
@@ -53,14 +76,14 @@ class TestRepr (unittest.TestCase):
app = QApplication([])
- self.assertEqual("<__main__.MyQObject object at ", repr(MyQObject())[:30])
- self.assertEqual("<__main__.MyQWidget object at ", repr(MyQWidget())[:30])
+ self.assertEqual("<__main__.MyQObject(0x", repr(MyQObject())[:22])
+ self.assertEqual("<__main__.MyQWidget(0x", repr(MyQWidget())[:22])
self.assertEqual("<__main__.MyQGraphicsObject(0x", repr(MyQGraphicsObject())[:30])
self.assertEqual("<__main__.MyQGraphicsItem(0x", repr(MyQGraphicsItem())[:28])
- self.assertEqual("<PySide2.QtCore.QObject object at ", repr(QObject())[:34])
- self.assertEqual("<PySide2.QtCore.QObject object at ", repr(PySide2.QtCore.QObject())[:34])
- self.assertEqual("<PySide2.QtWidgets.QWidget object at ", repr(QWidget())[:37])
+ self.assertEqual("<PySide2.QtCore.QObject(0x", repr(QObject())[:26])
+ self.assertEqual("<PySide2.QtCore.QObject(0x", repr(PySide2.QtCore.QObject())[:26])
+ self.assertEqual("<PySide2.QtWidgets.QWidget(0x", repr(QWidget())[:29])
self.assertEqual("<PySide2.QtWidgets.QGraphicsWidget(0x", repr(QGraphicsWidget())[:37])
if __name__ == "__main__":
diff --git a/sources/pyside2/tests/pysidetest/CMakeLists.txt b/sources/pyside2/tests/pysidetest/CMakeLists.txt
index 3c993cf4e..b8ec54270 100644
--- a/sources/pyside2/tests/pysidetest/CMakeLists.txt
+++ b/sources/pyside2/tests/pysidetest/CMakeLists.txt
@@ -30,6 +30,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/testbinding/testobject_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/intvalue_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/pysidecpp_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/pysidecpp_testobjectwithnamespace_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/testbinding/pysidecpp_testobject2withnamespace_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/pysidecpp2_testobjectwithoutnamespace_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/testview_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/testbinding_module_wrapper.cpp
@@ -142,3 +143,4 @@ PYSIDE_TEST(signal_slot_warning.py)
PYSIDE_TEST(all_modules_load_test.py)
PYSIDE_TEST(qapp_like_a_macro_test.py)
PYSIDE_TEST(embedding_test.py)
+PYSIDE_TEST(repr_test.py)
diff --git a/sources/pyside2/tests/pysidetest/repr_test.py b/sources/pyside2/tests/pysidetest/repr_test.py
new file mode 100644
index 000000000..295084f17
--- /dev/null
+++ b/sources/pyside2/tests/pysidetest/repr_test.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Copyright (C) 2019 Andreas Beckermann
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qt for Python.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import unittest
+from testbinding import PySideCPP, TestObject
+
+class QObjectDerivedReprTest(unittest.TestCase):
+ """Test the __repr__ implementation of QObject derived classes"""
+
+ def testReprWithoutNamespace(self):
+ """Test that classes outside a namespace that have a operator<<(QDebug,...) defined use that
+ for __repr__"""
+ t = TestObject(123)
+
+ # We don't define __str__, so str(q) should call __repr__
+ self.assertEqual(t.__repr__(), str(t))
+
+ # __repr__ should use the operator<<(QDebug,...) implementation
+ self.assertIn('TestObject(id=123)', str(t))
+
+ def testReprWithNamespace(self):
+ """Test that classes inside a namespace that have a operator<<(QDebug,...) defined use that
+ for __repr__"""
+ t = PySideCPP.TestObjectWithNamespace(None)
+
+ # We don't define __str__, so str(q) should call __repr__
+ self.assertEqual(t.__repr__(), str(t))
+
+ # __repr__ should use the operator<<(QDebug,...) implementation
+ self.assertIn('TestObjectWithNamespace("TestObjectWithNamespace")', str(t))
+
+ def testReprInject(self):
+ """Test that injecting __repr__ via typesystem overrides the operator<<(QDebug, ...)"""
+ t = PySideCPP.TestObject2WithNamespace(None)
+
+ # We don't define __str__, so str(q) should call __repr__
+ self.assertEqual(t.__repr__(), str(t))
+
+ # __repr__ should use the operator<<(QDebug,...) implementation
+ self.assertEqual(str(t), "TestObject2WithNamespace(injected_repr)")
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/sources/pyside2/tests/pysidetest/testobject.cpp b/sources/pyside2/tests/pysidetest/testobject.cpp
index 03a7a965c..441ae872f 100644
--- a/sources/pyside2/tests/pysidetest/testobject.cpp
+++ b/sources/pyside2/tests/pysidetest/testobject.cpp
@@ -52,3 +52,25 @@ void TestObject::emitSignalWithTypedefValue(int value)
{
emit signalWithTypedefValue(TypedefValue(value));
}
+
+QDebug operator<<(QDebug dbg, TestObject& testObject)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "TestObject(id=" << testObject.idValue() << ") ";
+ return dbg;
+}
+
+namespace PySideCPP {
+ QDebug operator<<(QDebug dbg, TestObjectWithNamespace& testObject)
+ {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "TestObjectWithNamespace(" << testObject.name() << ") ";
+ return dbg;
+ }
+ QDebug operator<<(QDebug dbg, TestObject2WithNamespace& testObject)
+ {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "TestObject2WithNamespace(" << testObject.name() << ") ";
+ return dbg;
+ }
+}
diff --git a/sources/pyside2/tests/pysidetest/testobject.h b/sources/pyside2/tests/pysidetest/testobject.h
index 6cfb01101..f8a174d46 100644
--- a/sources/pyside2/tests/pysidetest/testobject.h
+++ b/sources/pyside2/tests/pysidetest/testobject.h
@@ -33,6 +33,7 @@
#include <QApplication>
#include <QMetaType>
#include <QVariant>
+#include <QDebug>
#ifdef pysidetest_EXPORTS
#define PYSIDE_EXPORTS 1
#endif
@@ -81,6 +82,7 @@ private:
int m_idValue;
QList<QObject*> m_children;
};
+PYSIDE_API QDebug operator<<(QDebug dbg, TestObject &testObject);
typedef int PySideInt;
@@ -104,6 +106,16 @@ signals:
void emitSignalWithNamespace(PySideCPP::TestObjectWithNamespace* obj);
void emitSignalWithTypedef(PySideInt val);
};
+PYSIDE_API QDebug operator<<(QDebug dbg, TestObjectWithNamespace &testObject);
+
+class PYSIDE_API TestObject2WithNamespace : public QObject
+{
+ Q_OBJECT
+public:
+ TestObject2WithNamespace(QObject* parent) : QObject(parent) {}
+ QString name() { return "TestObject2WithNamespace"; }
+};
+PYSIDE_API QDebug operator<<(QDebug dbg, TestObject2WithNamespace& testObject);
} // Namespace PySideCPP
diff --git a/sources/pyside2/tests/pysidetest/typesystem_pysidetest.xml b/sources/pyside2/tests/pysidetest/typesystem_pysidetest.xml
index 1904f236f..1e777edd2 100644
--- a/sources/pyside2/tests/pysidetest/typesystem_pysidetest.xml
+++ b/sources/pyside2/tests/pysidetest/typesystem_pysidetest.xml
@@ -37,6 +37,13 @@
<namespace-type name="PySideCPP">
<object-type name="TestObjectWithNamespace"/>
+ <object-type name="TestObject2WithNamespace">
+ <add-function signature="__repr__" return-type="PyObject*">
+ <inject-code class="target" position="beginning">
+ %PYARG_0 = Shiboken::String::fromCString("TestObject2WithNamespace(injected_repr)");
+ </inject-code>
+ </add-function>
+ </object-type>
</namespace-type>
<namespace-type name="PySideCPP2" generate="no">
diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp
index 4b31dbb05..6aafe68be 100644
--- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp
+++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp
@@ -248,6 +248,15 @@ void AbstractMetaBuilderPrivate::registerHashFunction(const FunctionModelItem &f
}
}
+void AbstractMetaBuilderPrivate::registerToStringCapabilityIn(const NamespaceModelItem &nsItem)
+{
+ const FunctionList &streamOps = nsItem->findFunctions(QLatin1String("operator<<"));
+ for (const FunctionModelItem &item : streamOps)
+ registerToStringCapability(item, nullptr);
+ for (const NamespaceModelItem &ni : nsItem->namespaces())
+ registerToStringCapabilityIn(ni);
+}
+
/**
* Check if a class has a debug stream operator that can be used as toString
*/
@@ -261,7 +270,7 @@ void AbstractMetaBuilderPrivate::registerToStringCapability(const FunctionModelI
const ArgumentModelItem &arg = arguments.at(1);
if (AbstractMetaClass *cls = argumentToClass(arg, currentClass)) {
if (arg->type().indirections() < 2)
- cls->setToStringCapability(true);
+ cls->setToStringCapability(true, arg->type().indirections());
}
}
}
@@ -591,11 +600,7 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom)
registerHashFunction(item, nullptr);
}
- {
- const FunctionList &streamOps = dom->findFunctions(QLatin1String("operator<<"));
- for (const FunctionModelItem &item : streamOps)
- registerToStringCapability(item, nullptr);
- }
+ registerToStringCapabilityIn(dom);
{
FunctionList binaryOperators = dom->findFunctions(QStringLiteral("operator=="));
diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h
index fec2eddb9..9b1b181cc 100644
--- a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h
+++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h
@@ -106,6 +106,7 @@ public:
void checkFunctionModifications();
void registerHashFunction(const FunctionModelItem &functionItem,
AbstractMetaClass *currentClass);
+ void registerToStringCapabilityIn(const NamespaceModelItem &namespaceItem);
void registerToStringCapability(const FunctionModelItem &functionItem,
AbstractMetaClass *currentClass);
diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h
index afb4e5fbd..91ca4d7a9 100644
--- a/sources/shiboken2/ApiExtractor/abstractmetalang.h
+++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h
@@ -1691,9 +1691,10 @@ public:
return m_stream;
}
- void setToStringCapability(bool value)
+ void setToStringCapability(bool value, uint indirections = 0)
{
m_hasToStringCapability = value;
+ m_toStringCapabilityIndirections = indirections;
}
bool hasToStringCapability() const
@@ -1701,6 +1702,11 @@ public:
return m_hasToStringCapability;
}
+ uint toStringCapabilityIndirections() const
+ {
+ return m_toStringCapabilityIndirections;
+ }
+
bool deleteInMainThread() const;
static AbstractMetaClass *findClass(const AbstractMetaClassList &classes,
@@ -1753,6 +1759,7 @@ private:
// FunctionModelItem m_qDebugStreamFunction;
bool m_stream = false;
+ uint m_toStringCapabilityIndirections = 0;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaClass::FunctionQueryOptions)
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
index 7de0cfc09..61429b383 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
@@ -3933,9 +3933,10 @@ void CppGenerator::writeClassDefinition(QTextStream &s,
m_tpFuncs[func->name()] = cpythonFunctionName(func);
}
if (m_tpFuncs.value(QLatin1String("__repr__")).isEmpty()
- && !metaClass->isQObject()
&& metaClass->hasToStringCapability()) {
- m_tpFuncs[QLatin1String("__repr__")] = writeReprFunction(s, classContext);
+ m_tpFuncs[QLatin1String("__repr__")] = writeReprFunction(s,
+ classContext,
+ metaClass->toStringCapabilityIndirections());
}
// class or some ancestor has multiple inheritance
@@ -6006,7 +6007,9 @@ void CppGenerator::writeIndexError(QTextStream &s, const QString &errorMsg)
s << INDENT << '}' << endl;
}
-QString CppGenerator::writeReprFunction(QTextStream &s, GeneratorContext &context)
+QString CppGenerator::writeReprFunction(QTextStream &s,
+ GeneratorContext &context,
+ uint indirections)
{
const AbstractMetaClass *metaClass = context.metaClass();
QString funcName = cpythonBaseName(metaClass) + QLatin1String("__repr__");
@@ -6019,7 +6022,7 @@ QString CppGenerator::writeReprFunction(QTextStream &s, GeneratorContext &contex
s << INDENT << "buffer.open(QBuffer::ReadWrite);" << endl;
s << INDENT << "QDebug dbg(&buffer);" << endl;
s << INDENT << "dbg << ";
- if (metaClass->typeEntry()->isValue())
+ if (metaClass->typeEntry()->isValue() || indirections == 0)
s << '*';
s << CPP_SELF_VAR << ';' << endl;
s << INDENT << "buffer.close();" << endl;
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.h b/sources/shiboken2/generator/shiboken2/cppgenerator.h
index ae6da9582..005518f96 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.h
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.h
@@ -336,7 +336,7 @@ private:
/// Helper function for writeStdListWrapperMethods.
void writeIndexError(QTextStream &s, const QString &errorMsg);
- QString writeReprFunction(QTextStream &s, GeneratorContext &context);
+ QString writeReprFunction(QTextStream &s, GeneratorContext &context, uint indirections);
const AbstractMetaFunction *boolCast(const AbstractMetaClass *metaClass) const;
bool hasBoolCast(const AbstractMetaClass *metaClass) const