From ed87e710dd14bc9612dcfdba2e09143201768556 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 30 Nov 2020 18:39:25 +0100 Subject: shiboken6: Add support for a snake case typesystem attribute Add a snake case attribute to type system, complex type entry, function type entry as well as to function and field modifications. Add a function definitionNames() to AbstractMetaFunction/Field returning the names under which the function/field will be registered. Change the code writing the registration accordingly. Fixes: PYSIDE-1441 Change-Id: I178390bb80fa25aad9f8a56e99e4cc70064178eb Reviewed-by: Cristian Maureira-Fredes --- .../shiboken6/ApiExtractor/abstractmetabuilder.cpp | 49 +++++++++++- .../shiboken6/ApiExtractor/abstractmetabuilder.h | 6 ++ .../shiboken6/ApiExtractor/abstractmetafield.cpp | 23 ++++++ sources/shiboken6/ApiExtractor/abstractmetafield.h | 5 ++ .../ApiExtractor/abstractmetafunction.cpp | 46 ++++++++++++ .../shiboken6/ApiExtractor/abstractmetafunction.h | 5 ++ sources/shiboken6/ApiExtractor/modifications.cpp | 24 ++++++ sources/shiboken6/ApiExtractor/modifications.h | 6 ++ .../ApiExtractor/tests/testmodifyfunction.cpp | 22 ++++++ .../ApiExtractor/tests/testmodifyfunction.h | 2 + sources/shiboken6/ApiExtractor/typesystem.cpp | 64 +++++++++++++++- sources/shiboken6/ApiExtractor/typesystem.h | 13 ++++ sources/shiboken6/ApiExtractor/typesystem_enums.h | 7 ++ .../shiboken6/ApiExtractor/typesystemparser.cpp | 45 ++++++++++- .../doc/typesystem_manipulating_objects.rst | 10 ++- .../shiboken6/doc/typesystem_specifying_types.rst | 36 ++++++++- .../shiboken6/generator/shiboken/cppgenerator.cpp | 58 +++++++++++---- .../shiboken6/generator/shiboken/cppgenerator.h | 5 +- sources/shiboken6/tests/libsample/CMakeLists.txt | 1 + .../shiboken6/tests/libsample/snakecasetest.cpp | 69 +++++++++++++++++ sources/shiboken6/tests/libsample/snakecasetest.h | 65 ++++++++++++++++ .../shiboken6/tests/samplebinding/CMakeLists.txt | 2 + sources/shiboken6/tests/samplebinding/global.h | 1 + .../tests/samplebinding/snakecase_test.py | 86 ++++++++++++++++++++++ .../tests/samplebinding/typesystem_sample.xml | 8 ++ 25 files changed, 625 insertions(+), 33 deletions(-) create mode 100644 sources/shiboken6/tests/libsample/snakecasetest.cpp create mode 100644 sources/shiboken6/tests/libsample/snakecasetest.h create mode 100644 sources/shiboken6/tests/samplebinding/snakecase_test.py diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp index a3c2d3731..41e42ac2a 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp @@ -451,13 +451,11 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) if (!funcEntry->hasSignature(metaFunc->minimalSignature())) continue; + metaFunc->setTypeEntry(funcEntry); applyFunctionModifications(metaFunc); setInclude(funcEntry, func->fileName()); - if (metaFunc->typeEntry()) - delete metaFunc->typeEntry(); - metaFunc->setTypeEntry(funcEntry); m_globalFunctions << metaFuncPtr; } @@ -1368,6 +1366,51 @@ void AbstractMetaBuilderPrivate::fillAddedFunctions(AbstractMetaClass *metaClass } } +QString AbstractMetaBuilder::getSnakeCaseName(const QString &name) +{ + const int size = name.size(); + if (size < 3) + return name; + QString result; + result.reserve(size + 4); + for (int i = 0; i < size; ++i) { + const QChar c = name.at(i); + if (c.isUpper()) { + if (i > 0) { + if (name.at(i - 1).isUpper()) + return name; // Give up at consecutive upper chars + result.append(u'_'); + } + result.append(c.toLower()); + } else { + result.append(c); + } + } + return result; +} + +// Names under which an item will be registered to Python depending on snakeCase +QStringList AbstractMetaBuilder::definitionNames(const QString &name, + TypeSystem::SnakeCase snakeCase) +{ + QStringList result; + switch (snakeCase) { + case TypeSystem::SnakeCase::Unspecified: + case TypeSystem::SnakeCase::Disabled: + result.append(name); + break; + case TypeSystem::SnakeCase::Enabled: + result.append(AbstractMetaBuilder::getSnakeCaseName(name)); + break; + case TypeSystem::SnakeCase::Both: + result.append(AbstractMetaBuilder::getSnakeCaseName(name)); + if (name != result.constFirst()) + result.append(name); + break; + } + return result; +} + void AbstractMetaBuilderPrivate::applyFunctionModifications(AbstractMetaFunction *func) { AbstractMetaFunction& funcRef = *func; diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.h b/sources/shiboken6/ApiExtractor/abstractmetabuilder.h index cd9f36ca9..5b0414cd0 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.h +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.h @@ -32,6 +32,7 @@ #include "abstractmetalang_typedefs.h" #include "header_paths.h" #include "dependency.h" +#include "typesystem_enums.h" #include "clangparser/compilersupport.h" @@ -106,6 +107,11 @@ public: translateType(const QString &t, AbstractMetaClass *currentClass = nullptr, TranslateTypeFlags flags = {}, QString *errorMessage = nullptr); + static QString getSnakeCaseName(const QString &name); + // Names under which an item will be registered to Python depending on snakeCase + static QStringList definitionNames(const QString &name, + TypeSystem::SnakeCase snakeCase); + #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &d) const; #endif diff --git a/sources/shiboken6/ApiExtractor/abstractmetafield.cpp b/sources/shiboken6/ApiExtractor/abstractmetafield.cpp index 3c294c3c7..23c21d2a5 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetafield.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetafield.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "abstractmetafield.h" +#include "abstractmetabuilder.h" #include "abstractmetalang.h" #include "abstractmetatype.h" #include "documentation.h" @@ -111,6 +112,11 @@ QString AbstractMetaField::qualifiedCppName() const + originalName(); } +QStringList AbstractMetaField::definitionNames() const +{ + return AbstractMetaBuilder::definitionNames(d->m_name, snakeCase()); +} + QString AbstractMetaField::originalName() const { return d->m_originalName.isEmpty() ? d->m_name : d->m_originalName; @@ -166,6 +172,23 @@ bool AbstractMetaField::canGenerateSetter() const && (!d->m_type.isConstant() || d->m_type.isPointerToConst()); } +TypeSystem::SnakeCase AbstractMetaField::snakeCase() const +{ + // Renamed? + if (!d->m_originalName.isEmpty() && d->m_originalName != d->m_name) + return TypeSystem::SnakeCase::Disabled; + + for (const auto &mod : modifications()) { + if (mod.snakeCase() != TypeSystem::SnakeCase::Unspecified) + return mod.snakeCase(); + } + + auto typeEntry = enclosingClass()->typeEntry(); + const auto snakeCase = typeEntry->snakeCase(); + return snakeCase != TypeSystem::SnakeCase::Unspecified + ? snakeCase : typeEntry->typeSystemTypeEntry()->snakeCase(); +} + FieldModificationList AbstractMetaField::modifications() const { const FieldModificationList &mods = enclosingClass()->typeEntry()->fieldModifications(); diff --git a/sources/shiboken6/ApiExtractor/abstractmetafield.h b/sources/shiboken6/ApiExtractor/abstractmetafield.h index 7964d5b57..52e08e2af 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetafield.h +++ b/sources/shiboken6/ApiExtractor/abstractmetafield.h @@ -66,6 +66,9 @@ public: QString qualifiedCppName() const; + // Names under which the field will be registered to Python. + QStringList definitionNames() const; + QString originalName() const; void setOriginalName(const QString& name); @@ -80,6 +83,8 @@ public: bool canGenerateGetter() const; bool canGenerateSetter() const; + TypeSystem::SnakeCase snakeCase() const; + static std::optional find(const AbstractMetaFieldList &haystack, const QString &needle); diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp index e2d0fb9dc..9594cfdb6 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "abstractmetafunction.h" +#include "abstractmetabuilder.h" #include "abstractmetalang.h" #include "abstractmetalang_helpers.h" #include "abstractmetatype.h" @@ -144,6 +145,11 @@ void AbstractMetaFunction::setOriginalName(const QString &name) d->m_originalName = name; } +QStringList AbstractMetaFunction::definitionNames() const +{ + return AbstractMetaBuilder::definitionNames(d->m_name, snakeCase()); +} + const Documentation &AbstractMetaFunction::documentation() const { return d->m_doc; @@ -1132,6 +1138,46 @@ int AbstractMetaFunction::overloadNumber() const return d->overloadNumber(this); } +TypeSystem::SnakeCase AbstractMetaFunction::snakeCase() const +{ + if (isUserAdded()) + return TypeSystem::SnakeCase::Disabled; + // Renamed? + if (!d->m_originalName.isEmpty() && d->m_originalName != d->m_name) + return TypeSystem::SnakeCase::Disabled; + switch (d->m_functionType) { + case AbstractMetaFunction::NormalFunction: + case AbstractMetaFunction::SignalFunction: + case AbstractMetaFunction::EmptyFunction: + case AbstractMetaFunction::SlotFunction: + case AbstractMetaFunction::GlobalScopeFunction: + if (isOperatorOverload()) + return TypeSystem::SnakeCase::Disabled; + break; + default: + return TypeSystem::SnakeCase::Disabled; + } + + for (const auto &mod : modifications()) { + if (mod.snakeCase() != TypeSystem::SnakeCase::Unspecified) + return mod.snakeCase(); + } + + if (d->m_typeEntry) { // Global function + const auto snakeCase = d->m_typeEntry->snakeCase(); + return snakeCase != TypeSystem::SnakeCase::Unspecified + ? snakeCase : d->m_typeEntry->typeSystemTypeEntry()->snakeCase(); + } + + if (d->m_class) { + auto typeEntry = d->m_class->typeEntry(); + const auto snakeCase = typeEntry->snakeCase(); + return snakeCase != TypeSystem::SnakeCase::Unspecified + ? snakeCase : typeEntry->typeSystemTypeEntry()->snakeCase(); + } + return TypeSystem::SnakeCase::Disabled; +} + // Query functions for generators bool AbstractMetaFunction::injectedCodeUsesPySelf() const { diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.h b/sources/shiboken6/ApiExtractor/abstractmetafunction.h index 1a29fcefe..99174b47f 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetafunction.h +++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.h @@ -95,6 +95,9 @@ public: QString name() const; void setName(const QString &name); + // Names under which the function will be registered to Python. + QStringList definitionNames() const; + QString originalName() const; void setOriginalName(const QString &name); @@ -283,6 +286,8 @@ public: int overloadNumber() const; + TypeSystem::SnakeCase snakeCase() const; + // Query functions for generators /// Verifies if any of the function's code injections of the "native" /// type needs the type system variable "%PYSELF". diff --git a/sources/shiboken6/ApiExtractor/modifications.cpp b/sources/shiboken6/ApiExtractor/modifications.cpp index eba1437dc..9e5071a33 100644 --- a/sources/shiboken6/ApiExtractor/modifications.cpp +++ b/sources/shiboken6/ApiExtractor/modifications.cpp @@ -173,6 +173,7 @@ public: bool m_readable = true; bool m_writable = true; bool m_removed = false; + TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified; }; FieldModification::FieldModification() : d(new FieldModificationData) @@ -245,6 +246,17 @@ void FieldModification::setRemoved(bool r) d->m_removed = r; } +TypeSystem::SnakeCase FieldModification::snakeCase() const +{ + return d->snakeCase; +} + +void FieldModification::setSnakeCase(TypeSystem::SnakeCase s) +{ + if (d->snakeCase != s) + d->snakeCase = s; +} + // Helpers to split a parameter list of , // (@ denoting names), like // "void foo(QList &@list@ = QList{1,2}, int @b@=5, ...)" @@ -716,6 +728,7 @@ public: bool m_thread = false; TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified; TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; + TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified; }; FunctionModification::FunctionModification() : d(new FunctionModificationData) @@ -763,6 +776,17 @@ void FunctionModification::setArgument_mods(const QList &a d->m_argument_mods = argument_mods; } +TypeSystem::SnakeCase FunctionModification::snakeCase() const +{ + return d->snakeCase; +} + +void FunctionModification::setSnakeCase(TypeSystem::SnakeCase s) +{ + if (d->snakeCase != s) + d->snakeCase = s; +} + const CodeSnipList &FunctionModification::snips() const { return d->m_snips; diff --git a/sources/shiboken6/ApiExtractor/modifications.h b/sources/shiboken6/ApiExtractor/modifications.h index 1d217d0cb..de47c15c2 100644 --- a/sources/shiboken6/ApiExtractor/modifications.h +++ b/sources/shiboken6/ApiExtractor/modifications.h @@ -382,6 +382,9 @@ public: QList &argument_mods(); void setArgument_mods(const QList &argument_mods); + TypeSystem::SnakeCase snakeCase() const; + void setSnakeCase(TypeSystem::SnakeCase s); + #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &d) const; #endif @@ -423,6 +426,9 @@ public: bool isRemoved() const; void setRemoved(bool r); + TypeSystem::SnakeCase snakeCase() const; + void setSnakeCase(TypeSystem::SnakeCase s); + private: QSharedDataPointer d; }; diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp index 7c7bb6115..906b8a4b4 100644 --- a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp +++ b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp @@ -29,6 +29,7 @@ #include "testmodifyfunction.h" #include #include "testutil.h" +#include #include #include #include @@ -469,4 +470,25 @@ void TestModifyFunction::testScopedModifications() QCOMPARE(f->generateExceptionHandling(), expectedGenerateThrowing); } +void TestModifyFunction::testSnakeCaseRenaming_data() +{ + QTest::addColumn("name"); + QTest::addColumn("expected"); + QTest::newRow("s1") + << QStringLiteral("snakeCaseFunc") << QStringLiteral("snake_case_func"); + QTest::newRow("s2") + << QStringLiteral("SnakeCaseFunc") << QStringLiteral("snake_case_func"); + QTest::newRow("consecutive-uppercase") + << QStringLiteral("snakeCAseFunc") << QStringLiteral("snakeCAseFunc"); +} + +void TestModifyFunction::testSnakeCaseRenaming() +{ + QFETCH(QString, name); + QFETCH(QString, expected); + + const QString actual = AbstractMetaBuilder::getSnakeCaseName(name); + QCOMPARE(actual, expected); +} + QTEST_APPLESS_MAIN(TestModifyFunction) diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h index 375111e03..a9a13a82b 100644 --- a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h +++ b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h @@ -44,6 +44,8 @@ class TestModifyFunction : public QObject void testGlobalFunctionModification(); void testScopedModifications_data(); void testScopedModifications(); + void testSnakeCaseRenaming_data(); + void testSnakeCaseRenaming(); }; #endif diff --git a/sources/shiboken6/ApiExtractor/typesystem.cpp b/sources/shiboken6/ApiExtractor/typesystem.cpp index 372d36cfb..b76582b9f 100644 --- a/sources/shiboken6/ApiExtractor/typesystem.cpp +++ b/sources/shiboken6/ApiExtractor/typesystem.cpp @@ -637,9 +637,18 @@ void TypeEntry::useAsTypedef(const TypeEntry *source) m_d->m_version = source->m_d->m_version; } +// ----------------- TypeSystemTypeEntry +class TypeSystemTypeEntryPrivate : public TypeEntryPrivate +{ +public: + using TypeEntryPrivate::TypeEntryPrivate; + + TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Disabled; +}; + TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &entryName, const QVersionNumber &vr, const TypeEntry *parent) : - TypeEntry(entryName, TypeSystemType, vr, parent) + TypeEntry(new TypeSystemTypeEntryPrivate(entryName, TypeSystemType, vr, parent)) { } @@ -650,7 +659,20 @@ TypeSystemTypeEntry::TypeSystemTypeEntry(TypeEntryPrivate *d) : TypeEntry *TypeSystemTypeEntry::clone() const { - return new TypeSystemTypeEntry(new TypeEntryPrivate(*d_func())); + S_D(const TypeSystemTypeEntry); + return new TypeSystemTypeEntry(new TypeSystemTypeEntryPrivate(*d)); +} + +TypeSystem::SnakeCase TypeSystemTypeEntry::snakeCase() const +{ + S_D(const TypeSystemTypeEntry); + return d->m_snakeCase; +} + +void TypeSystemTypeEntry::setSnakeCase(TypeSystem::SnakeCase sc) +{ + S_D(TypeSystemTypeEntry); + d->m_snakeCase = sc; } // ----------------- VoidTypeEntry @@ -1148,6 +1170,7 @@ public: // For class functions TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified; + TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Unspecified; }; ComplexTypeEntry::ComplexTypeEntry(const QString &entryName, TypeEntry::Type t, @@ -1407,6 +1430,18 @@ bool ComplexTypeEntry::hasDefaultConstructor() const return !d->m_defaultConstructor.isEmpty(); } +TypeSystem::SnakeCase ComplexTypeEntry::snakeCase() const +{ + S_D(const ComplexTypeEntry); + return d->m_snakeCase; +} + +void ComplexTypeEntry::setSnakeCase(TypeSystem::SnakeCase sc) +{ + S_D(ComplexTypeEntry); + d->m_snakeCase = sc; +} + TypeEntry *ComplexTypeEntry::clone() const { S_D(const ComplexTypeEntry); @@ -1954,6 +1989,7 @@ public: } QStringList m_signatures; + TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Unspecified; }; FunctionTypeEntry::FunctionTypeEntry(const QString &entryName, const QString &signature, @@ -1981,6 +2017,18 @@ bool FunctionTypeEntry::hasSignature(const QString &signature) const return d->m_signatures.contains(signature); } +TypeSystem::SnakeCase FunctionTypeEntry::snakeCase() const +{ + S_D(const FunctionTypeEntry); + return d->m_snakeCase; +} + +void FunctionTypeEntry::setSnakeCase(TypeSystem::SnakeCase sc) +{ + S_D(FunctionTypeEntry); + d->m_snakeCase = sc; +} + TypeEntry *FunctionTypeEntry::clone() const { S_D(const FunctionTypeEntry); @@ -2074,7 +2122,8 @@ void ComplexTypeEntry::formatDebug(QDebug &debug) const if (d->m_typeFlags != 0) debug << ", typeFlags=" << d->m_typeFlags; debug << ", copyableFlag=" << d->m_copyableFlag - << ", except=" << int(d->m_exceptionHandling); + << ", except=" << int(d->m_exceptionHandling) + << ", snakeCase=" << int(d->m_snakeCase); FORMAT_NONEMPTY_STRING("defaultSuperclass", d->m_defaultSuperclass) FORMAT_NONEMPTY_STRING("polymorphicIdValue", d->m_polymorphicIdValue) FORMAT_NONEMPTY_STRING("targetType", d->m_targetType) @@ -2084,6 +2133,15 @@ void ComplexTypeEntry::formatDebug(QDebug &debug) const FORMAT_LIST_SIZE("fieldMods", d->m_fieldMods) } +void FunctionTypeEntry::formatDebug(QDebug &debug) const +{ + S_D(const FunctionTypeEntry); + + TypeEntry::formatDebug(debug); + debug << "signatures=" << d->m_signatures + << ", snakeCase=" << int(d->m_snakeCase); +} + void TypedefEntry::formatDebug(QDebug &debug) const { S_D(const TypedefEntry); diff --git a/sources/shiboken6/ApiExtractor/typesystem.h b/sources/shiboken6/ApiExtractor/typesystem.h index a2b52bb0d..a823814f7 100644 --- a/sources/shiboken6/ApiExtractor/typesystem.h +++ b/sources/shiboken6/ApiExtractor/typesystem.h @@ -296,6 +296,9 @@ public: TypeEntry *clone() const override; + TypeSystem::SnakeCase snakeCase() const; + void setSnakeCase(TypeSystem::SnakeCase sc); + protected: explicit TypeSystemTypeEntry(TypeEntryPrivate *d); }; @@ -573,6 +576,9 @@ public: void useAsTypedef(const ComplexTypeEntry *source); + TypeSystem::SnakeCase snakeCase() const; + void setSnakeCase(TypeSystem::SnakeCase sc); + #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &debug) const override; #endif @@ -741,8 +747,15 @@ public: bool hasSignature(const QString& signature) const; void addSignature(const QString& signature); + TypeSystem::SnakeCase snakeCase() const; + void setSnakeCase(TypeSystem::SnakeCase sc); + TypeEntry *clone() const override; +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const override; +#endif + protected: explicit FunctionTypeEntry(FunctionTypeEntryPrivate *d); }; diff --git a/sources/shiboken6/ApiExtractor/typesystem_enums.h b/sources/shiboken6/ApiExtractor/typesystem_enums.h index 0d7f279c4..60832b64e 100644 --- a/sources/shiboken6/ApiExtractor/typesystem_enums.h +++ b/sources/shiboken6/ApiExtractor/typesystem_enums.h @@ -81,6 +81,13 @@ enum class ExceptionHandling { On }; +enum class SnakeCase { + Unspecified, + Disabled, + Enabled, + Both +}; + enum Visibility { // For namespaces Unspecified, Visible, diff --git a/sources/shiboken6/ApiExtractor/typesystemparser.cpp b/sources/shiboken6/ApiExtractor/typesystemparser.cpp index 4c754f680..6762bef21 100644 --- a/sources/shiboken6/ApiExtractor/typesystemparser.cpp +++ b/sources/shiboken6/ApiExtractor/typesystemparser.cpp @@ -97,6 +97,7 @@ static inline QString replaceAttribute() { return QStringLiteral("replace"); } static inline QString toAttribute() { return QStringLiteral("to"); } 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 threadAttribute() { return QStringLiteral("thread"); } static inline QString sourceAttribute() { return QStringLiteral("source"); } @@ -392,6 +393,18 @@ ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, }; ENUM_LOOKUP_BINARY_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::SnakeCase, Qt::CaseSensitive, + snakeCaseFromAttribute, TypeSystem::SnakeCase::Unspecified) +{ + {u"no", TypeSystem::SnakeCase::Disabled}, + {u"false", TypeSystem::SnakeCase::Disabled}, + {u"yes", TypeSystem::SnakeCase::Enabled}, + {u"true", TypeSystem::SnakeCase::Enabled}, + {u"both", TypeSystem::SnakeCase::Both}, +}; +ENUM_LOOKUP_LINEAR_SEARCH() + ENUM_LOOKUP_BEGIN(TypeSystem::Visibility, Qt::CaseSensitive, visibilityFromAttribute, TypeSystem::Visibility::Unspecified) { @@ -1442,18 +1455,29 @@ FunctionTypeEntry * { if (!checkRootElement()) return nullptr; - const int signatureIndex = indexOfAttribute(*attributes, signatureAttribute()); - if (signatureIndex == -1) { + + QString signature; + TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Disabled; + + for (int i = attributes->size() - 1; i >= 0; --i) { + const auto name = attributes->at(i).qualifiedName(); + if (name == signatureAttribute()) { + signature = TypeDatabase::normalizedSignature(attributes->takeAt(i).value().toString()); + } else if (name == snakeCaseAttribute()) { + snakeCase = snakeCaseFromAttribute(attributes->takeAt(i).value()); + } + } + + if (signature.isEmpty()) { m_error = msgMissingAttribute(signatureAttribute()); return nullptr; } - const QString signature = - TypeDatabase::normalizedSignature(attributes->takeAt(signatureIndex).value().toString()); TypeEntry *existingType = m_database->findType(name); if (!existingType) { auto *result = new FunctionTypeEntry(name, signature, since, currentParentTypeEntry()); + result->setSnakeCase(snakeCase); applyCommonAttributes(reader, result, attributes); return result; } @@ -1564,6 +1588,8 @@ void TypeSystemParser::applyComplexTypeAttributes(const QXmlStreamReader &reader ctype->setDeleteInMainThread(true); } else if (name == QLatin1String("target-type")) { ctype->setTargetType(attributes->takeAt(i).value().toString()); + } else if (name == snakeCaseAttribute()) { + ctype->setSnakeCase(snakeCaseFromAttribute(attributes->takeAt(i).value())); } } @@ -1700,6 +1726,8 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const QXmlStreamReader & const QVersionNumber &since, QXmlStreamAttributes *attributes) { + TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified; + for (int i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); if (name == packageAttribute()) { @@ -1724,6 +1752,8 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const QXmlStreamReader & qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } + } else if (name == snakeCaseAttribute()) { + snakeCase = snakeCaseFromAttribute(attributes->takeAt(i).value()); } } @@ -1735,6 +1765,7 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const QXmlStreamReader & currentParentTypeEntry()); } moduleEntry->setCodeGeneration(m_generate); + moduleEntry->setSnakeCase(snakeCase); if ((m_generate == TypeEntry::GenerateForSubclass || m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty()) @@ -2078,6 +2109,8 @@ bool TypeSystemParser::parseModifyField(const QXmlStreamReader &reader, fm.setWritable(convertBoolean(attributes->takeAt(i).value(), writeAttribute(), true)); } else if (name == renameAttribute()) { fm.setRenamedToName(attributes->takeAt(i).value().toString()); + } else if (name == snakeCaseAttribute()) { + fm.setSnakeCase(snakeCaseFromAttribute(attributes->takeAt(i).value())); } } if (fm.name().isEmpty()) { @@ -2232,6 +2265,7 @@ bool TypeSystemParser::parseModifyFunction(const QXmlStreamReader &reader, int overloadNumber = TypeSystem::OverloadNumberUnset; TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; TypeSystem::AllowThread allowThread = TypeSystem::AllowThread::Unspecified; + TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified; for (int i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); if (name == QLatin1String("signature")) { @@ -2265,6 +2299,8 @@ bool TypeSystemParser::parseModifyFunction(const QXmlStreamReader &reader, } else if (name == overloadNumberAttribute()) { if (!parseOverloadNumber(attributes->takeAt(i), &overloadNumber, &m_error)) return false; + } else if (name == snakeCaseAttribute()) { + snakeCase = snakeCaseFromAttribute(attributes->takeAt(i).value()); } else if (name == virtualSlotAttribute()) { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeWarning(reader, name))); @@ -2295,6 +2331,7 @@ bool TypeSystemParser::parseModifyFunction(const QXmlStreamReader &reader, mod.setOriginalSignature(originalSignature); mod.setExceptionHandling(exceptionHandling); mod.setOverloadNumber(overloadNumber); + mod.setSnakeCase(snakeCase); m_currentSignature = signature; if (!access.isEmpty()) { diff --git a/sources/shiboken6/doc/typesystem_manipulating_objects.rst b/sources/shiboken6/doc/typesystem_manipulating_objects.rst index dcb3a46b1..ea8c6247c 100644 --- a/sources/shiboken6/doc/typesystem_manipulating_objects.rst +++ b/sources/shiboken6/doc/typesystem_manipulating_objects.rst @@ -78,7 +78,8 @@ modify-field + remove="true | false" + snake-case="yes | no | both" /> The ``name`` attribute is the name of the field, the *optional* ``write`` @@ -91,6 +92,8 @@ modify-field The *optional* ``rename`` attribute can be used to change the name of the given field in the generated target language API. + The *optional* **snake-case** attribute allows for overriding the value + specified on the class entry or **typesystem** element. .. _modify-function: @@ -112,7 +115,8 @@ modify-function allow-thread="true | auto | false" exception-handling="off | auto-off | auto-on | on" overload-number="number" - rename="..." /> + rename="..." + snake-case="yes | no | both" /> The ``signature`` attribute is a normalized C++ signature, excluding return @@ -191,6 +195,8 @@ modify-function The *optional* ``access`` attribute changes the access privileges of the given function in the generated target language API. + The *optional* **snake-case** attribute allows for overriding the value + specified on the class entry or **typesystem** element. .. _add-function: diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst index 405469ea2..243af9603 100644 --- a/sources/shiboken6/doc/typesystem_specifying_types.rst +++ b/sources/shiboken6/doc/typesystem_specifying_types.rst @@ -35,7 +35,8 @@ typesystem .. code-block:: xml - + The **package** attribute is a string describing the package to be used, @@ -47,6 +48,22 @@ typesystem specify the default handling for the corresponding function modification (see :ref:`modify-function`). + The *optional* **snake-case** attribute specifies whether function + and field names will be automatically changed to the snake case + style that is common in Python (for example, ``snakeCase`` will be + changed to ``snake_case``). + + The value ``both`` means that the function or field will be exposed + under both its original name and the snake case version. There are + limitations to this though: + + - When overriding a virtual function of a C++ class in Python, + the snake case name must be used. + + - When static and non-static overloads of a class member function + exist (as is the case for example for ``QFileInfo::exists()``), + the snake case name must be used. + load-typesystem ^^^^^^^^^^^^^^^ @@ -280,7 +297,8 @@ value-type hash-function="..." stream="yes | no" default-constructor="..." - revision="..." /> + revision="..." + snake-case="yes | no | both" /> The **name** attribute is the fully qualified C++ class name, such as @@ -310,6 +328,9 @@ value-type specify the default handling for the corresponding function modification (see :ref:`modify-function`). + The *optional* **snake-case** attribute allows for overriding the value + specified on the **typesystem** element. + .. _object-type: object-type @@ -330,7 +351,8 @@ object-type exception-handling="..." hash-function="..." stream="yes | no" - revision="..." /> + revision="..." + snake-case="yes | no | both" /> The **name** attribute is the fully qualified C++ class name. If there is no @@ -360,6 +382,9 @@ object-type specify the default handling for the corresponding function modification (see :ref:`modify-function`). + The *optional* **snake-case** attribute allows for overriding the value + specified on the **typesystem** element. + interface-type ^^^^^^^^^^^^^^ @@ -480,7 +505,7 @@ function .. code-block:: xml - + This tag has some limitations, it doesn't support function modifications, besides you @@ -490,6 +515,9 @@ function The function tag has two *optional* attributes: **since**, whose value is used to specify the API version of this function, and **rename**, to modify the function name. + The *optional* **snake-case** attribute allows for overriding the value + specified on the **typesystem** element. + .. _system_include: system-include diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 0f1fe18f4..dc92e2432 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -575,11 +575,15 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon writeMethodWrapper(s, overloads, classContext); writeSignatureInfo(signatureStream, overloads); + // For a mixture of static and member function overloads, + // a separate PyMethodDef entry is written which is referenced + // in the PyMethodDef list and later in getattro() for handling + // the non-static case. if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { QString methDefName = cpythonMethodDefinitionName(rfunc); - smd << "static PyMethodDef " << methDefName << " = {\n" << indent; - writeMethodDefinitionEntry(smd, overloads); - smd << outdent << "\n};\n\n"; + smd << "static PyMethodDef " << methDefName << " = " << indent; + writeMethodDefinitionEntries(smd, overloads, 1); + smd << outdent << ";\n\n"; } writeMethodDefinition(md, overloads); } @@ -716,12 +720,16 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon s << "static PyGetSetDef " << cpythonGettersSettersDefinitionName(metaClass) << "[] = {\n" << indent; for (const AbstractMetaField &metaField : fields) { - if (!metaField.isStatic()) { - const QString getter = metaField.canGenerateGetter() + const bool canGenerateGetter = metaField.canGenerateGetter(); + const bool canGenerateSetter = metaField.canGenerateSetter(); + if (canGenerateGetter || canGenerateSetter) { + const QString getter = canGenerateGetter ? cpythonGetterFunctionName(metaField) : QString(); - const QString setter = metaField.canGenerateSetter() + const QString setter = canGenerateSetter ? cpythonSetterFunctionName(metaField) : QString(); - writePyGetSetDefEntry(s, metaField.name(), getter, setter); + const auto names = metaField.definitionNames(); + for (const auto &name : names) + writePyGetSetDefEntry(s, name, getter, setter); } } @@ -953,7 +961,8 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s, return; const TypeEntry *retType = func->type().typeEntry(); - const QString funcName = func->isOperatorOverload() ? pythonOperatorFunctionName(func) : func->name(); + const QString funcName = func->isOperatorOverload() + ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst(); QString prefix = wrapperName(func->ownerClass()) + QLatin1String("::"); s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues|Generator::OriginalTypeDescription) @@ -4881,16 +4890,16 @@ void CppGenerator::writeRichCompareFunction(TextStream &s, << outdent << "}\n\n"; } -void CppGenerator::writeMethodDefinitionEntry(TextStream &s, const AbstractMetaFunctionCList &overloads) const +QString CppGenerator::methodDefinitionParameters(const OverloadData &overloadData) const { - Q_ASSERT(!overloads.isEmpty()); - OverloadData overloadData(overloads, this); bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData); const auto func = overloadData.referenceFunction(); int min = overloadData.minArgs(); int max = overloadData.maxArgs(); - s << '"' << func->name() << "\", reinterpret_cast(" + QString result; + QTextStream s(&result); + s << "reinterpret_cast(" << cpythonFunctionName(func) << "), "; if ((min == max) && (max < 2) && !usePyArgs) { if (max == 0) @@ -4910,6 +4919,24 @@ void CppGenerator::writeMethodDefinitionEntry(TextStream &s, const AbstractMetaF && overloadData.hasStaticFunction()) { s << "|METH_STATIC"; } + return result; +} + +void CppGenerator::writeMethodDefinitionEntries(TextStream &s, + const AbstractMetaFunctionCList &overloads, + qsizetype maxEntries) const +{ + Q_ASSERT(!overloads.isEmpty()); + OverloadData overloadData(overloads, this); + const QStringList names = overloadData.referenceFunction()->definitionNames(); + const QString parameters = methodDefinitionParameters(overloadData); + const qsizetype count = maxEntries > 0 + ? qMin(names.size(), maxEntries) : names.size(); + for (qsizetype i = 0; i < count; ++i) { + if (i) + s << ",\n"; + s << "{\"" << names.at(i) << "\", " << parameters << '}'; + } } void CppGenerator::writeMethodDefinition(TextStream &s, const AbstractMetaFunctionCList &overloads) const @@ -4922,9 +4949,7 @@ void CppGenerator::writeMethodDefinition(TextStream &s, const AbstractMetaFuncti if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { s << cpythonMethodDefinitionName(func); } else { - s << '{'; - writeMethodDefinitionEntry(s, overloads); - s << '}'; + writeMethodDefinitionEntries(s, overloads); } s << ',' << '\n'; } @@ -5778,7 +5803,8 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck, << defName << ".ml_doc,\n"; } s << "};\n" - << "if (Shiboken::String::compare(name, \"" << func->name() << "\") == 0)\n"; + << "if (Shiboken::String::compare(name, \"" + << func->definitionNames().constFirst() << "\") == 0)\n"; Indentation indent(s); s << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n"; } diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h index 0539a19ec..a81df17c9 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.h +++ b/sources/shiboken6/generator/shiboken/cppgenerator.h @@ -286,7 +286,10 @@ private: void writeClassDefinition(TextStream &s, const AbstractMetaClass *metaClass, const GeneratorContext &classContext); - void writeMethodDefinitionEntry(TextStream &s, const AbstractMetaFunctionCList &overloads) const; + QString methodDefinitionParameters(const OverloadData &overloadData) const; + void writeMethodDefinitionEntries(TextStream &s, + const AbstractMetaFunctionCList &overloads, + qsizetype maxEntries = -1) const; void writeMethodDefinition(TextStream &s, const AbstractMetaFunctionCList &overloads) const; void writeSignatureInfo(TextStream &s, const AbstractMetaFunctionCList &overloads) const; /// Writes the implementation of all methods part of python sequence protocol diff --git a/sources/shiboken6/tests/libsample/CMakeLists.txt b/sources/shiboken6/tests/libsample/CMakeLists.txt index 3bf119e9f..af4f3318f 100644 --- a/sources/shiboken6/tests/libsample/CMakeLists.txt +++ b/sources/shiboken6/tests/libsample/CMakeLists.txt @@ -43,6 +43,7 @@ samplenamespace.cpp sbkdate.cpp simplefile.cpp size.cpp +snakecasetest.cpp sometime.cpp str.cpp strlist.cpp diff --git a/sources/shiboken6/tests/libsample/snakecasetest.cpp b/sources/shiboken6/tests/libsample/snakecasetest.cpp new file mode 100644 index 000000000..d82984e6f --- /dev/null +++ b/sources/shiboken6/tests/libsample/snakecasetest.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of Qt for Python. +** +** $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$ +** +****************************************************************************/ + +#include "snakecasetest.h" + +int SnakeCaseGlobalFunction() +{ + return 42; +} + +SnakeCaseTest::SnakeCaseTest() = default; +SnakeCaseTest::~SnakeCaseTest() = default; + +int SnakeCaseTest::testFunction1() const +{ + return 42; +} + +int SnakeCaseTest::testFunctionDisabled() const +{ + return 42; +} + +int SnakeCaseTest::testFunctionBoth() const +{ + return 42; +} + +int SnakeCaseTest::callVirtualFunc() const +{ + return virtualFunc(); +} + +int SnakeCaseTest::virtualFunc() const +{ + return 42; +} + +SnakeCaseDerivedTest::SnakeCaseDerivedTest() = default; + +int SnakeCaseDerivedTest::virtualFunc() const +{ + return 43; +} diff --git a/sources/shiboken6/tests/libsample/snakecasetest.h b/sources/shiboken6/tests/libsample/snakecasetest.h new file mode 100644 index 000000000..8900c2eec --- /dev/null +++ b/sources/shiboken6/tests/libsample/snakecasetest.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of Qt for Python. +** +** $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$ +** +****************************************************************************/ + +#ifndef SNAKECASETEST_H +#define SNAKECASETEST_H + +#include "libsamplemacros.h" + +LIBSAMPLE_API int SnakeCaseGlobalFunction(); + +class LIBSAMPLE_API SnakeCaseTest +{ +public: + SnakeCaseTest(); + virtual ~SnakeCaseTest(); + + int testFunction1() const; + int testFunctionDisabled() const; + int testFunctionBoth() const; + + int callVirtualFunc() const; + + int testField = 42; + int testFieldDisabled = 42; + int testFieldBoth = 42; + +protected: + virtual int virtualFunc() const; +}; + +class LIBSAMPLE_API SnakeCaseDerivedTest : public SnakeCaseTest +{ +public: + SnakeCaseDerivedTest(); + +protected: + int virtualFunc() const override; +}; + +#endif // SNAKECASETEST_H diff --git a/sources/shiboken6/tests/samplebinding/CMakeLists.txt b/sources/shiboken6/tests/samplebinding/CMakeLists.txt index 418e47c0d..2630b9c79 100644 --- a/sources/shiboken6/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken6/tests/samplebinding/CMakeLists.txt @@ -111,6 +111,8 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_derivedfromnamespace_wrapper. ${CMAKE_CURRENT_BINARY_DIR}/sample/simplefile_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/size_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/sizef_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/snakecasetest_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/snakecasederivedtest_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/sonofmderived1_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/str_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/strlist_wrapper.cpp diff --git a/sources/shiboken6/tests/samplebinding/global.h b/sources/shiboken6/tests/samplebinding/global.h index 0a168099b..f647eb795 100644 --- a/sources/shiboken6/tests/samplebinding/global.h +++ b/sources/shiboken6/tests/samplebinding/global.h @@ -81,6 +81,7 @@ #include "samplenamespace.h" #include "simplefile.h" #include "size.h" +#include "snakecasetest.h" #include "str.h" #include "strlist.h" #include "sometime.h" diff --git a/sources/shiboken6/tests/samplebinding/snakecase_test.py b/sources/shiboken6/tests/samplebinding/snakecase_test.py new file mode 100644 index 000000000..893b9809e --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/snakecase_test.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $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$ +## +############################################################################# + +'''Test cases for snake case generation''' + +import os +import sys +import unittest + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from shiboken_paths import init_paths +init_paths() + +from sample import (SnakeCaseTest, SnakeCaseDerivedTest, + snake_case_global_function) + + +class OverrideTest(SnakeCaseDerivedTest): + def virtual_func(self): + return 4711 + + +class SnakeCaseTestCase(unittest.TestCase): + '''Test for SnakeCaseTest''' + def testMemberFunctions(self): + s = SnakeCaseTest() + self.assertEqual(s.test_function1(), 42) + + self.assertEqual(s.testFunctionDisabled(), 42) + + self.assertEqual(s.testFunctionBoth(), 42) + self.assertEqual(s.test_function_both(), 42) + + def virtualFunctions(self): + s = OverrideTest() + self.assertEqual(s.call_virtual_func(), 4711) + + def testMemberFields(self): + s = SnakeCaseTest() + old_value = s.test_field + s.test_field = old_value + 1 + self.assertEqual(s.test_field, old_value + 1) + + old_value = s.testFieldDisabled + s.testFieldDisabled = old_value + 1 + self.assertEqual(s.testFieldDisabled, old_value + 1) + + old_value = s.test_field_both + s.test_field_both = old_value + 1 + self.assertEqual(s.test_field_both, old_value + 1) + self.assertEqual(s.testFieldBoth, old_value + 1) + + def testGlobalFunction(self): + self.assertEqual(snake_case_global_function(), 42) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml index 6d9c842ac..d5a3edb34 100644 --- a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml @@ -1967,6 +1967,14 @@ + + + + + + + + -- cgit v1.2.3