diff options
author | Christian Tismer <tismer@stackless.com> | 2021-08-10 15:57:07 +0200 |
---|---|---|
committer | Christian Tismer <tismer@stackless.com> | 2021-08-10 22:39:47 +0200 |
commit | f7b23933a737503ba23bf4c98133ea1e578806a2 (patch) | |
tree | 83f53792625685bff2c468302d4cc01533492be0 | |
parent | f417d1b85d8f2bd499f18091994baab126dacc66 (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>
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; |