aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2021-08-10 15:57:07 +0200
committerChristian Tismer <tismer@stackless.com>2021-08-10 22:39:47 +0200
commitf7b23933a737503ba23bf4c98133ea1e578806a2 (patch)
tree83f53792625685bff2c468302d4cc01533492be0
parentf417d1b85d8f2bd499f18091994baab126dacc66 (diff)
ApiExtractor: add classmethod attribute to add-function for tr()
++ This change was forgotten to port to 6.1 . It is needed for ++ feature: move getFeatureSelectId to Shiboken and refactor [ChangeLog][PySide6] The tr() translation method of QObject has been changed to be a class method. This makes it possible to use tr() on a class without instantiation. The tr() method of QObject should be a class method. - Build class method support into apiextraktor. - Use the new functionality in the tr() method. Listing of tr() in QtCore.pyi will be solved in an extra check-in. Task-number: PYSIDE-131 Task-number: PYSIDE-1252 Change-Id: If5093e038c091bf8c4d2a940fe206f6caa99568e Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
-rw-r--r--sources/pyside6/PySide6/QtCore/typesystem_core_common.xml12
-rw-r--r--sources/pyside6/PySide6/glue/qtcore.cpp2
-rw-r--r--sources/pyside6/tests/QtCore/qobject_tr_as_instance_test.py7
-rw-r--r--sources/pyside6/tests/QtCore/translation_test.py23
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.cpp2
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.h7
-rw-r--r--sources/shiboken6/ApiExtractor/modifications.h13
-rw-r--r--sources/shiboken6/ApiExtractor/typesystemparser.cpp6
-rw-r--r--sources/shiboken6/doc/typesystem_manipulating_objects.rst13
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp32
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.h4
-rw-r--r--sources/shiboken6/generator/shiboken/overloaddata.cpp18
-rw-r--r--sources/shiboken6/generator/shiboken/overloaddata.h6
13 files changed, 118 insertions, 27 deletions
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
index 4ee667ae6..e74fd8130 100644
--- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
+++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
@@ -1730,15 +1730,11 @@
</modify-argument>
</add-function>
- <add-function signature="tr(const char*,const char*,int)" return-type="QString">
- <modify-argument index="2">
- <replace-default-expression with="0"/>
- </modify-argument>
- <modify-argument index="3">
- <replace-default-expression with="-1"/>
- </modify-argument>
-
+ <add-function signature="tr(const char *@sourceText@, const char *@disambiguation@=nullptr, int @n@=-1)" return-type="QString" classmethod="yes">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-tr"/>
+ <modify-argument index="1" pyi-type="str"/>
+ <modify-argument index="2" pyi-type="Optional[str]">
+ </modify-argument>
</add-function>
<modify-function signature="receivers(const char*)const">
diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp
index 41817a2c7..09e27c027 100644
--- a/sources/pyside6/PySide6/glue/qtcore.cpp
+++ b/sources/pyside6/PySide6/glue/qtcore.cpp
@@ -904,7 +904,7 @@ _findChildrenHelper(%CPPSELF, %2, reinterpret_cast<PyTypeObject *>(%PYARG_1), %3
// - return the translation.
// @snippet qobject-tr
-PyTypeObject *type = Py_TYPE(%PYSELF);
+PyTypeObject *type = reinterpret_cast<PyTypeObject *>(%PYSELF);
PyObject *mro = type->tp_mro;
auto len = PyTuple_GET_SIZE(mro);
QString result = QString::fromUtf8(%1);
diff --git a/sources/pyside6/tests/QtCore/qobject_tr_as_instance_test.py b/sources/pyside6/tests/QtCore/qobject_tr_as_instance_test.py
index 796fc93a2..f70c04344 100644
--- a/sources/pyside6/tests/QtCore/qobject_tr_as_instance_test.py
+++ b/sources/pyside6/tests/QtCore/qobject_tr_as_instance_test.py
@@ -64,11 +64,12 @@ class QObjectTrTest(unittest.TestCase):
self.assertEqual((invar1, invar2), (outvar1, outvar2))
def testTrAsInstanceMethod(self):
- # Test QObject.tr as instance
+ # Test QObject.tr as instance.
+ # PYSIDE-1252: This works now as a class method!
invar1 = 'test1'
- outvar1 = QObject.tr(self.obj, invar1)
+ outvar1 = QObject.tr(invar1)
invar2 = 'test2'
- outvar2 = QObject.tr(self.obj, invar2, 'test comment')
+ outvar2 = QObject.tr(invar2, 'test comment')
self.assertEqual((invar1, invar2), (outvar1, outvar2))
diff --git a/sources/pyside6/tests/QtCore/translation_test.py b/sources/pyside6/tests/QtCore/translation_test.py
index 6536ca66f..beb707914 100644
--- a/sources/pyside6/tests/QtCore/translation_test.py
+++ b/sources/pyside6/tests/QtCore/translation_test.py
@@ -62,6 +62,16 @@ class TranslationTest(UsesQCoreApplication):
obj.setObjectName(obj.tr('Hello World!'))
self.assertEqual(obj.objectName(), 'Orbis, te saluto!')
+ def testLatinClass(self):
+ # Set string value to Latin, no instance
+ translator = QTranslator()
+ translator.load(os.path.join(self.trdir, 'trans_latin.qm'))
+ self.app.installTranslator(translator)
+
+ obj = QObject()
+ obj.setObjectName(QObject.tr('Hello World!'))
+ self.assertEqual(obj.objectName(), 'Orbis, te saluto!')
+
def testLatinDerived(self):
# PYSIDE-131: Test that derived classes work, too.
translator = QTranslator()
@@ -75,6 +85,19 @@ class TranslationTest(UsesQCoreApplication):
obj.setObjectName(obj.tr('Hello World!'))
self.assertEqual(obj.objectName(), 'Orbis, te saluto!')
+ def testLatinDerivedClass(self):
+ # PYSIDE-131: Test that derived classes work, too, no instance.
+ translator = QTranslator()
+ translator.load(os.path.join(self.trdir, 'trans_latin.qm'))
+ self.app.installTranslator(translator)
+
+ class Derived(QObject):
+ pass
+
+ obj = Derived()
+ obj.setObjectName(Derived.tr('Hello World!'))
+ self.assertEqual(obj.objectName(), 'Orbis, te saluto!')
+
def testRussian(self):
# Set string value to Russian
translator = QTranslator()
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
index f5b9b168f..5c8c332c6 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
@@ -125,6 +125,8 @@ AbstractMetaFunction::AbstractMetaFunction(const AddedFunctionPtr &addedFunc) :
AbstractMetaFunction::Attributes atts = AbstractMetaFunction::FinalInTargetLang;
if (addedFunc->isStatic())
atts |= AbstractMetaFunction::Static;
+ if (addedFunc->isClassMethod())
+ atts |= AbstractMetaFunction::ClassMethod;
setAttributes(atts);
}
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.h b/sources/shiboken6/ApiExtractor/abstractmetafunction.h
index 46bb708fa..b0b9a246e 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetafunction.h
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.h
@@ -109,6 +109,7 @@ public:
Abstract = 0x00000002,
Static = 0x00000004,
+ ClassMethod = 0x00000008,
FinalInTargetLang = 0x00000010,
@@ -139,6 +140,7 @@ public:
bool isFinalInTargetLang() const;
bool isAbstract() const;
+ bool isClassMethod() const;
bool isStatic() const;
bool isInvokable() const;
bool isPropertyReader() const;
@@ -423,6 +425,11 @@ inline bool AbstractMetaFunction::isStatic() const
return attributes().testFlag(Static);
}
+inline bool AbstractMetaFunction::isClassMethod() const
+{
+ return attributes().testFlag(ClassMethod);
+}
+
inline bool AbstractMetaFunction::isInvokable() const
{
return attributes().testFlag(Invokable);
diff --git a/sources/shiboken6/ApiExtractor/modifications.h b/sources/shiboken6/ApiExtractor/modifications.h
index c79184cbe..547e4e4ab 100644
--- a/sources/shiboken6/ApiExtractor/modifications.h
+++ b/sources/shiboken6/ApiExtractor/modifications.h
@@ -491,12 +491,24 @@ struct AddedFunction
m_isStatic = value;
}
+ /// Set this method as a classmethod.
+ void setClassMethod(bool value)
+ {
+ m_isClassMethod = value;
+ }
+
/// Returns true if this is a static method.
bool isStatic() const
{
return m_isStatic;
}
+ /// Returns true if this is a class method.
+ bool isClassMethod() const
+ {
+ return m_isClassMethod;
+ }
+
bool isDeclaration() const { return m_isDeclaration; } // <declare-function>
void setDeclaration(bool value) { m_isDeclaration = value; }
@@ -508,6 +520,7 @@ private:
TypeInfo m_returnType;
Access m_access = Public;
bool m_isConst = false;
+ bool m_isClassMethod = false;
bool m_isStatic = false;
bool m_isDeclaration = false;
};
diff --git a/sources/shiboken6/ApiExtractor/typesystemparser.cpp b/sources/shiboken6/ApiExtractor/typesystemparser.cpp
index 845c2feda..5491bf028 100644
--- a/sources/shiboken6/ApiExtractor/typesystemparser.cpp
+++ b/sources/shiboken6/ApiExtractor/typesystemparser.cpp
@@ -97,6 +97,7 @@ static inline QString signatureAttribute() { return QStringLiteral("signature");
static inline QString snippetAttribute() { return QStringLiteral("snippet"); }
static inline QString snakeCaseAttribute() { return QStringLiteral("snake-case"); }
static inline QString staticAttribute() { return QStringLiteral("static"); }
+static inline QString classmethodAttribute() { return QStringLiteral("classmethod"); }
static inline QString threadAttribute() { return QStringLiteral("thread"); }
static inline QString sourceAttribute() { return QStringLiteral("source"); }
static inline QString streamAttribute() { return QStringLiteral("stream"); }
@@ -2219,6 +2220,7 @@ bool TypeSystemParser::parseAddFunction(const QXmlStreamReader &,
QString originalSignature;
QString returnType;
bool staticFunction = false;
+ bool classMethod = false;
QString access;
int overloadNumber = TypeSystem::OverloadNumberUnset;
for (int i = attributes->size() - 1; i >= 0; --i) {
@@ -2230,6 +2232,9 @@ bool TypeSystemParser::parseAddFunction(const QXmlStreamReader &,
} else if (name == staticAttribute()) {
staticFunction = convertBoolean(attributes->takeAt(i).value(),
staticAttribute(), false);
+ } else if (name == classmethodAttribute()) {
+ classMethod = convertBoolean(attributes->takeAt(i).value(),
+ classmethodAttribute(), false);
} else if (name == accessAttribute()) {
access = attributes->takeAt(i).value().toString();
} else if (name == overloadNumberAttribute()) {
@@ -2257,6 +2262,7 @@ bool TypeSystemParser::parseAddFunction(const QXmlStreamReader &,
}
func->setStatic(staticFunction);
+ func->setClassMethod(classMethod);
if (!signature.contains(QLatin1Char('(')))
signature += QLatin1String("()");
m_currentSignature = signature;
diff --git a/sources/shiboken6/doc/typesystem_manipulating_objects.rst b/sources/shiboken6/doc/typesystem_manipulating_objects.rst
index 08c3aad09..1e9a4569e 100644
--- a/sources/shiboken6/doc/typesystem_manipulating_objects.rst
+++ b/sources/shiboken6/doc/typesystem_manipulating_objects.rst
@@ -261,12 +261,21 @@ add-function
.. code-block:: xml
<object-type>
- <add-function signature="..." return-type="..." access="public | protected" static="yes | no" since="..."/>
+ <add-function signature="..." return-type="..." access="public | protected" static="yes | no" classmethod="yes | no" since="..."/>
</object-type>
The ``return-type`` attribute defaults to *void*, the ``access`` to *public* and the ``static`` one to *no*.
- The ``since`` attribute specify the API version when this function was added.
+ The ``since`` attribute specifies the API version when this function was added.
+
+ The ``classmethod`` attribute specifies whether the function should be a Python class method.
+ It sets the METH_CLASS flag which means that ``PyTypeObject`` instead of an instance
+ ``PyObject`` is passed as self, which needs to be handled in injected code.
+
+ Note that the label "static" in Qt's class documentation almost always means that a Python
+ ``classmethod`` should be generated, because an object's class is always accessible from the
+ static C++ code, while Python needs the explicit "self" parameter that ``classmethod``
+ provides.
Within the signature, names for the function parameters can be specified by
enclosing them within the delimiter *@*:
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
index a89c7f88b..cc5fb20fb 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -1842,7 +1842,8 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s, OverloadData &overl
} else {
if (rfunc->implementingClass() &&
(!rfunc->implementingClass()->isNamespace() && overloadData.hasInstanceFunction())) {
- writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction());
+ writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction(),
+ overloadData.hasClassMethod());
}
if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType())
s << "PyObject *" << PYTHON_RETURN_VAR << "{};\n";
@@ -2253,6 +2254,7 @@ void CppGenerator::writeCppSelfConversion(TextStream &s, const GeneratorContext
void CppGenerator::writeCppSelfDefinition(TextStream &s,
const GeneratorContext &context,
bool hasStaticOverload,
+ bool hasClassMethodOverload,
bool cppSelfAsReference) const
{
Q_ASSERT(!(cppSelfAsReference && hasStaticOverload));
@@ -2283,10 +2285,13 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s,
}
if (!hasStaticOverload) {
- s << "auto " << CPP_SELF_VAR << " = ";
- writeCppSelfConversion(s, context, className, useWrapperClass);
- s << ";\n";
- writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR));
+ if (!hasClassMethodOverload) {
+ // PYSIDE-131: The single case of a class method for now: tr().
+ s << "auto " << CPP_SELF_VAR << " = ";
+ writeCppSelfConversion(s, context, className, useWrapperClass);
+ s << ";\n";
+ writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR));
+ }
return;
}
@@ -2306,7 +2311,8 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s,
void CppGenerator::writeCppSelfDefinition(TextStream &s,
const AbstractMetaFunctionCPtr &func,
const GeneratorContext &context,
- bool hasStaticOverload) const
+ bool hasStaticOverload,
+ bool hasClassMethodOverload) const
{
if (!func->ownerClass() || func->isConstructor())
return;
@@ -2323,7 +2329,7 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s,
s << "std::swap(self, " << PYTHON_ARG << ");\n";
}
- writeCppSelfDefinition(s, context, hasStaticOverload);
+ writeCppSelfDefinition(s, context, hasStaticOverload, hasClassMethodOverload);
}
void CppGenerator::writeErrorSection(TextStream &s, OverloadData &overloadData)
@@ -4600,7 +4606,7 @@ void CppGenerator::writeCopyFunction(TextStream &s, const GeneratorContext &cont
const QString className = chopType(cpythonTypeName(metaClass));
s << "static PyObject *" << className << "___copy__(PyObject *self)\n"
<< "{\n" << indent;
- writeCppSelfDefinition(s, context, false, true);
+ writeCppSelfDefinition(s, context, false, false, true);
QString conversionCode;
if (!context.forSmartPointer())
conversionCode = cpythonToPythonConversionFunction(metaClass);
@@ -4812,7 +4818,7 @@ void CppGenerator::writeRichCompareFunction(TextStream &s,
s << "static PyObject * ";
s << baseName << "_richcompare(PyObject *self, PyObject *" << PYTHON_ARG
<< ", int op)\n{\n" << indent;
- writeCppSelfDefinition(s, context, false, true);
+ writeCppSelfDefinition(s, context, false, false, true);
writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR));
s << "PyObject *" << PYTHON_RETURN_VAR << "{};\n"
<< "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ";\n";
@@ -4960,9 +4966,11 @@ QString CppGenerator::methodDefinitionParameters(const OverloadData &overloadDat
// invisible namespaces).
auto ownerClass = func->ownerClass();
if (ownerClass
- && !invisibleTopNamespaces().contains(const_cast<AbstractMetaClass *>(ownerClass))
- && overloadData.hasStaticFunction()) {
- s << "|METH_STATIC";
+ && !invisibleTopNamespaces().contains(const_cast<AbstractMetaClass *>(ownerClass))) {
+ if (overloadData.hasStaticFunction())
+ s << "|METH_STATIC";
+ if (overloadData.hasClassMethod())
+ s << "|METH_CLASS";
}
return result;
}
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h
index 7795b4e11..00035c5ae 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.h
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.h
@@ -104,10 +104,12 @@ private:
void writeCppSelfDefinition(TextStream &s,
const AbstractMetaFunctionCPtr &func,
const GeneratorContext &context,
- bool hasStaticOverload = false) const;
+ bool hasStaticOverload = false,
+ bool hasClassMethodOverload = false) const;
void writeCppSelfDefinition(TextStream &s,
const GeneratorContext &context,
bool hasStaticOverload = false,
+ bool hasClassMethodOverload = false,
bool cppSelfAsReference = false) const;
static void writeErrorSection(TextStream &s, OverloadData &overloadData) ;
diff --git a/sources/shiboken6/generator/shiboken/overloaddata.cpp b/sources/shiboken6/generator/shiboken/overloaddata.cpp
index 515dfd31d..018a76b71 100644
--- a/sources/shiboken6/generator/shiboken/overloaddata.cpp
+++ b/sources/shiboken6/generator/shiboken/overloaddata.cpp
@@ -595,6 +595,24 @@ bool OverloadData::hasStaticFunction() const
return false;
}
+bool OverloadData::hasClassMethod(const AbstractMetaFunctionCList &overloads)
+{
+ for (const auto &func : overloads) {
+ if (func->isClassMethod())
+ return true;
+ }
+ return false;
+}
+
+bool OverloadData::hasClassMethod() const
+{
+ for (const auto &func : m_overloads) {
+ if (func->isClassMethod())
+ return true;
+ }
+ return false;
+}
+
bool OverloadData::hasInstanceFunction(const AbstractMetaFunctionCList &overloads)
{
for (const auto &func : overloads) {
diff --git a/sources/shiboken6/generator/shiboken/overloaddata.h b/sources/shiboken6/generator/shiboken/overloaddata.h
index ecd7a5596..89e56caa4 100644
--- a/sources/shiboken6/generator/shiboken/overloaddata.h
+++ b/sources/shiboken6/generator/shiboken/overloaddata.h
@@ -70,6 +70,12 @@ public:
/// Returns true if any of the overloads passed as argument is static.
static bool hasStaticFunction(const AbstractMetaFunctionCList &overloads);
+ /// Returns true if any of the overloads for the current OverloadData is classmethod.
+ bool hasClassMethod() const;
+
+ /// Returns true if any of the overloads passed as argument is classmethod.
+ static bool hasClassMethod(const AbstractMetaFunctionCList &overloads);
+
/// Returns true if any of the overloads for the current OverloadData is not static.
bool hasInstanceFunction() const;