diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-11-04 12:34:17 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-11-04 12:42:35 +0100 |
commit | 1704567d7ad0d21dd3b588af7aad40cd244c1799 (patch) | |
tree | 932b42988e482f01125dcbf4d470e9a56107689a /sources/shiboken2 | |
parent | 1d77cd18510412242384c5b37a00efd5b6ce8a26 (diff) | |
parent | c98ef56544cd54661b61529829caf6a799a1a94f (diff) |
Merge "Merge remote-tracking branch 'origin/5.14' into 5.15"
Diffstat (limited to 'sources/shiboken2')
54 files changed, 1885 insertions, 859 deletions
diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index c8a5fb336..c2a4c208e 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -30,6 +30,22 @@ parser/enumvalue.cpp xmlutils.cpp ) +find_package(Qt5XmlPatterns 5.12) +find_package(Qt5Xml 5.12) +find_package(LibXml2 2.6.32) +find_package(LibXslt 1.1.19) + +set(HAS_LIBXSLT 0) +if (LIBXSLT_FOUND AND LIBXML2_FOUND) + set(HAS_LIBXSLT 1) +endif() + +if(NOT Qt5XmlPatterns_FOUND AND NOT HAS_LIBXSLT) + set(DISABLE_DOCSTRINGS TRUE) + message(WARNING + "Documentation will not be built due to missing dependency (no Qt5XmlPatterns found).") +endif() + add_library(apiextractor STATIC ${apiextractor_SRC}) target_include_directories(apiextractor PRIVATE ${CLANG_EXTRA_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} @@ -68,6 +84,7 @@ target_compile_definitions(apiextractor PRIVATE CMAKE_CXX_COMPILER="${CMAKE_CXX_ set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) if (BUILD_TESTS) + find_package(Qt5Test 5.12 REQUIRED) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) enable_testing() add_subdirectory(tests) diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 9c930dcdc..ae3cdd86b 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()); } } } @@ -503,8 +512,12 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) ReportHandler::startProgress("Fixing class inheritance..."); for (AbstractMetaClass *cls : qAsConst(m_metaClasses)) { - if (!cls->isInterface() && !cls->isNamespace()) + if (!cls->isInterface() && !cls->isNamespace()) { setupInheritance(cls); + if (!cls->hasVirtualDestructor() && cls->baseClass() + && cls->baseClass()->hasVirtualDestructor()) + cls->setHasVirtualDestructor(true); + } } ReportHandler::startProgress("Detecting inconsistencies in class model..."); @@ -591,11 +604,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==")); @@ -1612,6 +1621,26 @@ void AbstractMetaBuilderPrivate::traverseEnums(const ScopeModelItem &scopeItem, } } +static void applyDefaultExpressionModifications(const FunctionModificationList &functionMods, + int i, AbstractMetaArgument *metaArg) +{ + // use replace/remove-default-expression for set default value + for (const auto &modification : functionMods) { + for (const auto &argumentModification : modification.argument_mods) { + if (argumentModification.index == i + 1) { + if (argumentModification.removedDefaultExpression) { + metaArg->setDefaultValueExpression(QString()); + break; + } + if (!argumentModification.replacedDefaultExpression.isEmpty()) { + metaArg->setDefaultValueExpression(argumentModification.replacedDefaultExpression); + break; + } + } + } + } +} + AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFunctionPtr &addedFunc) { return traverseFunction(addedFunc, nullptr); @@ -1670,20 +1699,13 @@ AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFu // Find the correct default values + const FunctionModificationList functionMods = metaFunction->modifications(metaClass); for (int i = 0; i < metaArguments.size(); ++i) { AbstractMetaArgument* metaArg = metaArguments.at(i); - //use relace-default-expression for set default value - QString replacedExpression; - if (metaClass) - replacedExpression = metaFunction->replacedDefaultExpression(metaClass, i + 1); - - if (!replacedExpression.isEmpty()) { - if (!metaFunction->removedDefaultExpression(metaClass, i + 1)) { - metaArg->setDefaultValueExpression(replacedExpression); - metaArg->setOriginalDefaultValueExpression(replacedExpression); - } - } + // use replace-default-expression for set default value + applyDefaultExpressionModifications(functionMods, i, metaArg); + metaArg->setOriginalDefaultValueExpression(metaArg->defaultValueExpression()); // appear unmodified } metaFunction->setOriginalAttributes(metaFunction->attributes()); @@ -1915,7 +1937,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio AbstractMetaType *type = nullptr; if (!returnType.isVoid()) { - type = translateType(returnType, currentClass, true, &errorMessage); + type = translateType(returnType, currentClass, {}, &errorMessage); if (!type) { const QString reason = msgUnmatchedReturnType(functionItem, errorMessage); qCWarning(lcShiboken, "%s", @@ -1951,7 +1973,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio return nullptr; } - AbstractMetaType *metaType = translateType(arg->type(), currentClass, true, &errorMessage); + AbstractMetaType *metaType = translateType(arg->type(), currentClass, {}, &errorMessage); if (!metaType) { // If an invalid argument has a default value, simply remove it if (arg->defaultValue()) { @@ -2002,35 +2024,16 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio const ArgumentModelItem &arg = arguments.at(i); AbstractMetaArgument* metaArg = metaArguments.at(i); - //use relace-default-expression for set default value - QString replacedExpression; - if (currentClass) { - replacedExpression = metaFunction->replacedDefaultExpression(currentClass, i + 1); - } else { - if (!functionMods.isEmpty()) { - QVector<ArgumentModification> argMods = functionMods.constFirst().argument_mods; - if (!argMods.isEmpty()) - replacedExpression = argMods.constFirst().replacedDefaultExpression; - } - } + const QString originalDefaultExpression = + fixDefaultValue(arg, metaArg->type(), metaFunction, currentClass, i); - bool hasDefaultValue = false; - if (arg->defaultValue() || !replacedExpression.isEmpty()) { - QString expr = arg->defaultValueExpression(); - expr = fixDefaultValue(arg, metaArg->type(), metaFunction, currentClass, i); - metaArg->setOriginalDefaultValueExpression(expr); + metaArg->setOriginalDefaultValueExpression(originalDefaultExpression); + metaArg->setDefaultValueExpression(originalDefaultExpression); - if (metaFunction->removedDefaultExpression(currentClass, i + 1)) { - expr.clear(); - } else if (!replacedExpression.isEmpty()) { - expr = replacedExpression; - } - metaArg->setDefaultValueExpression(expr); - hasDefaultValue = !expr.isEmpty(); - } + applyDefaultExpressionModifications(functionMods, i, metaArg); //Check for missing argument name - if (hasDefaultValue + if (!metaArg->defaultValueExpression().isEmpty() && !metaArg->hasName() && !metaFunction->isOperatorOverload() && !metaFunction->isSignal() @@ -2155,22 +2158,27 @@ static const TypeEntry* findTypeEntryUsingContext(const AbstractMetaClass* metaC AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typei, AbstractMetaClass *currentClass, - bool resolveType, + TranslateTypeFlags flags, QString *errorMessage) { - return translateTypeStatic(_typei, currentClass, this, resolveType, errorMessage); + return translateTypeStatic(_typei, currentClass, this, flags, errorMessage); } AbstractMetaType *AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo &_typei, AbstractMetaClass *currentClass, AbstractMetaBuilderPrivate *d, - bool resolveType, + TranslateTypeFlags flags, QString *errorMessageIn) { // 1. Test the type info without resolving typedefs in case this is present in the // type system + const bool resolveType = !flags.testFlag(AbstractMetaBuilder::DontResolveType); if (resolveType) { - if (AbstractMetaType *resolved = translateTypeStatic(_typei, currentClass, d, false, errorMessageIn)) + AbstractMetaType *resolved = + translateTypeStatic(_typei, currentClass, d, + flags | AbstractMetaBuilder::DontResolveType, + errorMessageIn); + if (resolved) return resolved; } @@ -2229,7 +2237,7 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo newInfo.setReferenceType(typeInfo.referenceType()); newInfo.setVolatile(typeInfo.isVolatile()); - AbstractMetaType *elementType = translateTypeStatic(newInfo, currentClass, d, true, &errorMessage); + AbstractMetaType *elementType = translateTypeStatic(newInfo, currentClass, d, flags, &errorMessage); if (!elementType) { if (errorMessageIn) { errorMessage.prepend(QLatin1String("Unable to translate array element: ")); @@ -2340,7 +2348,7 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo const auto &templateArguments = typeInfo.instantiations(); for (int t = 0, size = templateArguments.size(); t < size; ++t) { const TypeInfo &ti = templateArguments.at(t); - AbstractMetaType *targType = translateTypeStatic(ti, currentClass, d, true, &errorMessage); + AbstractMetaType *targType = translateTypeStatic(ti, currentClass, d, flags, &errorMessage); if (!targType) { if (errorMessageIn) *errorMessageIn = msgCannotTranslateTemplateArgument(t, ti, errorMessage); @@ -2362,17 +2370,17 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo AbstractMetaType *AbstractMetaBuilder::translateType(const TypeInfo &_typei, AbstractMetaClass *currentClass, - bool resolveType, + TranslateTypeFlags flags, QString *errorMessage) { return AbstractMetaBuilderPrivate::translateTypeStatic(_typei, currentClass, - nullptr, resolveType, + nullptr, flags, errorMessage); } AbstractMetaType *AbstractMetaBuilder::translateType(const QString &t, AbstractMetaClass *currentClass, - bool resolveType, + TranslateTypeFlags flags, QString *errorMessageIn) { QString errorMessage; @@ -2385,7 +2393,7 @@ AbstractMetaType *AbstractMetaBuilder::translateType(const QString &t, qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); return nullptr; } - return translateType(typeInfo, currentClass, resolveType, errorMessageIn); + return translateType(typeInfo, currentClass, flags, errorMessageIn); } qint64 AbstractMetaBuilderPrivate::findOutValueFromString(const QString &stringValue, bool &ok) @@ -2431,10 +2439,10 @@ QString AbstractMetaBuilderPrivate::fixDefaultValue(const ArgumentModelItem &ite AbstractMetaClass *implementingClass, int /* argumentIndex */) { - QString functionName = fnc->name(); - QString className = implementingClass ? implementingClass->qualifiedCppName() : QString(); - QString expr = item->defaultValueExpression(); + if (expr.isEmpty()) + return expr; + if (type) { if (type->isPrimitive()) { if (type->name() == QLatin1String("boolean")) { @@ -2508,11 +2516,12 @@ QString AbstractMetaBuilderPrivate::fixDefaultValue(const ArgumentModelItem &ite } } } else { + const QString className = implementingClass ? implementingClass->qualifiedCppName() : QString(); qCWarning(lcShiboken).noquote().nospace() << QStringLiteral("undefined type for default value '%3' of argument in function '%1', class '%2'") - .arg(functionName, className, item->defaultValueExpression()); + .arg(fnc->name(), className, item->defaultValueExpression()); - expr = QString(); + expr.clear(); } return expr; diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder.h index 1789ca2aa..93b9d9fd2 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.h +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.h @@ -90,13 +90,18 @@ public: void setSkipDeprecated(bool value); + enum TranslateTypeFlag { + DontResolveType = 0x1 + }; + Q_DECLARE_FLAGS(TranslateTypeFlags, TranslateTypeFlag); + static AbstractMetaType *translateType(const TypeInfo &_typei, AbstractMetaClass *currentClass = nullptr, - bool resolveType = true, + TranslateTypeFlags flags = {}, QString *errorMessage = nullptr); static AbstractMetaType *translateType(const QString &t, AbstractMetaClass *currentClass = nullptr, - bool resolveType = true, + TranslateTypeFlags flags = {}, QString *errorMessage = nullptr); @@ -109,6 +114,8 @@ private: AbstractMetaBuilderPrivate *d; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaBuilder::TranslateTypeFlags); + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const AbstractMetaBuilder &ab); #endif diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h index fec2eddb9..30df236d6 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h @@ -43,6 +43,8 @@ class TypeDatabase; class AbstractMetaBuilderPrivate { public: + using TranslateTypeFlags = AbstractMetaBuilder::TranslateTypeFlags; + Q_DISABLE_COPY(AbstractMetaBuilderPrivate) AbstractMetaBuilderPrivate(); @@ -56,7 +58,7 @@ public: void dumpLog() const; AbstractMetaClassList classesTopologicalSorted(const AbstractMetaClassList &classList, const Dependencies &additionalDependencies = Dependencies()) const; - ScopeModelItem popScope() { return m_scopes.takeLast(); } + NamespaceModelItem popScope() { return m_scopes.takeLast(); } void pushScope(const NamespaceModelItem &item); @@ -106,6 +108,7 @@ public: void checkFunctionModifications(); void registerHashFunction(const FunctionModelItem &functionItem, AbstractMetaClass *currentClass); + void registerToStringCapabilityIn(const NamespaceModelItem &namespaceItem); void registerToStringCapability(const FunctionModelItem &functionItem, AbstractMetaClass *currentClass); @@ -135,12 +138,12 @@ public: AbstractMetaType *translateType(const AddedFunction::TypeInfo &typeInfo); AbstractMetaType *translateType(const TypeInfo &type, AbstractMetaClass *currentClass, - bool resolveType = true, + TranslateTypeFlags flags = {}, QString *errorMessage = nullptr); static AbstractMetaType *translateTypeStatic(const TypeInfo &type, AbstractMetaClass *current, AbstractMetaBuilderPrivate *d = nullptr, - bool resolveType = true, + TranslateTypeFlags flags = {}, QString *errorMessageIn = nullptr); qint64 findOutValueFromString(const QString &stringValue, bool &ok); diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 4ca448d50..99e9d01a3 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -731,37 +731,6 @@ ArgumentOwner AbstractMetaFunction::argumentOwner(const AbstractMetaClass *cls, return ArgumentOwner(); } - -QString AbstractMetaFunction::replacedDefaultExpression(const AbstractMetaClass *cls, int key) const -{ - const FunctionModificationList &modifications = this->modifications(cls); - for (const FunctionModification &modification : modifications) { - for (const ArgumentModification &argumentModification : modification.argument_mods) { - if (argumentModification.index == key - && !argumentModification.replacedDefaultExpression.isEmpty()) { - return argumentModification.replacedDefaultExpression; - } - } - } - - return QString(); -} - -bool AbstractMetaFunction::removedDefaultExpression(const AbstractMetaClass *cls, int key) const -{ - const FunctionModificationList &modifications = this->modifications(cls); - for (const FunctionModification &modification : modifications) { - for (const ArgumentModification &argumentModification : modification.argument_mods) { - if (argumentModification.index == key - && argumentModification.removedDefaultExpression) { - return true; - } - } - } - - return false; -} - QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int key) const { const FunctionModificationList &modifications = this->modifications(declaringClass()); @@ -780,19 +749,6 @@ QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int return QString(); } -QString AbstractMetaFunction::argumentReplaced(int key) const -{ - const FunctionModificationList &modifications = this->modifications(declaringClass()); - for (const FunctionModification &modification : modifications) { - for (const ArgumentModification &argumentModification : modification.argument_mods) { - if (argumentModification.index == key && !argumentModification.replace_value.isEmpty()) - return argumentModification.replace_value; - } - } - - return QString(); -} - // FIXME If we remove a arg. in the method at the base class, it will not reflect here. bool AbstractMetaFunction::argumentRemoved(int key) const { diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index 7f0f9fbaa..077191471 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -671,7 +671,9 @@ public: } bool hasDefaultValueExpression() const - { return !m_originalExpression.isEmpty() || !m_expression.isEmpty(); } + { return !m_expression.isEmpty(); } + bool hasOriginalDefaultValueExpression() const + { return !m_originalExpression.isEmpty(); } bool hasUnmodifiedDefaultValueExpression() const { return !m_originalExpression.isEmpty() && m_originalExpression == m_expression; } bool hasModifiedDefaultValueExpression() const @@ -1020,8 +1022,6 @@ public: AbstractMetaFunction *copy() const; - QString replacedDefaultExpression(const AbstractMetaClass *cls, int idx) const; - bool removedDefaultExpression(const AbstractMetaClass *cls, int idx) const; QString conversionRule(TypeSystem::Language language, int idx) const; QVector<ReferenceCount> referenceCounts(const AbstractMetaClass *cls, int idx = -2) const; ArgumentOwner argumentOwner(const AbstractMetaClass *cls, int idx) const; @@ -1034,9 +1034,6 @@ public: bool isRemovedFromAllLanguages(const AbstractMetaClass *) const; bool isRemovedFrom(const AbstractMetaClass *, TypeSystem::Language language) const; bool argumentRemoved(int) const; - - QString argumentReplaced(int key) const; - /** * Verifies if any modification to the function is an inject code. * \return true if there is inject code modifications to the function. @@ -1695,9 +1692,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 @@ -1705,6 +1703,11 @@ public: return m_hasToStringCapability; } + uint toStringCapabilityIndirections() const + { + return m_toStringCapabilityIndirections; + } + bool deleteInMainThread() const; static AbstractMetaClass *findClass(const AbstractMetaClassList &classes, @@ -1757,6 +1760,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/ApiExtractor/tests/testabstractmetaclass.cpp b/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.cpp index 98b493c67..b85a022b3 100644 --- a/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testabstractmetaclass.cpp @@ -47,11 +47,12 @@ void TestAbstractMetaClass::testClassName() void TestAbstractMetaClass::testClassNameUnderNamespace() { const char* cppCode ="namespace Namespace { class ClassName {}; }\n"; - const char* xmlCode = "\ - <typesystem package=\"Foo\">\n\ - <namespace-type name=\"Namespace\"/>\n\ - <value-type name=\"Namespace::ClassName\"/>\n\ - </typesystem>\n"; + const char* xmlCode = R"XML( + <typesystem package="Foo"> + <namespace-type name="Namespace"> + <value-type name="ClassName"/> + </namespace-type> + </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); @@ -201,11 +202,12 @@ void TestAbstractMetaClass::testDefaultValues() class B {};\n\ void method(B b = B());\n\ };\n"; - const char* xmlCode = "\ - <typesystem package=\"Foo\">\n\ - <value-type name='A'/>\n\ - <value-type name='A::B'/>\n\ - </typesystem>\n"; + const char* xmlCode = R"XML( + <typesystem package="Foo"> + <value-type name='A'> + <value-type name='B'/> + </value-type> + </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); @@ -224,17 +226,17 @@ void TestAbstractMetaClass::testModifiedDefaultValues() class B {};\n\ void method(B b = B());\n\ };\n"; - const char* xmlCode = "\ - <typesystem package=\"Foo\">\n\ - <value-type name='A'>\n\ - <modify-function signature='method(A::B)'>\n\ - <modify-argument index='1'>\n\ - <replace-default-expression with='Hello'/>\n\ - </modify-argument>\n\ - </modify-function>\n\ - </value-type>\n\ - <value-type name='A::B'/>\n\ - </typesystem>\n"; + const char* xmlCode = R"XML( + <typesystem package="Foo"> + <value-type name='A'> + <modify-function signature='method(A::B)'> + <modify-argument index='1'> + <replace-default-expression with='Hello'/> + </modify-argument> + </modify-function> + <value-type name='B'/> + </value-type> + </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); @@ -254,11 +256,12 @@ void TestAbstractMetaClass::testInnerClassOfAPolymorphicOne() class B {};\n\ virtual void method();\n\ };\n"; - const char* xmlCode = "\ - <typesystem package=\"Foo\">\n\ - <object-type name='A'/>\n\ - <value-type name='A::B'/>\n\ - </typesystem>\n"; + const char* xmlCode = R"XML( + <typesystem package="Foo"> + <object-type name='A'> + <value-type name='B'/> + </object-type> + </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); @@ -281,11 +284,12 @@ void TestAbstractMetaClass::testForwardDeclaredInnerClass() public:\n\ void foo();\n\ };\n"; - const char xmlCode[] = "\ - <typesystem package=\"Foo\">\n\ - <value-type name='A'/>\n\ - <value-type name='A::B'/>\n\ - </typesystem>\n"; + const char xmlCode[] = R"XML( + <typesystem package="Foo"> + <value-type name='A'> + <value-type name='B'/> + </value-type> + </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); diff --git a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp index c438e0c37..aeca2d3f4 100644 --- a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp @@ -71,8 +71,8 @@ void TestCodeInjections::testReadFile() <value-type name='A'>\n\ <conversion-rule ") + attribute + QLatin1String("/>\n\ <inject-code class='target' ") + attribute + QLatin1String("/>\n\ + <value-type name='B'/>\n\ </value-type>\n\ - <value-type name='A::B'/>\n\ </typesystem>\n"); QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.toLocal8Bit().constData())); QVERIFY(!builder.isNull()); diff --git a/sources/shiboken2/ApiExtractor/tests/testdtorinformation.cpp b/sources/shiboken2/ApiExtractor/tests/testdtorinformation.cpp index f4204e9b9..0eee8af24 100644 --- a/sources/shiboken2/ApiExtractor/tests/testdtorinformation.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testdtorinformation.cpp @@ -69,6 +69,39 @@ void TestDtorInformation::testDtorIsVirtual() QCOMPARE(AbstractMetaClass::findClass(classes, QLatin1String("Subject"))->hasVirtualDestructor(), true); } +void TestDtorInformation::testDtorFromBaseIsVirtual() +{ + const char* cppCode = R"CPP(class ControlBase { public: ~ControlBase() {} }; +class Control : public ControlBase {}; +class SubjectBase { public: virtual ~SubjectBase() {} }; +class Subject : public SubjectBase {}; +)CPP"; + const char* xmlCode = R"XML(<typesystem package="Foo"><value-type name="ControlBase"/> +<value-type name="Control"/>" +<value-type name="SubjectBase"/>" +<value-type name="Subject"/> +</typesystem> +)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(!builder.isNull()); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.count(), 4); + + auto klass = AbstractMetaClass::findClass(classes, QLatin1String("ControlBase")); + QVERIFY(klass); + QVERIFY(!klass->hasVirtualDestructor()); + klass = AbstractMetaClass::findClass(classes, QLatin1String("Control")); + QVERIFY(klass); + QVERIFY(!klass->hasVirtualDestructor()); + + klass = AbstractMetaClass::findClass(classes, QLatin1String("SubjectBase")); + QVERIFY(klass); + QVERIFY(klass->hasVirtualDestructor()); + klass = AbstractMetaClass::findClass(classes, QLatin1String("Subject")); + QVERIFY(klass); + QVERIFY(klass->hasVirtualDestructor()); +} + void TestDtorInformation::testClassWithVirtualDtorIsPolymorphic() { const char* cppCode ="class Control { public: virtual ~Control() {} }; class Subject { protected: virtual ~Subject() {} };"; diff --git a/sources/shiboken2/ApiExtractor/tests/testdtorinformation.h b/sources/shiboken2/ApiExtractor/tests/testdtorinformation.h index 068ef4952..0a57dd8d1 100644 --- a/sources/shiboken2/ApiExtractor/tests/testdtorinformation.h +++ b/sources/shiboken2/ApiExtractor/tests/testdtorinformation.h @@ -40,6 +40,7 @@ private slots: void testDtorIsPrivate(); void testDtorIsProtected(); void testDtorIsVirtual(); + void testDtorFromBaseIsVirtual(); void testClassWithVirtualDtorIsPolymorphic(); }; diff --git a/sources/shiboken2/ApiExtractor/tests/testresolvetype.cpp b/sources/shiboken2/ApiExtractor/tests/testresolvetype.cpp index 2e5a5759a..2203f3648 100644 --- a/sources/shiboken2/ApiExtractor/tests/testresolvetype.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testresolvetype.cpp @@ -44,13 +44,15 @@ void TestResolveType::testResolveReturnTypeFromParentScope() C* method();\n\ };\n\ };"; - const char* xmlCode = "\n\ - <typesystem package='Foo'>\n\ - <namespace-type name='A'/>\n\ - <value-type name='A::B'/>\n\ - <value-type name='A::B::C'/>\n\ - <value-type name='A::D'/>\n\ - </typesystem>"; + const char* xmlCode = R"XML( + <typesystem package='Foo'> + <namespace-type name='A'> + <value-type name='B'> + <value-type name='C'/> + </value-type> + <value-type name='D'/> + </namespace-type> + </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); diff --git a/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp b/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp index 59b601f3b..5191cb38d 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp @@ -63,9 +63,10 @@ namespace Internet { <typesystem package='Package.Internet'> <load-typesystem name='%1' generate='no'/> <container-type name='QList' type='list'/> - <namespace-type name='Internet' generate='no'/> - <value-type name='Internet::Url'/> - <value-type name='Internet::Bookmarks'/> + <namespace-type name='Internet' generate='no'> + <value-type name='Url'/> + <value-type name='Bookmarks'/> + </namespace-type> </typesystem>)XML").arg(file.fileName()); QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, qPrintable(xmlCode1), false)); @@ -97,11 +98,12 @@ namespace Namespace { const char xmlCode[] = R"XML( <typesystem package="Package"> <container-type name='QList' type='list'/> - <namespace-type name='Namespace'/> - <enum-type name='Namespace::SomeEnum'/> + <namespace-type name='Namespace'> + <enum-type name='SomeEnum'/> + <object-type name='A' generate='no'/> + <object-type name='B'/> + </namespace-type> <object-type name='Base'/> - <object-type name='Namespace::A' generate='no'/> - <object-type name='Namespace::B'/> </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); @@ -211,8 +213,9 @@ struct List { const char xmlCode[] = R"XML( <typesystem package='Package'> - <container-type name='List' type='list'/> - <value-type name='List::Iterator'/> + <container-type name='List' type='list'> + <value-type name='Iterator'/> + </container-type> </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); @@ -324,11 +327,12 @@ template<SomeEnum type> struct Future {}; const char xmlCode[] = R"XML( <typesystem package='Package'> - <namespace-type name='Namespace'/> - <enum-type name='Namespace::SomeEnum'/> - <value-type name='Namespace::A' generate='no'/> - <value-type name='Namespace::B'/> - <value-type name='Namespace::Future' generate='no'/> + <namespace-type name='Namespace'> + <enum-type name='SomeEnum'/> + <value-type name='A' generate='no'/> + <value-type name='B'/> + <value-type name='Future' generate='no'/> + </namespace-type> </typesystem>)XML"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt index be1b4cd7f..c1349cae6 100644 --- a/sources/shiboken2/CMakeLists.txt +++ b/sources/shiboken2/CMakeLists.txt @@ -19,42 +19,9 @@ option(USE_PYTHON_VERSION "Use specific python version to build shiboken2." "") option(DISABLE_DOCSTRINGS "Disable documentation extraction." FALSE) find_package(Qt5 5.12 REQUIRED COMPONENTS Core) -find_package(Qt5Xml 5.12) -find_package(Qt5XmlPatterns 5.12) -find_package(LibXml2 2.6.32) -find_package(LibXslt 1.1.19) -if(BUILD_TESTS) - find_package(Qt5Test 5.12 REQUIRED) -endif() - -set(HAS_LIBXSLT 0) -if (LIBXSLT_FOUND AND LIBXML2_FOUND) - set(HAS_LIBXSLT 1) -endif() - -if(NOT Qt5XmlPatterns_FOUND AND NOT HAS_LIBXSLT) - set(DISABLE_DOCSTRINGS TRUE) - message(WARNING - "Documentation will not be built due to missing dependency (no Qt5XmlPatterns found).") -endif() - -# Don't display "up-to-date / install" messages when installing, to reduce visual clutter. -if (QUIET_BUILD) - set(CMAKE_INSTALL_MESSAGE NEVER) -endif() -# Override message not to display info messages when doing a quiet build. if (QUIET_BUILD) - function(message) - list(GET ARGV 0 MessageType) - if (MessageType STREQUAL FATAL_ERROR OR - MessageType STREQUAL SEND_ERROR OR - MessageType STREQUAL WARNING OR - MessageType STREQUAL AUTHOR_WARNING) - list(REMOVE_AT ARGV 0) - _message(${MessageType} "${ARGV}") - endif() - endfunction() + set_quiet_build() endif() if (USE_PYTHON_VERSION) @@ -63,115 +30,11 @@ else() shiboken_find_required_python() endif() -macro(get_python_arch) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: - import sys - print('64' if sys.maxsize > 2**31-1 else '32') - " - OUTPUT_VARIABLE PYTHON_ARCH - OUTPUT_STRIP_TRAILING_WHITESPACE) - message("PYTHON_ARCH: " ${PYTHON_ARCH}) -endmacro() - if (NOT PYTHON_ARCH) get_python_arch() endif() -macro(get_llvm_config) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: - import os - import sys - sys.path.append(os.path.realpath(os.path.join('${CMAKE_CURRENT_LIST_DIR}', '..', '..'))) - from build_scripts.utils import find_llvm_config - llvmConfig = find_llvm_config() - if llvmConfig: - print(llvmConfig) - " - OUTPUT_VARIABLE LLVM_CONFIG - OUTPUT_STRIP_TRAILING_WHITESPACE) - message("LLVM_CONFIG: " ${LLVM_CONFIG}) -endmacro() - -set(CLANG_DIR "") -set(CLANG_DIR_SOURCE "") - -set(clang_not_found_message "Unable to detect CLANG location by checking LLVM_INSTALL_DIR, \ - CLANG_INSTALL_DIR or running llvm-config.") - -if (DEFINED ENV{LLVM_INSTALL_DIR}) - set(CLANG_DIR $ENV{LLVM_INSTALL_DIR}) - string(REPLACE "_ARCH_" "${PYTHON_ARCH}" CLANG_DIR "${CLANG_DIR}") - set(CLANG_DIR_SOURCE "LLVM_INSTALL_DIR") -elseif (DEFINED ENV{CLANG_INSTALL_DIR}) - set(CLANG_DIR $ENV{CLANG_INSTALL_DIR}) - string(REPLACE "_ARCH_" "${PYTHON_ARCH}" CLANG_DIR "${CLANG_DIR}") - set(CLANG_DIR_SOURCE "CLANG_INSTALL_DIR") -else () - if (NOT LLVM_CONFIG) - get_llvm_config() - endif() - set(CLANG_DIR_SOURCE "${LLVM_CONFIG}") - if ("${CLANG_DIR_SOURCE}" STREQUAL "") - message(FATAL_ERROR "${clang_not_found_message}") - endif() - - EXEC_PROGRAM("${LLVM_CONFIG}" ARGS "--prefix" OUTPUT_VARIABLE CLANG_DIR) - if (NOT "${CLANG_DIR}" STREQUAL "") - EXEC_PROGRAM("${LLVM_CONFIG}" ARGS "--version" OUTPUT_VARIABLE CLANG_VERSION) - if (CLANG_VERSION VERSION_LESS 3.9) - message(FATAL_ERROR "libclang version 3.9 or higher is required (${LLVM_CONFIG} detected ${CLANG_VERSION} at ${CLANG_DIR}).") - endif() - endif() -endif() - -if ("${CLANG_DIR}" STREQUAL "") - message(FATAL_ERROR "${clang_not_found_message}") -elseif (NOT IS_DIRECTORY ${CLANG_DIR}) - message(FATAL_ERROR "${CLANG_DIR} detected by ${CLANG_DIR_SOURCE} does not exist.") -endif() - -# The non-development Debian / Ubuntu packages (e.g. libclang1-6.0) do not ship a -# libclang.so symlink, but only libclang-6.0.so.1 and libclang.so.1 (adjusted for version number). -# Thus searching for libclang would not succeed. -# The "libclang.so" symlink is shipped as part of the development package (libclang-6.0-dev) which -# we need anyway because of the headers. Thus we will search for libclang.so.1 also, and complain -# about the headers not being found in a check further down. This is more friendly to the user, -# so they don't scratch their head thinking that they have already installed the necessary package. -set(CLANG_LIB_NAMES clang libclang.so libclang.so.1) -if(MSVC) - set(CLANG_LIB_NAMES libclang) -endif() - -find_library(CLANG_LIBRARY NAMES ${CLANG_LIB_NAMES} HINTS ${CLANG_DIR}/lib) -if (NOT EXISTS ${CLANG_LIBRARY}) - string(REPLACE ";" ", " CLANG_LIB_NAMES_STRING "${CLANG_LIB_NAMES}") - message(FATAL_ERROR "Unable to find the Clang library in ${CLANG_DIR}.\ - Names tried: ${CLANG_LIB_NAMES_STRING}.") -endif() - -message(STATUS "CLANG: ${CLANG_DIR}, ${CLANG_LIBRARY} detected by ${CLANG_DIR_SOURCE}") - -set(CLANG_EXTRA_INCLUDES ${CLANG_DIR}/include) -set(CLANG_EXTRA_LIBRARIES ${CLANG_LIBRARY}) - -# Check if one of the required clang headers is found. Error out early at CMake time instead of -# compile time if not found. -# It can happen that a user uses a distro-provided libclang.so, but no development header package -# was installed (e.g. libclang-6.0-dev on Ubuntu). -set(CMAKE_REQUIRED_INCLUDES ${CLANG_EXTRA_INCLUDES}) -set(CLANG_HEADER_FILE_TO_CHECK "clang-c/Index.h") -check_include_file_cxx(${CLANG_HEADER_FILE_TO_CHECK} CLANG_INCLUDE_FOUND) -unset(CMAKE_REQUIRED_INCLUDES) -if (NOT CLANG_INCLUDE_FOUND) - # Need to unset so that when installing the package, CMake doesn't complain that the header - # still isn't found. - unset(CLANG_INCLUDE_FOUND CACHE) - message(FATAL_ERROR "Unable to find required Clang header file ${CLANG_HEADER_FILE_TO_CHECK} \ - in ${CLANG_DIR}/include. Perhaps you forgot to install the clang development header \ - package? (e.g. libclang-6.0-dev)") -endif() +setup_clang() set(SHIBOKEN_VERSION_FILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/shiboken_version.py") set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS @@ -209,29 +72,6 @@ message("PYTHONINTERP_FOUND: " ${PYTHONINTERP_FOUND}) message("PYTHON_EXECUTABLE: " ${PYTHON_EXECUTABLE}) message("PYTHON_VERSION: " ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}) -macro(get_python_extension_suffix) - # Result of imp.get_suffixes() depends on the platform, but generally looks something like: - # [('.cpython-34m-x86_64-linux-gnu.so', 'rb', 3), ('.cpython-34m.so', 'rb', 3), - # ('.abi3.so', 'rb', 3), ('.so', 'rb', 3), ('.py', 'r', 1), ('.pyc', 'rb', 2)] - # We pick the first most detailed one, strip of the file extension part. - - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: - import imp, re - first_suffix = imp.get_suffixes()[0][0] - res = re.search(r'^(.+)\\.', first_suffix) - if res: - first_suffix = res.group(1) - else: - first_suffix = '' - print(first_suffix) - " - OUTPUT_VARIABLE PYTHON_EXTENSION_SUFFIX - OUTPUT_STRIP_TRAILING_WHITESPACE) - message("PYTHON_EXTENSION_SUFFIX: " ${PYTHON_EXTENSION_SUFFIX}) -endmacro() - - if (NOT PYTHON_EXTENSION_SUFFIX) get_python_extension_suffix() endif() @@ -242,42 +82,15 @@ set(PYTHON_LIMITED_API 0) shiboken_check_if_limited_api() if (PYTHON_LIMITED_API) - if (WIN32 AND NOT EXISTS "${PYTHON_LIMITED_LIBRARIES}") - message(FATAL_ERROR "The Limited API was enabled, but ${PYTHON_LIMITED_LIBRARIES} was not found!") - endif() - message(STATUS "******************************************************") - message(STATUS "** Limited API enabled ${PYTHON_LIMITED_LIBRARIES}") - message(STATUS "******************************************************") + set_limited_api() endif() if (NOT PYTHON_CONFIG_SUFFIX) - if (PYTHON_VERSION_MAJOR EQUAL 2) - set(PYTHON_CONFIG_SUFFIX "-python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") - if (PYTHON_EXTENSION_SUFFIX) - set(PYTHON_CONFIG_SUFFIX "${PYTHON_CONFIG_SUFFIX}${PYTHON_EXTENSION_SUFFIX}") - endif() - elseif (PYTHON_VERSION_MAJOR EQUAL 3) - if (PYTHON_LIMITED_API) - if(WIN32) - set(PYTHON_EXTENSION_SUFFIX "") - else() - set(PYTHON_EXTENSION_SUFFIX ".abi3") - endif() - set(PYTHON_CONFIG_SUFFIX ".abi3") - else() - set(PYTHON_CONFIG_SUFFIX "${PYTHON_EXTENSION_SUFFIX}") - endif() - endif() + set_python_config_suffix() endif() if (NOT PYTHON_SHARED_LIBRARY_SUFFIX) - set(PYTHON_SHARED_LIBRARY_SUFFIX "${PYTHON_CONFIG_SUFFIX}") - - # Append a "v" to disambiguate the python version and the shiboken version in the - # shared library file name. - if (APPLE AND PYTHON_VERSION_MAJOR EQUAL 2) - set(PYTHON_SHARED_LIBRARY_SUFFIX "${PYTHON_SHARED_LIBRARY_SUFFIX}v") - endif() + set_python_shared_library_suffix() endif() if (NOT PYTHON_CONFIG_SUFFIX) @@ -291,39 +104,10 @@ message(STATUS "PYTHON_SHARED_LIBRARY_SUFFIX: ${PYTHON_SHARED_LIBRARY_SUFFIX}") if (NOT PYTHON_SITE_PACKAGES) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: - from distutils import sysconfig - from os.path import sep - print(sysconfig.get_python_lib(1, 0, prefix='${CMAKE_INSTALL_PREFIX}').replace(sep, '/')) - " - OUTPUT_VARIABLE PYTHON_SITE_PACKAGES - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (NOT PYTHON_SITE_PACKAGES) - message(FATAL_ERROR "Could not detect Python module installation directory.") - elseif (APPLE) - message(STATUS "!!! The generated bindings will be installed on ${PYTHON_SITE_PACKAGES}, is it right!?") - endif() -endif() - -if(MSVC) - # Qt5: this flag has changed from /Zc:wchar_t- in Qt4.X - set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") -else() - if(CMAKE_HOST_UNIX AND NOT CYGWIN) - add_definitions(-fPIC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fvisibility=hidden -Wno-strict-aliasing") - endif() - set(CMAKE_CXX_FLAGS_DEBUG "-g") - option(ENABLE_GCC_OPTIMIZATION "Enable specific GCC flags to optimization library size and performance. Only available on Release Mode" 0) - if(ENABLE_GCC_OPTIMIZATION) - set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -Wl,-O1") - if(NOT CMAKE_HOST_APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--hash-style=gnu") - endif() - endif() + set_python_site_packages() endif() +set_cmake_cxx_flags() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D QT_NO_CAST_FROM_ASCII -D QT_NO_CAST_TO_ASCII") # Force usage of the C++11 standard, without a silent fallback @@ -332,8 +116,10 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) -set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) -set(BIN_INSTALL_DIR "bin" CACHE PATH "The subdirectory relative to the install prefix where dlls will be installed (default is /bin)" FORCE) +set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install \ + prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) +set(BIN_INSTALL_DIR "bin" CACHE PATH "The subdirectory relative to the install prefix where \ + dlls will be installed (default is /bin)" FORCE) if (WIN32) set(PATH_SEP "\;") @@ -342,26 +128,17 @@ else() endif() if(CMAKE_HOST_APPLE) - set(OSX_USE_LIBCPP "OFF" CACHE BOOL "Explicitly link the libc++ standard library (useful for osx deployment targets lower than 10.9.") + set(OSX_USE_LIBCPP "OFF" CACHE BOOL "Explicitly link the libc++ standard library \ + (useful for osx deployment targets lower than 10.9.") if(OSX_USE_LIBCPP) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") endif() endif() -# Build with Address sanitizer enabled if requested. This may break things, so use at your own risk. +# Build with Address sanitizer enabled if requested. +# This may break things, so use at your own risk. if (SANITIZE_ADDRESS AND NOT MSVC) - # Currently this does not check that the clang / gcc version used supports Address sanitizer, - # so once again, use at your own risk. - add_compile_options("-fsanitize=address" "-g" "-fno-omit-frame-pointer") - # We need to add the sanitize address option to all linked executables / shared libraries - # so that proper sanitizer symbols are linked in. - # - # Note that when running tests, you may need to set an additional environment variable - # in set_tests_properties for shiboken2 / pyside tests, or exported in your shell. Address - # sanitizer will tell you what environment variable needs to be exported. For example: - # export DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/ - # ./XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib - set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_STANDARD_LIBRARIES} -fsanitize=address") + set_sanitize_address() endif() # Detect if the python libs were compiled in debug mode @@ -410,23 +187,12 @@ execute_process( set(SHIBOKEN_BUILD_TYPE "${CMAKE_BUILD_TYPE}") if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(SHIBOKEN_BUILD_TYPE "Debug") - - if(NOT PYTHON_DEBUG_LIBRARIES) - message(WARNING "Python debug shared library not found; assuming python was built with shared library support disabled.") - endif() - - if(NOT PYTHON_WITH_DEBUG) - message(WARNING "Compiling shiboken2 with debug enabled, but the python executable was not compiled with debug support.") - else() - set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION " -DPy_DEBUG") - endif() - - if (PYTHON_WITH_COUNT_ALLOCS) - set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION "${SBK_PKG_CONFIG_PY_DEBUG_DEFINITION} -DCOUNT_ALLOCS") - endif() + set_debug_build() endif() +###################################################################### +# Adding sub directories to build +###################################################################### add_subdirectory(ApiExtractor) set(generator_plugin_DIR ${LIB_INSTALL_DIR}/generatorrunner${generator_SUFFIX}) @@ -451,7 +217,7 @@ if (Qt5Core_FOUND AND PYTHONINTERP_FOUND) add_subdirectory(tests) endif() else() - message(WARNING "Some dependencies were not found, shiboken2 generator compilation disabled!") + message(WARNING "Some dependencies were not found: shiboken2 generator compilation disabled!") endif() add_subdirectory(data) diff --git a/sources/shiboken2/data/shiboken_helpers.cmake b/sources/shiboken2/data/shiboken_helpers.cmake index f4dd4d5dc..8111fa61f 100644 --- a/sources/shiboken2/data/shiboken_helpers.cmake +++ b/sources/shiboken2/data/shiboken_helpers.cmake @@ -1,5 +1,274 @@ include(CMakeParseArguments) +macro(set_python_shared_library_suffix) + set(PYTHON_SHARED_LIBRARY_SUFFIX "${PYTHON_CONFIG_SUFFIX}") + + # Append a "v" to disambiguate the python version and the shiboken version in the + # shared library file name. + if (APPLE AND PYTHON_VERSION_MAJOR EQUAL 2) + set(PYTHON_SHARED_LIBRARY_SUFFIX "${PYTHON_SHARED_LIBRARY_SUFFIX}v") + endif() +endmacro() + +macro(set_limited_api) + if (WIN32 AND NOT EXISTS "${PYTHON_LIMITED_LIBRARIES}") + message(FATAL_ERROR "The Limited API was enabled, but ${PYTHON_LIMITED_LIBRARIES} was not found!") + endif() + message(STATUS "******************************************************") + message(STATUS "** Limited API enabled ${PYTHON_LIMITED_LIBRARIES}") + message(STATUS "******************************************************") +endmacro() + +macro(set_debug_build) + set(SHIBOKEN_BUILD_TYPE "Debug") + + if(NOT PYTHON_DEBUG_LIBRARIES) + message(WARNING "Python debug shared library not found; \ + assuming python was built with shared library support disabled.") + endif() + + if(NOT PYTHON_WITH_DEBUG) + message(WARNING "Compiling shiboken2 with debug enabled, \ + but the python executable was not compiled with debug support.") + else() + set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION " -DPy_DEBUG") + endif() + + if (PYTHON_WITH_COUNT_ALLOCS) + set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION "${SBK_PKG_CONFIG_PY_DEBUG_DEFINITION} -DCOUNT_ALLOCS") + endif() +endmacro() + +macro(setup_sanitize_address) + # Currently this does not check that the clang / gcc version used supports Address sanitizer, + # so once again, use at your own risk. + add_compile_options("-fsanitize=address" "-g" "-fno-omit-frame-pointer") + # We need to add the sanitize address option to all linked executables / shared libraries + # so that proper sanitizer symbols are linked in. + # + # Note that when running tests, you may need to set an additional environment variable + # in set_tests_properties for shiboken2 / pyside tests, or exported in your shell. Address + # sanitizer will tell you what environment variable needs to be exported. For example: + # export DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/ + # ./XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_STANDARD_LIBRARIES} -fsanitize=address") +endmacro() + +macro(set_cmake_cxx_flags) +if(MSVC) + # Qt5: this flag has changed from /Zc:wchar_t- in Qt4.X + set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") + #set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DNOCOLOR /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") # XXX +else() + if(CMAKE_HOST_UNIX AND NOT CYGWIN) + add_definitions(-fPIC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fvisibility=hidden -Wno-strict-aliasing") + endif() + set(CMAKE_CXX_FLAGS_DEBUG "-g") + option(ENABLE_GCC_OPTIMIZATION "Enable specific GCC flags to optimization library \ + size and performance. Only available on Release Mode" 0) + if(ENABLE_GCC_OPTIMIZATION) + set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -Wl,-O1") + if(NOT CMAKE_HOST_APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--hash-style=gnu") + endif() + endif() + if(CMAKE_HOST_APPLE) + # ALTERNATIVE_QT_INCLUDE_DIR is deprecated, because CMake takes care of finding the proper + # include folders using the qmake found in the environment. Only use it for now in case + # something goes wrong with the cmake process. + if(ALTERNATIVE_QT_INCLUDE_DIR AND NOT QT_INCLUDE_DIR) + set(QT_INCLUDE_DIR ${ALTERNATIVE_QT_INCLUDE_DIR}) + endif() + endif() +endif() + +endmacro() + +macro(set_python_site_packages) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + from distutils import sysconfig + from os.path import sep + print(sysconfig.get_python_lib(1, 0, prefix='${CMAKE_INSTALL_PREFIX}').replace(sep, '/')) + " + OUTPUT_VARIABLE PYTHON_SITE_PACKAGES + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (NOT PYTHON_SITE_PACKAGES) + message(FATAL_ERROR "Could not detect Python module installation directory.") + elseif (APPLE) + message(STATUS "!!! The generated bindings will be installed on ${PYTHON_SITE_PACKAGES}, \ + is it right!?") + endif() +endmacro() + +macro(set_python_config_suffix) + if (PYTHON_VERSION_MAJOR EQUAL 2) + set(PYTHON_CONFIG_SUFFIX "-python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") + if (PYTHON_EXTENSION_SUFFIX) + set(PYTHON_CONFIG_SUFFIX "${PYTHON_CONFIG_SUFFIX}${PYTHON_EXTENSION_SUFFIX}") + endif() + elseif (PYTHON_VERSION_MAJOR EQUAL 3) + if (PYTHON_LIMITED_API) + if(WIN32) + set(PYTHON_EXTENSION_SUFFIX "") + else() + set(PYTHON_EXTENSION_SUFFIX ".abi3") + endif() + set(PYTHON_CONFIG_SUFFIX ".abi3") + else() + set(PYTHON_CONFIG_SUFFIX "${PYTHON_EXTENSION_SUFFIX}") + endif() + endif() +endmacro() + +macro(setup_clang) + set(CLANG_DIR "") + set(CLANG_DIR_SOURCE "") + + set(clang_not_found_message "Unable to detect CLANG location by checking LLVM_INSTALL_DIR, \ + CLANG_INSTALL_DIR or running llvm-config.") + + if (DEFINED ENV{LLVM_INSTALL_DIR}) + set(CLANG_DIR $ENV{LLVM_INSTALL_DIR}) + string(REPLACE "_ARCH_" "${PYTHON_ARCH}" CLANG_DIR "${CLANG_DIR}") + set(CLANG_DIR_SOURCE "LLVM_INSTALL_DIR") + elseif (DEFINED ENV{CLANG_INSTALL_DIR}) + set(CLANG_DIR $ENV{CLANG_INSTALL_DIR}) + string(REPLACE "_ARCH_" "${PYTHON_ARCH}" CLANG_DIR "${CLANG_DIR}") + set(CLANG_DIR_SOURCE "CLANG_INSTALL_DIR") + else () + if (NOT LLVM_CONFIG) + get_llvm_config() + endif() + set(CLANG_DIR_SOURCE "${LLVM_CONFIG}") + if ("${CLANG_DIR_SOURCE}" STREQUAL "") + message(FATAL_ERROR "${clang_not_found_message}") + endif() + + EXEC_PROGRAM("${LLVM_CONFIG}" ARGS "--prefix" OUTPUT_VARIABLE CLANG_DIR) + if (NOT "${CLANG_DIR}" STREQUAL "") + EXEC_PROGRAM("${LLVM_CONFIG}" ARGS "--version" OUTPUT_VARIABLE CLANG_VERSION) + if (CLANG_VERSION VERSION_LESS 3.9) + message(FATAL_ERROR "libclang version 3.9 or higher is required \ + (${LLVM_CONFIG} detected ${CLANG_VERSION} at ${CLANG_DIR}).") + endif() + endif() + endif() + + if ("${CLANG_DIR}" STREQUAL "") + message(FATAL_ERROR "${clang_not_found_message}") + elseif (NOT IS_DIRECTORY ${CLANG_DIR}) + message(FATAL_ERROR "${CLANG_DIR} detected by ${CLANG_DIR_SOURCE} does not exist.") + endif() + + # The non-development Debian / Ubuntu packages (e.g. libclang1-6.0) do not ship a + # libclang.so symlink, but only libclang-6.0.so.1 and libclang.so.1 (adjusted for version number). + # Thus searching for libclang would not succeed. + # The "libclang.so" symlink is shipped as part of the development package (libclang-6.0-dev) which + # we need anyway because of the headers. Thus we will search for libclang.so.1 also, and complain + # about the headers not being found in a check further down. This is more friendly to the user, + # so they don't scratch their head thinking that they have already installed the necessary package. + set(CLANG_LIB_NAMES clang libclang.so libclang.so.1) + if(MSVC) + set(CLANG_LIB_NAMES libclang) + endif() + + find_library(CLANG_LIBRARY NAMES ${CLANG_LIB_NAMES} HINTS ${CLANG_DIR}/lib) + if (NOT EXISTS ${CLANG_LIBRARY}) + string(REPLACE ";" ", " CLANG_LIB_NAMES_STRING "${CLANG_LIB_NAMES}") + message(FATAL_ERROR "Unable to find the Clang library in ${CLANG_DIR}.\ + Names tried: ${CLANG_LIB_NAMES_STRING}.") + endif() + + message(STATUS "CLANG: ${CLANG_DIR}, ${CLANG_LIBRARY} detected by ${CLANG_DIR_SOURCE}") + + set(CLANG_EXTRA_INCLUDES ${CLANG_DIR}/include) + set(CLANG_EXTRA_LIBRARIES ${CLANG_LIBRARY}) + + # Check if one of the required clang headers is found. Error out early at CMake time instead of + # compile time if not found. + # It can happen that a user uses a distro-provided libclang.so, but no development header package + # was installed (e.g. libclang-6.0-dev on Ubuntu). + set(CMAKE_REQUIRED_INCLUDES ${CLANG_EXTRA_INCLUDES}) + set(CLANG_HEADER_FILE_TO_CHECK "clang-c/Index.h") + check_include_file_cxx(${CLANG_HEADER_FILE_TO_CHECK} CLANG_INCLUDE_FOUND) + unset(CMAKE_REQUIRED_INCLUDES) + if (NOT CLANG_INCLUDE_FOUND) + # Need to unset so that when installing the package, CMake doesn't complain that the header + # still isn't found. + unset(CLANG_INCLUDE_FOUND CACHE) + message(FATAL_ERROR "Unable to find required Clang header file ${CLANG_HEADER_FILE_TO_CHECK} \ + in ${CLANG_DIR}/include. Perhaps you forgot to install the clang development header \ + package? (e.g. libclang-6.0-dev)") + endif() +endmacro() + +macro(set_quiet_build) + # Don't display "up-to-date / install" messages when installing, to reduce visual clutter. + set(CMAKE_INSTALL_MESSAGE NEVER PARENT_SCOPE) + # Override message not to display info messages when doing a quiet build. + function(message) + list(GET ARGV 0 MessageType) + if (MessageType STREQUAL FATAL_ERROR OR + MessageType STREQUAL SEND_ERROR OR + MessageType STREQUAL WARNING OR + MessageType STREQUAL AUTHOR_WARNING) + list(REMOVE_AT ARGV 0) + _message(${MessageType} "${ARGV}") + endif() + endfunction() +endmacro() + +macro(get_python_extension_suffix) + # Result of imp.get_suffixes() depends on the platform, but generally looks something like: + # [('.cpython-34m-x86_64-linux-gnu.so', 'rb', 3), ('.cpython-34m.so', 'rb', 3), + # ('.abi3.so', 'rb', 3), ('.so', 'rb', 3), ('.py', 'r', 1), ('.pyc', 'rb', 2)] + # We pick the first most detailed one, strip of the file extension part. + + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + import imp, re + first_suffix = imp.get_suffixes()[0][0] + res = re.search(r'^(.+)\\.', first_suffix) + if res: + first_suffix = res.group(1) + else: + first_suffix = '' + print(first_suffix) + " + OUTPUT_VARIABLE PYTHON_EXTENSION_SUFFIX + OUTPUT_STRIP_TRAILING_WHITESPACE) + message("PYTHON_EXTENSION_SUFFIX: " ${PYTHON_EXTENSION_SUFFIX}) +endmacro() + +macro(get_llvm_config) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + import os + import sys + sys.path.append(os.path.realpath(os.path.join('${CMAKE_CURRENT_LIST_DIR}', '..', '..'))) + from build_scripts.utils import find_llvm_config + llvmConfig = find_llvm_config() + if llvmConfig: + print(llvmConfig) + " + OUTPUT_VARIABLE LLVM_CONFIG + OUTPUT_STRIP_TRAILING_WHITESPACE) + message("LLVM_CONFIG: " ${LLVM_CONFIG}) +endmacro() + +macro(get_python_arch) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + import sys + print('64' if sys.maxsize > 2**31-1 else '32') + " + OUTPUT_VARIABLE PYTHON_ARCH + OUTPUT_STRIP_TRAILING_WHITESPACE) + message("PYTHON_ARCH: " ${PYTHON_ARCH}) +endmacro() + macro(shiboken_parse_all_arguments prefix type flags options multiopts) cmake_parse_arguments(${prefix} "${flags}" "${options}" "${multiopts}" ${ARGN}) if(DEFINED ${prefix}_UNPARSED_ARGUMENTS) diff --git a/sources/shiboken2/doc/_themes/pysidedocs/static/pyside.css b/sources/shiboken2/doc/_themes/pysidedocs/static/pyside.css index 956e3113b..23e460262 100644 --- a/sources/shiboken2/doc/_themes/pysidedocs/static/pyside.css +++ b/sources/shiboken2/doc/_themes/pysidedocs/static/pyside.css @@ -3,8 +3,8 @@ /* -- admonitions -- */ div.admonition { - margin: 1em 0 1em; - padding: 7px; + margin: 1.5em 0 1.5em; + padding: 0; } div.admonition dt { @@ -19,6 +19,23 @@ p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } + +div.admonition code { + font-family: inherit; +} + +p.admonition-title + p { + padding-left: 1em; +} + +div.admonition a:after { + content: ', '; +} + +div.admonition a:last-child:after { + content: ''; +} + .body { width: 100% } @@ -35,18 +52,21 @@ div.body p.centered { margin-top: 25px; } +div.warning, div.seealso, div.note { + padding: 6px 0px 6px 10px; + border: none; +} + div.warning { background-color: #ffe4e4; - border: 1px solid #f66; } div.seealso { - background-color: #ffffcc; - border: 1px solid #ffff66; + background-color: #fff2d6; } div.note { - border: 1px solid #e3e3e3; + background-color: #f3f3f4; } table.docutils { @@ -91,7 +111,7 @@ h2 em { .body blockquote { border: none; padding-left: 0; - margin-bottom: 2em; + margin-bottom: 1.5em; } .sphinxsidebar { @@ -146,11 +166,8 @@ h2 em { display: block; padding: 5px; margin: 0 10px 10px 0; - border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:6px; - -webkit-border-radius:6px; - -khtml-border-radius:6px; + border: none; + background-color: #e2e2e2; } .section .docutils.container td { @@ -290,14 +307,23 @@ tt.descname { } #functions ul, #virtual-functions ul, #slots ul, #signals ul, #static-functions ul { - list-style: none; - margin: 0px; - padding: 10px; + margin: 0; + padding: 6px; border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:10px; - -webkit-border-radius:10px; - -khtml-border-radius:10px; + border-radius: 0; + background-color: #e2e2e2; +} + +#functions p, #virtual-functions p, #slots p, #signals p, #static-functions p { + margin: 0; + padding: 0; +} + +#functions li, #virtual-functions li, #slots li, #signals li, #static-functions li { + list-style: none; + margin: 5px; + padding: 0; + font-size: 90%; } #synopsis span.pre { @@ -312,11 +338,94 @@ tt.descname { margin: 0px; margin-bottom: 10px; padding: 10px; - border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:10px; - -webkit-border-radius:10px; - -khtml-border-radius:10px; + font-weight: bold; + background-color: #e2e2e2; + border: none; + border-radius: 0; +} + +#detailed-description dd > blockquote, +#detailed-description dd > .field-list { + font-family: 'Droid Sans Mono'; + font-size: small; + border-left: 10px solid #e2e2e2; + padding-left: 10px; + margin-bottom: 1.5em; +} + +#detailed-description dd > blockquote blockquote { + border: none; + padding: 0; +} + +#detailed-description .class .field-odd, +#detailed-description .method .field-odd, +#detailed-description .staticmethod .field-odd, +#detailed-description .attribute .field-odd { + margin: 0; + padding: 1px 0 0 0; + background-color: #ffffff; + +} + +#detailed-description .class .field-even, +#detailed-description .method .field-even, +#detailed-description .staticmethod .field-even, +#detailed-description .attribute .field-even { + margin: 0; + padding: 1px 0 0 0; + background-color: #ffffff; +} + +#detailed-description .class .field-odd li, +#detailed-description .method .field-odd li, +#detailed-description .staticmethod .field-odd li, +#detailed-description .attribute .field-odd li { + list-style: none; + margin: 0; + padding: 0; + +} + +#detailed-description .class .field-even li, +#detailed-description .method .field-even li, +#detailed-description .staticmethod .field-even li, +#detailed-description .attribute .field-even li { + list-style: none; + margin: 0; + padding: 0; +} + +#detailed-description .class .field-odd p, +#detailed-description .method .field-odd p, +#detailed-description .staticmethod .field-odd p, +#detailed-description .attribute .field-odd p{ + margin: 0; + margin-left: 20px; + +} + +#detailed-description .class .field-even p, +#detailed-description .method .field-even p, +#detailed-description .staticmethod .field-even p, +#detailed-description .attribute .field-even p{ + margin: 0; + margin-left: 20px; +} + +#detailed-description .class .field-odd p:last-child, +#detailed-description .method .field-odd p:last-child, +#detailed-description .staticmethod .field-odd p:last-child, +#detailed-description .attribute .field-odd p:last-child { + margin-bottom: 10px; + +} + +#detailed-description .class .field-even p:last-child, +#detailed-description .method .field-even p:last-child, +#detailed-description .staticmethod .field-even p:last-child, +#detailed-description .attribute .field-even p:last-child{ + margin-bottom: 10px; } .document dl.attribute, @@ -333,6 +442,10 @@ tt.descname { padding-left: 1em; } +#detailed-description .attribute td:nth-child(1) { + font-family: 'Droid Sans Mono'; +} + /* Qt theme */ #navbar { position:fixed; @@ -1056,9 +1169,15 @@ div.multi-column div { } .col-2 h2,.toc h3,.sidebar-content h2, .sidebar-content h3,.sectionlist h2, +.sphinxsidebar { + position: fixed; + overflow: scroll; + overflow-x: hidden; + overflow-y: hidden; +} .sphinxsidebar h3 { - font-weight:400; - margin-bottom:1em + font-weight: bold; + margin-bottom:1em; } .toc h3 a { color:#404244 @@ -1450,6 +1569,10 @@ span.wrap:active { code,.codelike { font-family:"Droid Sans Mono" } +#detailed-description .function dt > code, +#detailed-description .function dt > em { + font-weight:bold +} h3.fn code { font-size:0.75em; float:right; diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp index 6da9fd933..7c6e921c7 100644 --- a/sources/shiboken2/generator/generator.cpp +++ b/sources/shiboken2/generator/generator.cpp @@ -760,7 +760,7 @@ DefaultValue Generator::minimalConstructor(const AbstractMetaClass *metaClass) c bool simple = true; bool suitable = true; for (int i = 0, size = arguments.size(); - suitable && i < size && !arguments.at(i)->hasDefaultValueExpression(); ++i) { + suitable && i < size && !arguments.at(i)->hasOriginalDefaultValueExpression(); ++i) { const AbstractMetaArgument *arg = arguments.at(i); const TypeEntry *aType = arg->type()->typeEntry(); suitable &= aType != cType; @@ -777,11 +777,12 @@ DefaultValue Generator::minimalConstructor(const AbstractMetaClass *metaClass) c bool ok = true; for (int i =0, size = arguments.size(); ok && i < size; ++i) { const AbstractMetaArgument *arg = arguments.at(i); - if (arg->hasDefaultValueExpression()) { - if (arg->hasModifiedDefaultValueExpression()) - args << arg->defaultValueExpression(); // Spell out modified values + if (arg->hasModifiedDefaultValueExpression()) { + args << arg->defaultValueExpression(); // Spell out modified values break; } + if (arg->hasOriginalDefaultValueExpression()) + break; auto argValue = minimalConstructor(arg->type()); ok &= argValue.isValid(); args << argValue.constructorParameter(); diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp index 9cad400f3..86abf21b0 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp @@ -349,6 +349,7 @@ QtXmlToSphinx::QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, cons m_handlerMap.insert(QLatin1String("skipto"), &QtXmlToSphinx::handleIgnoredTag); m_handlerMap.insert(QLatin1String("target"), &QtXmlToSphinx::handleTargetTag); m_handlerMap.insert(QLatin1String("page"), &QtXmlToSphinx::handlePageTag); + m_handlerMap.insert(QLatin1String("group"), &QtXmlToSphinx::handlePageTag); // useless tags m_handlerMap.insert(QLatin1String("description"), &QtXmlToSphinx::handleUselessTag); @@ -1023,6 +1024,8 @@ QtXmlToSphinx::LinkContext *QtXmlToSphinx::handleLinkStart(const QString &type, result->type = LinkContext::External; else result->type = LinkContext::Reference; + } else if (type == QLatin1String("external")) { + result->type = LinkContext::External; } else { result->type = LinkContext::Reference; } @@ -1228,14 +1231,11 @@ void QtXmlToSphinx::handlePageTag(QXmlStreamReader &reader) m_output << rstLabel(title.toString()); const QStringRef fullTitle = reader.attributes().value(fullTitleAttribute()); - if (!fullTitle.isEmpty()) { - const int size = writeEscapedRstText(m_output, fullTitle); - m_output << endl << Pad('*', size) << endl << endl; - } + const int size = fullTitle.isEmpty() + ? writeEscapedRstText(m_output, title) + : writeEscapedRstText(m_output, fullTitle); - const QStringRef brief = reader.attributes().value(briefAttribute()); - if (!brief.isEmpty()) - m_output << escape(brief) << endl << endl; + m_output << endl << Pad('*', size) << endl << endl; } void QtXmlToSphinx::handleTargetTag(QXmlStreamReader &reader) @@ -1621,7 +1621,7 @@ void QtDocGenerator::generateClass(QTextStream &s, GeneratorContext &classContex m_docParser->setPackageName(metaClass->package()); m_docParser->fillDocumentation(const_cast<AbstractMetaClass*>(metaClass)); - s << ".. module:: " << metaClass->package() << endl; + s << ".. currentmodule:: " << metaClass->package() << endl; QString className = getClassTargetFullName(metaClass, false); s << ".. _" << className << ":" << endl << endl; @@ -2301,8 +2301,9 @@ void QtDocGenerator::writeAdditionalDocumentation() if (fi.isFile()) { const QString rstFileName = fi.baseName() + rstSuffix; const QString rstFile = targetDir + QLatin1Char('/') + rstFileName; + const QString context = targetDir.mid(targetDir.lastIndexOf(QLatin1Char('/')) + 1); if (QtXmlToSphinx::convertToRst(this, fi.absoluteFilePath(), - rstFile, QString(), &errorMessage)) { + rstFile, context, &errorMessage)) { ++successCount; qCDebug(lcShiboken).nospace().noquote() << __FUNCTION__ << " converted " << fi.fileName() diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 5a4aa8eba..5460fd7c7 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -317,7 +317,10 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) s << "#include <pyside.h>" << endl; s << "#include <destroylistener.h>" << endl; s << "#include <qapp_macro.h>" << endl; - } + s << endl; + s << "QT_WARNING_DISABLE_DEPRECATED" << endl; + s << endl; + } s << "#include <typeinfo>" << endl; if (usePySideExtensions() && metaClass->isQObject()) { @@ -368,9 +371,6 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (metaClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated) s << "#Deprecated" << endl; - if (usePySideExtensions()) - s << "\nQT_WARNING_DISABLE_DEPRECATED\n"; - // Use class base namespace { const AbstractMetaClass *context = metaClass->enclosingClass(); @@ -1860,18 +1860,19 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction // For custom classes, operations like __radd__ and __rmul__ // will enter an infinite loop. if (rfunc->isBinaryOperator() && revOpName.contains(QLatin1String("shift"))) { + s << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"" << revOpName << "\"));" << endl; s << INDENT << "if (!isReverse" << endl; { Indentation indent(INDENT); s << INDENT << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")" << endl; s << INDENT << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)" << endl; - s << INDENT << "&& PyObject_HasAttrString(" << PYTHON_ARG << ", const_cast<char *>(\"" << revOpName << "\"))) {" << endl; + s << INDENT << "&& PyObject_HasAttr(" << PYTHON_ARG << ", attrName)) {" << endl; // This PyObject_CallMethod call will emit lots of warnings like // "deprecated conversion from string constant to char *" during compilation // due to the method name argument being declared as "char *" instead of "const char *" // issue 6952 http://bugs.python.org/issue6952 - s << INDENT << "PyObject *revOpMethod = PyObject_GetAttrString(" << PYTHON_ARG << ", const_cast<char *>(\"" << revOpName << "\"));" << endl; + s << INDENT << "PyObject *revOpMethod = PyObject_GetAttr(" << PYTHON_ARG << ", attrName);" << endl; s << INDENT << "if (revOpMethod && PyCallable_Check(revOpMethod)) {" << endl; { Indentation indent(INDENT); @@ -2154,6 +2155,8 @@ void CppGenerator::writeTypeCheck(QTextStream &s, const AbstractMetaType *argTyp QString customCheck; if (!customType.isEmpty()) { AbstractMetaType *metaType; + // PYSIDE-795: Note: XML-Overrides are handled in this shibokengenerator function! + // This enables iterables for QMatrix4x4 for instance. customCheck = guessCPythonCheckFunction(customType, &metaType); if (metaType) argType = metaType; @@ -2939,6 +2942,9 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream &s, typeCheck = QLatin1String("PyType_Check(%in)"); else if (pyTypeName == QLatin1String("PyObject")) typeCheck = QLatin1String("PyObject_TypeCheck(%in, &PyBaseObject_Type)"); + // PYSIDE-795: We abuse PySequence for iterables + else if (pyTypeName == QLatin1String("PySequence")) + typeCheck = QLatin1String("Shiboken::String::checkIterable(%in)"); else if (pyTypeName.startsWith(QLatin1String("Py"))) typeCheck = pyTypeName + QLatin1String("_Check(%in)"); } @@ -3039,29 +3045,31 @@ void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMe QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG); s << INDENT << "keyName = Py_BuildValue(\"s\",\"" << arg->name() << "\");" << endl; s << INDENT << "if (PyDict_Contains(kwds, keyName)) {" << endl; - s << INDENT << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; - s << INDENT << "if (value && " << pyArgName << ") {" << endl; { Indentation indent(INDENT); - s << INDENT << pyErrString.arg(arg->name()) << endl; - s << INDENT << returnStatement(m_currentErrorCode) << endl; - } - s << INDENT << INDENT << "} else if (value) {" << endl; - { - Indentation indent(INDENT); - s << INDENT << pyArgName << " = value;" << endl; - s << INDENT << "if (!"; - writeTypeCheck(s, arg->type(), pyArgName, isNumber(arg->type()->typeEntry()), func->typeReplaced(arg->argumentIndex() + 1)); - s << ')' << endl; + s << INDENT << "value = PyDict_GetItem(kwds, keyName);" << endl; + s << INDENT << "if (value && " << pyArgName << ") {" << endl; { Indentation indent(INDENT); - s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;" << endl; + s << INDENT << pyErrString.arg(arg->name()) << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } + s << INDENT << '}' << endl; + s << INDENT << "if (value) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << pyArgName << " = value;" << endl; + s << INDENT << "if (!"; + writeTypeCheck(s, arg->type(), pyArgName, isNumber(arg->type()->typeEntry()), func->typeReplaced(arg->argumentIndex() + 1)); + s << ')' << endl; + { + Indentation indent(INDENT); + s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;" << endl; + } + } + s << INDENT << '}' << endl; } s << INDENT << '}' << endl; - if (arg != args.constLast()) - s << INDENT; - s << "}" << endl; } } s << INDENT << '}' << endl; @@ -3933,9 +3941,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 @@ -4608,6 +4617,15 @@ void CppGenerator::writeEnumsInitialization(QTextStream &s, AbstractMetaEnumList } } +static QString mangleName(QString name) +{ + if ( name == QLatin1String("None") + || name == QLatin1String("False") + || name == QLatin1String("True")) + name += QLatin1Char('_'); + return name; +} + void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnum *cppEnum) { const AbstractMetaClass *enclosingClass = getProperEnclosingClassForEnum(cppEnum); @@ -4687,7 +4705,7 @@ void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnu Indentation indent(INDENT); s << INDENT << "PyObject *anonEnumItem = PyInt_FromLong(" << enumValueText << ");" << endl; s << INDENT << "if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(reinterpret_cast<SbkObjectType *>(" << enclosingObjectVariable - << "))->tp_dict, \"" << enumValue->name() << "\", anonEnumItem) < 0)" << endl; + << "))->tp_dict, \"" << mangleName(enumValue->name()) << "\", anonEnumItem) < 0)" << endl; { Indentation indent(INDENT); s << INDENT << returnStatement(m_currentErrorCode) << endl; @@ -4696,7 +4714,7 @@ void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnu } s << INDENT << '}' << endl; } else { - s << INDENT << "if (PyModule_AddIntConstant(module, \"" << enumValue->name() << "\", "; + s << INDENT << "if (PyModule_AddIntConstant(module, \"" << mangleName(enumValue->name()) << "\", "; s << enumValueText << ") < 0)" << endl; { Indentation indent(INDENT); @@ -4709,7 +4727,7 @@ void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnu s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" : "createGlobalEnumItem"); s << '(' << enumVarTypeObj << ',' << endl; Indentation indent(INDENT); - s << INDENT << enclosingObjectVariable << ", \"" << enumValue->name() << "\", "; + s << INDENT << enclosingObjectVariable << ", \"" << mangleName(enumValue->name()) << "\", "; s << enumValueText << "))" << endl; s << INDENT << returnStatement(m_currentErrorCode) << endl; } @@ -4718,7 +4736,7 @@ void CppGenerator::writeEnumInitialization(QTextStream &s, const AbstractMetaEnu s << INDENT << "if (!Shiboken::Enum::createScopedEnumItem(" << enumVarTypeObj << ',' << endl; Indentation indent(INDENT); - s << INDENT << enumVarTypeObj<< ", \"" << enumValue->name() << "\", " + s << INDENT << enumVarTypeObj<< ", \"" << mangleName(enumValue->name()) << "\", " << enumValueText << "))" << endl << INDENT << returnStatement(m_currentErrorCode) << endl; } @@ -5541,10 +5559,11 @@ bool CppGenerator::finishGeneration() { Indentation indentation(INDENT); s << INDENT << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);" << endl; - s << INDENT << "if (pyType && PyObject_HasAttrString(pyType, \"staticMetaObject\"))"<< endl; + s << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"staticMetaObject\"));" << endl; + s << INDENT << "if (pyType && PyObject_HasAttr(pyType, attrName))"<< endl; { Indentation indentation(INDENT); - s << INDENT << "PyObject_SetAttrString(pyType, \"staticMetaObject\", Py_None);" << endl; + s << INDENT << "PyObject_SetAttr(pyType, attrName, Py_None);" << endl; } } s << INDENT << "}" << endl; @@ -5981,7 +6000,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__"); @@ -5994,7 +6015,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; @@ -6005,7 +6026,8 @@ QString CppGenerator::writeReprFunction(QTextStream &s, GeneratorContext &contex Indentation indent(INDENT); s << INDENT << "str.replace(0, idx, Py_TYPE(self)->tp_name);" << endl; } - s << INDENT << "PyObject *mod = PyDict_GetItemString(Py_TYPE(self)->tp_dict, \"__module__\");" << endl; + s << INDENT << "str = str.trimmed();" << endl; + s << INDENT << "PyObject *mod = PyDict_GetItem(Py_TYPE(self)->tp_dict, Shiboken::PyMagicName::module());" << endl; // PYSIDE-595: The introduction of heap types has the side effect that the module name // is always prepended to the type name. Therefore the strchr check: s << INDENT << "if (mod && !strchr(str, '.'))" << 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 diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.cpp b/sources/shiboken2/generator/shiboken2/headergenerator.cpp index 8a2c56232..e47c37523 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/headergenerator.cpp @@ -454,6 +454,11 @@ bool HeaderGenerator::finishGeneration() // TODO-CONVERTER ------------------------------------------------------------------------------ macrosStream << "// Macros for type check" << endl; + + if (usePySideExtensions()) { + typeFunctions << "QT_WARNING_PUSH" << endl; + typeFunctions << "QT_WARNING_DISABLE_DEPRECATED" << endl; + } for (const AbstractMetaEnum *cppEnum : qAsConst(globalEnums)) { if (cppEnum->isAnonymous() || cppEnum->isPrivate()) continue; @@ -489,6 +494,8 @@ bool HeaderGenerator::finishGeneration() includes << classType->include(); writeSbkTypeFunction(typeFunctions, metaType); } + if (usePySideExtensions()) + typeFunctions << "QT_WARNING_POP" << endl; QString moduleHeaderFileName(outputDirectory() + QDir::separator() + subDirectoryForPackage(packageName()) diff --git a/sources/shiboken2/generator/shiboken2/overloaddata.cpp b/sources/shiboken2/generator/shiboken2/overloaddata.cpp index 4aa5e5c02..5f74ee64d 100644 --- a/sources/shiboken2/generator/shiboken2/overloaddata.cpp +++ b/sources/shiboken2/generator/shiboken2/overloaddata.cpp @@ -520,7 +520,7 @@ void OverloadData::addOverload(const AbstractMetaFunction *func) for (int i = 0; m_headOverloadData->m_minArgs > 0 && i < origNumArgs; i++) { if (func->argumentRemoved(i + 1)) continue; - if (!ShibokenGenerator::getDefaultValue(func, func->arguments().at(i)).isEmpty()) { + if (func->arguments().at(i)->hasDefaultValueExpression()) { int fixedArgIndex = i - removed; if (fixedArgIndex < m_headOverloadData->m_minArgs) m_headOverloadData->m_minArgs = fixedArgIndex; @@ -754,7 +754,7 @@ const AbstractMetaFunction *OverloadData::getFunctionWithDefaultValue() const if (func->argumentRemoved(i + 1)) removedArgs++; } - if (!ShibokenGenerator::getDefaultValue(func, func->arguments().at(m_argPos + removedArgs)).isEmpty()) + if (func->arguments().at(m_argPos + removedArgs)->hasDefaultValueExpression()) return func; } return nullptr; @@ -771,7 +771,7 @@ QVector<int> OverloadData::invalidArgumentLengths() const if (func->argumentRemoved(i+1)) { offset++; } else { - if (!ShibokenGenerator::getDefaultValue(func, args[i]).isEmpty()) + if (args.at(i)->hasDefaultValueExpression()) validArgLengths << i-offset; } } @@ -820,7 +820,7 @@ QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList if (func->argumentRemoved(j + 1)) continue; int fixedArgIndex = j - removed; - if (fixedArgIndex < minArgs && !ShibokenGenerator::getDefaultValue(func, func->arguments().at(j)).isEmpty()) + if (fixedArgIndex < minArgs && func->arguments().at(j)->hasDefaultValueExpression()) minArgs = fixedArgIndex; } } @@ -967,7 +967,7 @@ QString OverloadData::dumpGraph() const const AbstractMetaArgument *arg = argument(func); if (!arg) continue; - QString argDefault = ShibokenGenerator::getDefaultValue(func, arg); + QString argDefault = arg->defaultValueExpression(); if (!argDefault.isEmpty() || argDefault != arg->originalDefaultValueExpression()) { s << "<tr><td bgcolor=\"gray\" align=\"right\">f" << functionNumber(func); @@ -1038,7 +1038,7 @@ bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunction *func) for (const AbstractMetaArgument *arg : arguments) { if (func->argumentRemoved(arg->argumentIndex() + 1)) continue; - if (!ShibokenGenerator::getDefaultValue(func, arg).isEmpty()) + if (arg->hasDefaultValueExpression()) return true; } return false; @@ -1049,7 +1049,7 @@ AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const Abstr AbstractMetaArgumentList args; const AbstractMetaArgumentList &arguments = func->arguments(); for (AbstractMetaArgument *arg : arguments) { - if (ShibokenGenerator::getDefaultValue(func, arg).isEmpty() + if (!arg->hasDefaultValueExpression() || func->argumentRemoved(arg->argumentIndex() + 1)) continue; args << arg; diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 34e43a4c7..fd75c620e 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -324,18 +324,16 @@ bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass *metaCl void ShibokenGenerator::lookForEnumsInClassesNotToBeGenerated(AbstractMetaEnumList &enumList, const AbstractMetaClass *metaClass) { - if (!metaClass) - return; - + Q_ASSERT(metaClass); + // if a scope is not to be generated, collect its enums into the parent scope if (metaClass->typeEntry()->codeGeneration() == TypeEntry::GenerateForSubclass) { const AbstractMetaEnumList &enums = metaClass->enums(); - for (const AbstractMetaEnum *metaEnum : enums) { - if (metaEnum->isPrivate() || metaEnum->typeEntry()->codeGeneration() == TypeEntry::GenerateForSubclass) - continue; - if (!enumList.contains(const_cast<AbstractMetaEnum *>(metaEnum))) - enumList.append(const_cast<AbstractMetaEnum *>(metaEnum)); + for (AbstractMetaEnum *metaEnum : enums) { + if (!metaEnum->isPrivate() && metaEnum->typeEntry()->generateCode() + && !enumList.contains(metaEnum)) { + enumList.append(metaEnum); + } } - lookForEnumsInClassesNotToBeGenerated(enumList, metaClass->enclosingClass()); } } @@ -571,13 +569,13 @@ QString ShibokenGenerator::guessScopeForDefaultFlagsValue(const AbstractMetaFunc QString ShibokenGenerator::guessScopeForDefaultValue(const AbstractMetaFunction *func, const AbstractMetaArgument *arg) const { - QString value = getDefaultValue(func, arg); + QString value = arg->defaultValueExpression(); - if (value.isEmpty()) - return QString(); - - if (isPointer(arg->type())) + if (value.isEmpty() + || arg->hasModifiedDefaultValueExpression() + || isPointer(arg->type())) { return value; + } static const QRegularExpression enumValueRegEx(QStringLiteral("^([A-Za-z_]\\w*)?$")); Q_ASSERT(enumValueRegEx.isValid()); @@ -1255,6 +1253,11 @@ QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry *type, bool gene QString ShibokenGenerator::guessCPythonCheckFunction(const QString &type, AbstractMetaType **metaType) { *metaType = nullptr; + // PYSIDE-795: We abuse PySequence for iterables. + // This part handles the overrides in the XML files. + if (type == QLatin1String("PySequence")) + return QLatin1String("Shiboken::String::checkIterable"); + if (type == QLatin1String("PyTypeObject")) return QLatin1String("PyType_Check"); @@ -2306,7 +2309,7 @@ AbstractMetaType *ShibokenGenerator::buildAbstractMetaTypeFromString(QString typ auto it = m_metaTypeFromStringCache.find(typeSignature); if (it == m_metaTypeFromStringCache.end()) { AbstractMetaType *metaType = - AbstractMetaBuilder::translateType(typeSignature, nullptr, true, errorMessage); + AbstractMetaBuilder::translateType(typeSignature, nullptr, {}, errorMessage); if (Q_UNLIKELY(!metaType)) { if (errorMessage) errorMessage->prepend(msgCannotBuildMetaType(typeSignature)); @@ -2708,22 +2711,6 @@ bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const OverloadD || overloadData.hasArgumentWithDefaultValue(); } -QString ShibokenGenerator::getDefaultValue(const AbstractMetaFunction *func, const AbstractMetaArgument *arg) -{ - if (!arg->defaultValueExpression().isEmpty()) - return arg->defaultValueExpression(); - - //Check modifications - const FunctionModificationList &mods = func->modifications(); - for (const FunctionModification &m : mods) { - for (const ArgumentModification &am : m.argument_mods) { - if (am.index == (arg->argumentIndex() + 1)) - return am.replacedDefaultExpression; - } - } - return QString(); -} - void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream &s, const AbstractMetaType *type, const QString &defaultCtor) { if (!defaultCtor.isEmpty()) { diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.h b/sources/shiboken2/generator/shiboken2/shibokengenerator.h index 84b3137b8..7970ceb94 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.h +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.h @@ -71,11 +71,6 @@ public: const char *name() const override { return "Shiboken"; } - /** - * Helper function to find for argument default value - */ - static QString getDefaultValue(const AbstractMetaFunction *func, const AbstractMetaArgument *arg); - /// Returns a list of all ancestor classes for the given class. AbstractMetaClassList getAllAncestors(const AbstractMetaClass *metaClass) const; diff --git a/sources/shiboken2/libshiboken/CMakeLists.txt b/sources/shiboken2/libshiboken/CMakeLists.txt index 7cbb22978..a38da8d89 100644 --- a/sources/shiboken2/libshiboken/CMakeLists.txt +++ b/sources/shiboken2/libshiboken/CMakeLists.txt @@ -30,8 +30,8 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/embed/signature_bootstrap.py" "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_bootstrap.py" @ONLY) add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_bootstrap.inc" - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embed/signature.inc" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_bootstrap_inc.h" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embed/signature_inc.h" COMMAND ${PYTHON_EXECUTABLE} -E "${CMAKE_CURRENT_SOURCE_DIR}/embed/embedding_generator.py" --cmake-dir "${CMAKE_CURRENT_BINARY_DIR}/embed" @@ -53,6 +53,7 @@ sbkconverter.cpp sbkenum.cpp sbkmodule.cpp sbkstring.cpp +sbkstaticstrings.cpp bindingmanager.cpp threadstatesaver.cpp shibokenbuffer.cpp @@ -62,8 +63,8 @@ pep384impl.cpp voidptr.cpp typespec.cpp bufferprocs_py37.cpp -embed/signature_bootstrap.inc -embed/signature.inc +embed/signature_bootstrap_inc.h +embed/signature_inc.h ) get_numpy_location() @@ -129,6 +130,7 @@ install(FILES python25compat.h sbkdbg.h sbkstring.h + sbkstaticstrings.h shiboken.h shibokenmacros.h threadstatesaver.h diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index b9f6735d8..000035627 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -44,6 +44,7 @@ #include "sbkconverter.h" #include "sbkenum.h" #include "sbkstring.h" +#include "sbkstaticstrings_p.h" #include "autodecref.h" #include "gilstate.h" #include <string> @@ -92,6 +93,7 @@ static PyType_Slot SbkObjectType_Type_slots[] = { {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)}, {Py_tp_new, reinterpret_cast<void *>(SbkObjectTypeTpNew)}, {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, + {Py_tp_getset, reinterpret_cast<void *>(SbkObjectType_Type_getsetlist)}, {0, nullptr} }; static PyType_Spec SbkObjectType_Type_spec = { @@ -106,7 +108,7 @@ static PyType_Spec SbkObjectType_Type_spec = { #if PY_VERSION_HEX < 0x03000000 /***************************************************************************** * - * PYSIDE-816: Workaround for Python 2.7 + * PYSIDE-816: Workaround for Python 2.7 for SbkObjectType_TypeF(). * * This is an add-on for function typeobject.c:tp_new_wrapper from Python 2.7 . * Problem: @@ -121,9 +123,16 @@ static PyType_Spec SbkObjectType_Type_spec = { * The problem is that heap types have this unwanted dependency. * But we cannot get at static slot_tp_new, and so we have to use * the original function and patch Py_TPFLAGS_HEAPTYPE away during the call. + * + * PYSIDE-1051: The same problem holds for all dynamic metatypes generated by + * SbkObjectTypeTpNew() and all types generated by + * introduceWrapperType() . + * + * This led to a drastic overhaul of patch_tp_new_wrapper() which now adds + * the new wrapper to exactly those types which have the old wrapper. */ -static PyCFunction old_tp_new_wrapper = nullptr; +ternaryfunc old_tp_new_wrapper = nullptr; static PyObject * tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) @@ -136,9 +145,9 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) return ret; } -// This is intentionally the new docstring of Python 3.7 . +// This is intentionally the __new__ docstring of Python 3.7 . static struct PyMethodDef tp_new_methoddef[] = { - {"__new__", (PyCFunction)tp_new_wrapper, METH_VARARGS|METH_KEYWORDS, + {"__new__", reinterpret_cast<PyCFunction>(tp_new_wrapper), METH_VARARGS|METH_KEYWORDS, PyDoc_STR("__new__($type, *args, **kwargs)\n--\n\n" "Create and return a new object. " "See help(type) for accurate signature.")}, @@ -146,43 +155,38 @@ static struct PyMethodDef tp_new_methoddef[] = { }; static int -get_old_tp_new_wrapper(void) -{ - // We get the old tp_new_wrapper from any initialized type. - PyTypeObject *type = &PyType_Type; - PyObject *dict = type->tp_dict; - PyObject *key, *func = nullptr; - Py_ssize_t pos = 0; - while (PyDict_Next(dict, &pos, &key, &func)) { - char *name = PyString_AsString(key); - if (strcmp(name, "__new__") == 0) { - break; +patch_tp_new_wrapper(PyTypeObject *type) +{ + /* + * The old tp_new_wrapper is added to all types that have tp_new. + * We patch that with a version that ignores the heaptype flag. + */ + auto newMethod = Shiboken::PyMagicName::new_(); + if (old_tp_new_wrapper == nullptr) { + PyObject *func = PyDict_GetItem(PyType_Type.tp_dict, newMethod); + assert(func); + PyCFunctionObject *pycf_ob = reinterpret_cast<PyCFunctionObject *>(func); + old_tp_new_wrapper = reinterpret_cast<ternaryfunc>(pycf_ob->m_ml->ml_meth); + } + PyObject *mro = type->tp_mro; + Py_ssize_t i, n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, i)); + PyObject *existing = PyDict_GetItem(type->tp_dict, newMethod); + if (existing && PyCFunction_Check(existing) + && type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + auto *pycf_ob = reinterpret_cast<PyCFunctionObject *>(existing); + auto existing_wrapper = reinterpret_cast<ternaryfunc>(pycf_ob->m_ml->ml_meth); + if (existing_wrapper == tp_new_wrapper) + break; + if (existing_wrapper == old_tp_new_wrapper) { + PyObject *ob_type = reinterpret_cast<PyObject *>(type); + Shiboken::AutoDecRef func(PyCFunction_New(tp_new_methoddef, ob_type)); + if (func.isNull() || PyDict_SetItem(type->tp_dict, newMethod, func)) + return -1; + } } } - if (func == nullptr) - return -1; - PyCFunctionObject *pycf_ob = reinterpret_cast<PyCFunctionObject *>(func); - old_tp_new_wrapper = pycf_ob->m_ml->ml_meth; - return 0; -} - -static int -add_tp_new_wrapper(PyTypeObject *type) -{ - // get the original tp_new_wrapper - if (old_tp_new_wrapper == nullptr && get_old_tp_new_wrapper() < 0) - return -1; - // initialize tp_dict - if (type->tp_dict == nullptr) - type->tp_dict = PyDict_New(); - if (type->tp_dict == nullptr) - return -1; - PyObject *ob_type = reinterpret_cast<PyObject *>(type); - Shiboken::AutoDecRef func(PyCFunction_New(tp_new_methoddef, ob_type)); - if (func.isNull()) - return -1; - if (PyDict_SetItemString(type->tp_dict, "__new__", func)) - return -1; return 0; } /*****************************************************************************/ @@ -197,7 +201,7 @@ PyTypeObject *SbkObjectType_TypeF(void) PepHeapType_SIZE + sizeof(SbkObjectTypePrivate); type = reinterpret_cast<PyTypeObject *>(PyType_FromSpec(&SbkObjectType_Type_spec)); #if PY_VERSION_HEX < 0x03000000 - if (add_tp_new_wrapper(type) < 0) + if (patch_tp_new_wrapper(type) < 0) return nullptr; #endif } @@ -452,6 +456,11 @@ PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *k auto *newType = reinterpret_cast<SbkObjectType *>(type_new(metatype, args, kwds)); if (!newType) return nullptr; +#if PY_VERSION_HEX < 0x03000000 + // PYSIDE-1051: The newly created metatype needs the PYSIDE-816 wrapper, too. + if (patch_tp_new_wrapper(&newType->type) < 0) + return nullptr; +#endif Shiboken::ObjectType::initPrivateData(newType); SbkObjectTypePrivate *sotp = PepType_SOTP(newType); @@ -842,6 +851,11 @@ introduceWrapperType(PyObject *enclosingObject, Py_TYPE(heaptype) = SbkObjectType_TypeF(); Py_INCREF(Py_TYPE(heaptype)); auto *type = reinterpret_cast<SbkObjectType *>(heaptype); +#if PY_VERSION_HEX < 0x03000000 + // PYSIDE-1051: The newly created type needs the PYSIDE-816 wrapper, too. + if (patch_tp_new_wrapper(&type->type) < 0) + return nullptr; +#endif if (baseType) { if (baseTypes) { for (int i = 0; i < PySequence_Fast_GET_SIZE(baseTypes); ++i) diff --git a/sources/shiboken2/libshiboken/embed/embedding_generator.py b/sources/shiboken2/libshiboken/embed/embedding_generator.py index 77aa5c329..15f63649b 100644 --- a/sources/shiboken2/libshiboken/embed/embedding_generator.py +++ b/sources/shiboken2/libshiboken/embed/embedding_generator.py @@ -88,7 +88,7 @@ def create_zipfile(limited_api): and make a chunked base64 encoded file from it. """ zip_name = "signature.zip" - inc_name = "signature.inc" + inc_name = "signature_inc.h" flag = '-b' if sys.version_info >= (3,) else '' os.chdir(work_dir) @@ -131,7 +131,7 @@ def create_zipfile(limited_api): tmp.close() # also generate a simple embeddable .pyc file for signature_bootstrap.pyc boot_name = "signature_bootstrap.py" if limited_api else "signature_bootstrap.pyc" - with open(boot_name, "rb") as ldr, open("signature_bootstrap.inc", "w") as inc: + with open(boot_name, "rb") as ldr, open("signature_bootstrap_inc.h", "w") as inc: _embed_bytefile(ldr, inc, limited_api) os.chdir(cur_dir) diff --git a/sources/shiboken2/libshiboken/helper.cpp b/sources/shiboken2/libshiboken/helper.cpp index fac72d56f..013080b6e 100644 --- a/sources/shiboken2/libshiboken/helper.cpp +++ b/sources/shiboken2/libshiboken/helper.cpp @@ -39,6 +39,7 @@ #include "helper.h" #include "sbkstring.h" +#include "sbkstaticstrings.h" #include <stdarg.h> #ifdef _WIN32 @@ -78,7 +79,7 @@ bool listToArgcArgv(PyObject *argList, int *argc, char ***argv, const char *defa if (hasEmptyArgList) { // Try to get the script name PyObject *globals = PyEval_GetGlobals(); - PyObject *appName = PyDict_GetItemString(globals, "__file__"); + PyObject *appName = PyDict_GetItem(globals, Shiboken::PyMagicName::file()); (*argv)[0] = strdup(appName ? Shiboken::String::toCString(appName) : defaultAppName); } else { for (int i = 0; i < numArgs; ++i) { diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp index fe7157d24..5729100bf 100644 --- a/sources/shiboken2/libshiboken/pep384impl.cpp +++ b/sources/shiboken2/libshiboken/pep384impl.cpp @@ -39,6 +39,8 @@ #include "pep384impl.h" #include "autodecref.h" +#include "sbkstaticstrings.h" +#include "sbkstaticstrings_p.h" extern "C" { @@ -85,14 +87,15 @@ static PyGetSetDef probe_getseters[] = { #define probe_tp_str make_dummy(2) #define probe_tp_traverse make_dummy(3) #define probe_tp_clear make_dummy(4) +#define probe_tp_iternext make_dummy(5) #define probe_tp_methods probe_methoddef #define probe_tp_getset probe_getseters -#define probe_tp_descr_get make_dummy(7) -#define probe_tp_init make_dummy(8) -#define probe_tp_alloc make_dummy(9) -#define probe_tp_new make_dummy(10) -#define probe_tp_free make_dummy(11) -#define probe_tp_is_gc make_dummy(12) +#define probe_tp_descr_get make_dummy(8) +#define probe_tp_init make_dummy(9) +#define probe_tp_alloc make_dummy(10) +#define probe_tp_new make_dummy(11) +#define probe_tp_free make_dummy(12) +#define probe_tp_is_gc make_dummy(13) #define probe_tp_name "type.probe" #define probe_tp_basicsize make_dummy_int(42) @@ -102,6 +105,7 @@ static PyType_Slot typeprobe_slots[] = { {Py_tp_str, probe_tp_str}, {Py_tp_traverse, probe_tp_traverse}, {Py_tp_clear, probe_tp_clear}, + {Py_tp_iternext, probe_tp_iternext}, {Py_tp_methods, probe_tp_methods}, {Py_tp_getset, probe_tp_getset}, {Py_tp_descr_get, probe_tp_descr_get}, @@ -125,16 +129,16 @@ check_PyTypeObject_valid() { auto *obtype = reinterpret_cast<PyObject *>(&PyType_Type); auto *probe_tp_base = reinterpret_cast<PyTypeObject *>( - PyObject_GetAttrString(obtype, "__base__")); - PyObject *probe_tp_bases = PyObject_GetAttrString(obtype, "__bases__"); + PyObject_GetAttr(obtype, Shiboken::PyMagicName::base())); + auto *probe_tp_bases = PyObject_GetAttr(obtype, Shiboken::PyMagicName::bases()); auto *check = reinterpret_cast<PyTypeObject *>( PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases)); auto *typetype = reinterpret_cast<PyTypeObject *>(obtype); - PyObject *w = PyObject_GetAttrString(obtype, "__weakrefoffset__"); + PyObject *w = PyObject_GetAttr(obtype, Shiboken::PyMagicName::weakrefoffset()); long probe_tp_weakrefoffset = PyLong_AsLong(w); - PyObject *d = PyObject_GetAttrString(obtype, "__dictoffset__"); + PyObject *d = PyObject_GetAttr(obtype, Shiboken::PyMagicName::dictoffset()); long probe_tp_dictoffset = PyLong_AsLong(d); - PyObject *probe_tp_mro = PyObject_GetAttrString(obtype, "__mro__"); + PyObject *probe_tp_mro = PyObject_GetAttr(obtype, Shiboken::PyMagicName::mro()); if (false || strcmp(probe_tp_name, check->tp_name) != 0 || probe_tp_basicsize != check->tp_basicsize @@ -143,6 +147,7 @@ check_PyTypeObject_valid() || probe_tp_traverse != check->tp_traverse || probe_tp_clear != check->tp_clear || probe_tp_weakrefoffset != typetype->tp_weaklistoffset + || probe_tp_iternext != check->tp_iternext || probe_tp_methods != check->tp_methods || probe_tp_getset != check->tp_getset || probe_tp_base != typetype->tp_base @@ -416,9 +421,10 @@ PepRun_GetResult(const char *command, const char *resvar) PyObject *d, *v, *res; d = PyDict_New(); - if (d == NULL || PyDict_SetItemString(d, "__builtins__", - PyEval_GetBuiltins()) < 0) - return NULL; + if (d == nullptr + || PyDict_SetItem(d, Shiboken::PyMagicName::builtins(), PyEval_GetBuiltins()) < 0) { + return nullptr; + } v = PyRun_String(command, Py_file_input, d, d); res = v ? PyDict_GetItemString(d, resvar) : NULL; Py_XDECREF(v); @@ -457,7 +463,7 @@ PyMethod_New(PyObject *func, PyObject *self) PyObject * PyMethod_Function(PyObject *im) { - PyObject *ret = PyObject_GetAttrString(im, "__func__"); + PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::func()); // We have to return a borrowed reference. Py_DECREF(ret); @@ -467,7 +473,7 @@ PyMethod_Function(PyObject *im) PyObject * PyMethod_Self(PyObject *im) { - PyObject *ret = PyObject_GetAttrString(im, "__self__"); + PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::self()); // We have to return a borrowed reference. // If we don't obey that here, then we get a test error! @@ -620,8 +626,8 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name) return name; } #endif - Shiboken::AutoDecRef privateobj(PyObject_GetAttrString( - reinterpret_cast<PyObject *>(Py_TYPE(self)), "__name__")); + Shiboken::AutoDecRef privateobj(PyObject_GetAttr( + reinterpret_cast<PyObject *>(Py_TYPE(self)), Shiboken::PyMagicName::name())); #ifndef Py_LIMITED_API return _Py_Mangle(privateobj, name); #else diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h index 93f718988..1aa7e6fc0 100644 --- a/sources/shiboken2/libshiboken/pep384impl.h +++ b/sources/shiboken2/libshiboken/pep384impl.h @@ -110,7 +110,7 @@ typedef struct _typeobject { void *X23; // richcmpfunc tp_richcompare; Py_ssize_t tp_weaklistoffset; void *X25; // getiterfunc tp_iter; - void *X26; // iternextfunc tp_iternext; + iternextfunc tp_iternext; struct PyMethodDef *tp_methods; void *X28; // struct PyMemberDef *tp_members; struct PyGetSetDef *tp_getset; diff --git a/sources/shiboken2/libshiboken/qapp_macro.cpp b/sources/shiboken2/libshiboken/qapp_macro.cpp index 12af9613c..306f53b74 100644 --- a/sources/shiboken2/libshiboken/qapp_macro.cpp +++ b/sources/shiboken2/libshiboken/qapp_macro.cpp @@ -120,6 +120,7 @@ reset_qApp_var(void) PyObject * MakeSingletonQAppWrapper(PyTypeObject *type) { + static bool app_created = false; if (type == nullptr) type = Py_NONE_TYPE; if (!(type == Py_NONE_TYPE || Py_TYPE(qApp_content) == Py_NONE_TYPE)) { @@ -145,6 +146,9 @@ MakeSingletonQAppWrapper(PyTypeObject *type) Py_REFCNT(qApp_var) = 1; // fuse is armed... } if (type == Py_NONE_TYPE) { + // PYSIDE-1093: Ignore None when no instance has ever been created. + if (!app_created) + Py_RETURN_NONE; // Debug mode showed that we need to do more than just remove the // reference. To keep everything in the right order, it is easiest // to do a full shutdown, using QtCore.__moduleShutdown(). @@ -158,9 +162,10 @@ MakeSingletonQAppWrapper(PyTypeObject *type) Py_REFCNT(qApp_content) = Py_REFCNT(Py_None); if (__moduleShutdown != nullptr) Py_XDECREF(PyObject_CallFunction(__moduleShutdown, const_cast<char *>("()"))); + } else { + PyObject_INIT(qApp_content, type); + app_created = true; } - else - (void)PyObject_INIT(qApp_content, type); Py_INCREF(qApp_content); return qApp_content; } diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp new file mode 100644 index 000000000..3727eb494 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#include "sbkstaticstrings.h" +#include "sbkstaticstrings_p.h" +#include "sbkstring.h" + +#define STATIC_STRING_IMPL(funcName, value) \ +PyObject *funcName() \ +{ \ + static PyObject *const s = Shiboken::String::createStaticString(value); \ + return s; \ +} + +namespace Shiboken +{ +namespace PyName { +// exported: +STATIC_STRING_IMPL(dumps, "dumps") +STATIC_STRING_IMPL(loads, "loads") + +// Internal: +STATIC_STRING_IMPL(classmethod, "classmethod") +STATIC_STRING_IMPL(compile, "compile"); +STATIC_STRING_IMPL(function, "function") +STATIC_STRING_IMPL(marshal, "marshal") +STATIC_STRING_IMPL(method, "method") +STATIC_STRING_IMPL(overload, "overload") +STATIC_STRING_IMPL(staticmethod, "staticmethod") +} // namespace PyName + +namespace PyMagicName { +// exported: +STATIC_STRING_IMPL(class_, "__class__") +STATIC_STRING_IMPL(ecf, "__ecf__") +STATIC_STRING_IMPL(file, "__file__") +STATIC_STRING_IMPL(module, "__module__") +STATIC_STRING_IMPL(name, "__name__") + +// Internal: +STATIC_STRING_IMPL(base, "__base__") +STATIC_STRING_IMPL(bases, "__bases__") +STATIC_STRING_IMPL(builtins, "__builtins__") +STATIC_STRING_IMPL(code, "__code__") +STATIC_STRING_IMPL(dictoffset, "__dictoffset__") +STATIC_STRING_IMPL(func, "__func__") +STATIC_STRING_IMPL(func_kind, "__func_kind__") +STATIC_STRING_IMPL(iter, "__iter__") +STATIC_STRING_IMPL(mro, "__mro__") +STATIC_STRING_IMPL(new_, "__new__") +STATIC_STRING_IMPL(objclass, "__objclass__") +STATIC_STRING_IMPL(self, "__self__") +STATIC_STRING_IMPL(signature, "__signature__") +STATIC_STRING_IMPL(weakrefoffset, "__weakrefoffset__") +} // namespace PyMagicName +} // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.h b/sources/shiboken2/libshiboken/sbkstaticstrings.h new file mode 100644 index 000000000..fa21a8e2c --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkstaticstrings.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#ifndef SBKSTATICSTRINGS_H +#define SBKSTATICSTRINGS_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +namespace Shiboken +{ +// Some often-used strings +namespace PyName +{ +LIBSHIBOKEN_API PyObject *dumps(); +LIBSHIBOKEN_API PyObject *loads(); +} // namespace PyName + +namespace PyMagicName +{ +LIBSHIBOKEN_API PyObject *class_(); +LIBSHIBOKEN_API PyObject *ecf(); +LIBSHIBOKEN_API PyObject *file(); +LIBSHIBOKEN_API PyObject *module(); +LIBSHIBOKEN_API PyObject *name(); +} // namespace PyMagicName +} // namespace Shiboken + +#endif // SBKSTATICSTRINGS_H diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings_p.h b/sources/shiboken2/libshiboken/sbkstaticstrings_p.h new file mode 100644 index 000000000..42c5585fa --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkstaticstrings_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#include "sbkpython.h" +#include "shibokenmacros.h" + +namespace Shiboken +{ +namespace PyName +{ +PyObject *classmethod(); +PyObject *compile(); +PyObject *function(); +PyObject *marshal(); +PyObject *method(); +PyObject *overload(); +PyObject *staticmethod(); +} // namespace PyName +namespace PyMagicName +{ +PyObject *base(); +PyObject *bases(); +PyObject *builtins(); +PyObject *code(); +PyObject *dictoffset(); +PyObject *func(); +PyObject *func_kind(); +PyObject *iter(); +PyObject *module(); +PyObject *mro(); +PyObject *new_(); +PyObject *objclass(); +PyObject *self(); +PyObject *signature(); +PyObject *weakrefoffset(); +} // namespace PyMagicName +} // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/sbkstring.cpp b/sources/shiboken2/libshiboken/sbkstring.cpp index 9ba5be281..4a441222f 100644 --- a/sources/shiboken2/libshiboken/sbkstring.cpp +++ b/sources/shiboken2/libshiboken/sbkstring.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -38,14 +38,23 @@ ****************************************************************************/ #include "sbkstring.h" +#include "sbkstaticstrings_p.h" #include "autodecref.h" +#include <vector> + namespace Shiboken { namespace String { +// PYSIDE-795: Redirecting PySequence to Iterable +bool checkIterable(PyObject *obj) +{ + return PyObject_HasAttr(obj, Shiboken::PyMagicName::iter()); +} + bool checkType(PyTypeObject *type) { return type == &PyUnicode_Type @@ -200,6 +209,64 @@ Py_ssize_t len(PyObject *str) return 0; } -} // namespace String +/////////////////////////////////////////////////////////////////////// +// +// Implementation of efficient Python strings +// ------------------------------------------ +// +// Instead of repetitively executing +// +// PyObject *attr = PyObject_GetAttrString(obj, "__name__"); +// +// a helper of the form +// +// PyObject *name() +// { +// static PyObject *const s = Shiboken::String::createStaticString("__name__"); +// return result; +// } +// +// can now be implemented, which registers the string into a static set avoiding +// repetitive string creation. The resulting code looks like: +// +// PyObject *attr = PyObject_GetAttr(obj, name()); +// +// Missing: +// There is no finalization for the string structures, yet. +// But this is a global fault in shiboken. We are missing a true +// finalization like in all other modules. + +using StaticStrings = std::vector<PyObject *>; +static StaticStrings &staticStrings() +{ + static StaticStrings result; + return result; +} + +PyObject *createStaticString(const char *str) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyObject *result = PyUnicode_InternFromString(str); +#else + PyObject *result = PyString_InternFromString(str); +#endif + if (result == nullptr) { + // This error is never checked, but also very unlikely. Report and exit. + PyErr_Print(); + Py_FatalError("unexpected error in createStaticString()"); + } + staticStrings().push_back(result); + return result; +} + +void finalizeStaticStrings() // Currently unused +{ + auto &list = staticStrings(); + for (auto s : list) + Py_DECREF(s); + list.clear(); +} + +} // namespace String } // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/sbkstring.h b/sources/shiboken2/libshiboken/sbkstring.h index 7f434e1b9..84d7768c5 100644 --- a/sources/shiboken2/libshiboken/sbkstring.h +++ b/sources/shiboken2/libshiboken/sbkstring.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -43,17 +43,12 @@ #include "sbkpython.h" #include "shibokenmacros.h" -#if PY_MAJOR_VERSION >= 3 - #define SBK_BYTES_NAME "bytes" -#else - #define SBK_BYTES_NAME "str" -#endif - namespace Shiboken { namespace String { LIBSHIBOKEN_API bool check(PyObject *obj); + LIBSHIBOKEN_API bool checkIterable(PyObject *obj); LIBSHIBOKEN_API bool checkType(PyTypeObject *obj); LIBSHIBOKEN_API bool checkChar(PyObject *obj); LIBSHIBOKEN_API bool isConvertible(PyObject *obj); @@ -65,6 +60,7 @@ namespace String LIBSHIBOKEN_API PyObject *fromStringAndSize(const char *str, Py_ssize_t size); LIBSHIBOKEN_API int compare(PyObject *val1, const char *val2); LIBSHIBOKEN_API Py_ssize_t len(PyObject *str); + LIBSHIBOKEN_API PyObject *createStaticString(const char *str); } // namespace String } // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/shiboken.h b/sources/shiboken2/libshiboken/shiboken.h index 1356670aa..0d2d6b0a6 100644 --- a/sources/shiboken2/libshiboken/shiboken.h +++ b/sources/shiboken2/libshiboken/shiboken.h @@ -52,6 +52,7 @@ #include "sbkenum.h" #include "sbkmodule.h" #include "sbkstring.h" +#include "sbkstaticstrings.h" #include "shibokenmacros.h" #include "shibokenbuffer.h" diff --git a/sources/shiboken2/libshiboken/shibokenbuffer.cpp b/sources/shiboken2/libshiboken/shibokenbuffer.cpp index 330470183..dd6e46320 100644 --- a/sources/shiboken2/libshiboken/shibokenbuffer.cpp +++ b/sources/shiboken2/libshiboken/shibokenbuffer.cpp @@ -43,7 +43,11 @@ bool Shiboken::Buffer::checkType(PyObject *pyObj) { +#ifdef IS_PY3K + return PyObject_CheckBuffer(pyObj) != 0; +#else return PyObject_CheckReadBuffer(pyObj) != 0; +#endif } void *Shiboken::Buffer::getPointer(PyObject *pyObj, Py_ssize_t *size) diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index 07ef366ba..5430ab064 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -39,6 +39,8 @@ #include "basewrapper.h" #include "autodecref.h" +#include "sbkstaticstrings.h" +#include "sbkstaticstrings_p.h" extern "C" { @@ -51,11 +53,11 @@ extern "C" // These constants were needed in former versions of the module: #define PYTHON_HAS_QUALNAME (PY_VERSION_HEX >= 0x03030000) -#define PYTHON_HAS_UNICODE (PY_VERSION_HEX >= 0x03000000) #define PYTHON_HAS_WEAKREF_PYCFUNCTION (PY_VERSION_HEX >= 0x030500A0) #define PYTHON_IS_PYTHON3 (PY_VERSION_HEX >= 0x03000000) #define PYTHON_HAS_KEYWORDONLY (PYTHON_IS_PYTHON3) #define PYTHON_USES_PERCENT_V_FORMAT (PYTHON_IS_PYTHON3) +#define PYTHON_USES_D_COMMON (PY_VERSION_HEX >= 0x03020000) #define PYTHON_HAS_DESCR_REDUCE (PY_VERSION_HEX >= 0x03040000) #define PYTHON_HAS_METH_REDUCE (PYTHON_HAS_DESCR_REDUCE) #define PYTHON_NEEDS_ITERATOR_FLAG (!PYTHON_IS_PYTHON3) @@ -64,7 +66,7 @@ extern "C" #define PYTHON_HAS_INT_AND_LONG (!PYTHON_IS_PYTHON3) // These constants are still in use: -#define PYTHON_USES_D_COMMON (PY_VERSION_HEX >= 0x03020000) +#define PYTHON_USES_UNICODE (PY_VERSION_HEX >= 0x03000000) typedef struct safe_globals_struc { // init part 1: get arg_dict @@ -84,11 +86,11 @@ static safe_globals pyside_globals = nullptr; static PyObject *GetTypeKey(PyObject *ob); -static PyObject *GetSignature_Function(PyObject *, const char *); -static PyObject *GetSignature_TypeMod(PyObject *, const char *); -static PyObject *GetSignature_Wrapper(PyObject *, const char *); +static PyObject *GetSignature_Function(PyObject *, PyObject *); +static PyObject *GetSignature_TypeMod(PyObject *, PyObject *); +static PyObject *GetSignature_Wrapper(PyObject *, PyObject *); static PyObject *get_signature(PyObject *self, PyObject *args); -static PyObject *get_signature_intern(PyObject *ob, const char *modifier); +static PyObject *get_signature_intern(PyObject *ob, PyObject *modifier); static PyObject *PySide_BuildSignatureProps(PyObject *class_mod); @@ -100,7 +102,7 @@ CreateSignature(PyObject *props, PyObject *key) { /* * Here is the new function to create all signatures. It simply calls - * into Python and creates a signature object for a dummy-function. + * into Python and creates a signature object directly. * This is so much simpler than using all the attributes explicitly * to support '_signature_is_functionlike()'. */ @@ -108,10 +110,10 @@ CreateSignature(PyObject *props, PyObject *key) const_cast<char *>("(OO)"), props, key); } -typedef PyObject *(*signaturefunc)(PyObject *, const char *); +typedef PyObject *(*signaturefunc)(PyObject *, PyObject *); static PyObject * -_get_written_signature(signaturefunc sf, PyObject *ob, const char *modifier) +_get_written_signature(signaturefunc sf, PyObject *ob, PyObject *modifier) { /* * Be a writable Attribute, but have a computed value. @@ -134,19 +136,19 @@ _get_written_signature(signaturefunc sf, PyObject *ob, const char *modifier) } static PyObject * -pyside_cf_get___signature__(PyObject *func, const char *modifier) +pyside_cf_get___signature__(PyObject *func, PyObject *modifier) { init_module_2(); return _get_written_signature(GetSignature_Function, func, modifier); } static PyObject * -pyside_sm_get___signature__(PyObject *sm, const char *modifier) +pyside_sm_get___signature__(PyObject *sm, PyObject *modifier) { init_module_2(); - Shiboken::AutoDecRef func(PyObject_GetAttrString(sm, "__func__")); + Shiboken::AutoDecRef func(PyObject_GetAttr(sm, Shiboken::PyMagicName::func())); if (Py_TYPE(func) == PepFunction_TypePtr) - return PyObject_GetAttrString(func, "__signature__"); + return PyObject_GetAttr(func, Shiboken::PyMagicName::signature()); return _get_written_signature(GetSignature_Function, func, modifier); } @@ -158,7 +160,7 @@ _get_class_of_cf(PyObject *ob_cf) selftype = PyDict_GetItem(pyside_globals->map_dict, ob_cf); if (selftype == nullptr) { // This must be an overloaded function that we handled special. - Shiboken::AutoDecRef special(Py_BuildValue("(Os)", ob_cf, "overload")); + Shiboken::AutoDecRef special(Py_BuildValue("(OO)", ob_cf, Shiboken::PyName::overload())); selftype = PyDict_GetItem(pyside_globals->map_dict, special); if (selftype == nullptr) { // This is probably a module function. We will return type(None). @@ -176,15 +178,15 @@ _get_class_of_cf(PyObject *ob_cf) static PyObject * _get_class_of_sm(PyObject *ob_sm) { - Shiboken::AutoDecRef func(PyObject_GetAttrString(ob_sm, "__func__")); + Shiboken::AutoDecRef func(PyObject_GetAttr(ob_sm, Shiboken::PyMagicName::func())); return _get_class_of_cf(func); } static PyObject * _get_class_of_descr(PyObject *ob) { - Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob, "__name__")); - return PyObject_GetAttrString(ob, "__objclass__"); + Shiboken::AutoDecRef func_name(PyObject_GetAttr(ob, Shiboken::PyMagicName::name())); + return PyObject_GetAttr(ob, Shiboken::PyMagicName::objclass()); } static PyObject * @@ -216,10 +218,10 @@ get_funcname(PyObject *ob) { PyObject *func = ob; if (Py_TYPE(ob) == PepStaticMethod_TypePtr) - func = PyObject_GetAttrString(ob, "__func__"); + func = PyObject_GetAttr(ob, Shiboken::PyMagicName::func()); else Py_INCREF(func); - PyObject *func_name = PyObject_GetAttrString(func, "__name__"); + PyObject *func_name = PyObject_GetAttr(func, Shiboken::PyMagicName::name()); Py_DECREF(func); if (func_name == nullptr) Py_FatalError("unexpected name problem in compute_name_key"); @@ -287,7 +289,7 @@ name_key_to_func(PyObject *ob) } static PyObject * -pyside_md_get___signature__(PyObject *ob_md, const char *modifier) +pyside_md_get___signature__(PyObject *ob_md, PyObject *modifier) { init_module_2(); Shiboken::AutoDecRef func(name_key_to_func(ob_md)); @@ -299,14 +301,14 @@ pyside_md_get___signature__(PyObject *ob_md, const char *modifier) } static PyObject * -pyside_wd_get___signature__(PyObject *ob, const char *modifier) +pyside_wd_get___signature__(PyObject *ob, PyObject *modifier) { init_module_2(); return _get_written_signature(GetSignature_Wrapper, ob, modifier); } static PyObject * -pyside_tp_get___signature__(PyObject *obtype_mod, const char *modifier) +pyside_tp_get___signature__(PyObject *obtype_mod, PyObject *modifier) { init_module_2(); return _get_written_signature(GetSignature_TypeMod, obtype_mod, modifier); @@ -314,7 +316,7 @@ pyside_tp_get___signature__(PyObject *obtype_mod, const char *modifier) // forward static PyObject * -GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier); +GetSignature_Cached(PyObject *props, PyObject *func_kind, PyObject *modifier); static PyObject * GetTypeKey(PyObject *ob) @@ -332,8 +334,8 @@ GetTypeKey(PyObject *ob) * * This is the PyCFunction behavior, as opposed to Python functions. */ - Shiboken::AutoDecRef class_name(PyObject_GetAttrString(ob, "__name__")); - Shiboken::AutoDecRef module_name(PyObject_GetAttrString(ob, "__module__")); + Shiboken::AutoDecRef class_name(PyObject_GetAttr(ob, Shiboken::PyMagicName::name())); + Shiboken::AutoDecRef module_name(PyObject_GetAttr(ob, Shiboken::PyMagicName::module())); if (module_name.isNull()) PyErr_Clear(); @@ -364,7 +366,7 @@ TypeKey_to_PropsDict(PyObject *type_key, PyObject *obtype) } static PyObject * -GetSignature_Function(PyObject *obfunc, const char *modifier) +GetSignature_Function(PyObject *obfunc, PyObject *modifier) { // make sure that we look into PyCFunction, only... if (Py_TYPE(obfunc) == PepFunction_TypePtr) @@ -376,29 +378,29 @@ GetSignature_Function(PyObject *obfunc, const char *modifier) PyObject *dict = TypeKey_to_PropsDict(type_key, obtype_mod); if (dict == nullptr) return nullptr; - Shiboken::AutoDecRef func_name(PyObject_GetAttrString(obfunc, "__name__")); + Shiboken::AutoDecRef func_name(PyObject_GetAttr(obfunc, Shiboken::PyMagicName::name())); PyObject *props = !func_name.isNull() ? PyDict_GetItem(dict, func_name) : nullptr; if (props == nullptr) Py_RETURN_NONE; int flags = PyCFunction_GET_FLAGS(obfunc); - const char *sig_kind; + PyObject *func_kind; if (PyModule_Check(obtype_mod)) - sig_kind = "function"; + func_kind = Shiboken::PyName::function(); else if (flags & METH_CLASS) - sig_kind = "classmethod"; + func_kind = Shiboken::PyName::classmethod(); else if (flags & METH_STATIC) - sig_kind = "staticmethod"; + func_kind = Shiboken::PyName::staticmethod(); else - sig_kind = "method"; - return GetSignature_Cached(props, sig_kind, modifier); + func_kind = Shiboken::PyName::method(); + return GetSignature_Cached(props, func_kind, modifier); } static PyObject * -GetSignature_Wrapper(PyObject *ob, const char *modifier) +GetSignature_Wrapper(PyObject *ob, PyObject *modifier) { - Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob, "__name__")); - Shiboken::AutoDecRef objclass(PyObject_GetAttrString(ob, "__objclass__")); + Shiboken::AutoDecRef func_name(PyObject_GetAttr(ob, Shiboken::PyMagicName::name())); + Shiboken::AutoDecRef objclass(PyObject_GetAttr(ob, Shiboken::PyMagicName::objclass())); Shiboken::AutoDecRef class_key(GetTypeKey(objclass)); if (func_name.isNull() || objclass.isNull() || class_key.isNull()) @@ -409,13 +411,13 @@ GetSignature_Wrapper(PyObject *ob, const char *modifier) PyObject *props = PyDict_GetItem(dict, func_name); if (props == nullptr) Py_RETURN_NONE; - return GetSignature_Cached(props, "method", modifier); + return GetSignature_Cached(props, Shiboken::PyName::method(), modifier); } static PyObject * -GetSignature_TypeMod(PyObject *ob, const char *modifier) +GetSignature_TypeMod(PyObject *ob, PyObject *modifier) { - Shiboken::AutoDecRef ob_name(PyObject_GetAttrString(ob, "__name__")); + Shiboken::AutoDecRef ob_name(PyObject_GetAttr(ob, Shiboken::PyMagicName::name())); Shiboken::AutoDecRef ob_key(GetTypeKey(ob)); PyObject *dict = TypeKey_to_PropsDict(ob_key, ob); @@ -424,15 +426,26 @@ GetSignature_TypeMod(PyObject *ob, const char *modifier) PyObject *props = PyDict_GetItem(dict, ob_name); if (props == nullptr) Py_RETURN_NONE; - return GetSignature_Cached(props, "method", modifier); + return GetSignature_Cached(props, Shiboken::PyName::method(), modifier); } static PyObject * -GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier) +GetSignature_Cached(PyObject *props, PyObject *func_kind, PyObject *modifier) { + // Special case: We want to know the func_kind. + if (modifier) { +#if PYTHON_USES_UNICODE + PyUnicode_InternInPlace(&modifier); +#else + PyString_InternInPlace(&modifier); +#endif + if (modifier == Shiboken::PyMagicName::func_kind()) + return Py_BuildValue("O", func_kind); + } + Shiboken::AutoDecRef key(modifier == nullptr - ? Py_BuildValue("s", sig_kind) - : Py_BuildValue("(ss)", sig_kind, modifier)); + ? Py_BuildValue("O", func_kind) + : Py_BuildValue("(OO)", func_kind, modifier)); PyObject *value = PyDict_GetItem(props, key); if (value == nullptr) { // we need to compute a signature object @@ -451,11 +464,11 @@ GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier) } static const char *PySide_CompressedSignaturePackage[] = { -#include "embed/signature.inc" +#include "embed/signature_inc.h" }; static const unsigned char PySide_SignatureLoader[] = { -#include "embed/signature_bootstrap.inc" +#include "embed/signature_bootstrap_inc.h" }; static safe_globals_struc * @@ -474,13 +487,10 @@ init_phase_1(void) #ifdef Py_LIMITED_API // We must work for multiple versions, so use source code. #else - Shiboken::AutoDecRef marshal_str(Py_BuildValue("s", "marshal")); - if (marshal_str.isNull()) - goto error; - Shiboken::AutoDecRef marshal_module(PyImport_Import(marshal_str)); + Shiboken::AutoDecRef marshal_module(PyImport_Import(Shiboken::PyName::marshal())); if (marshal_module.isNull()) goto error; - Shiboken::AutoDecRef loads(PyObject_GetAttrString(marshal_module, "loads")); + Shiboken::AutoDecRef loads(PyObject_GetAttr(marshal_module, Shiboken::PyName::loads())); if (loads.isNull()) goto error; #endif @@ -492,7 +502,7 @@ init_phase_1(void) goto error; #ifdef Py_LIMITED_API PyObject *builtins = PyEval_GetBuiltins(); - PyObject *compile = PyDict_GetItemString(builtins, "compile"); + PyObject *compile = PyDict_GetItem(builtins, Shiboken::PyName::compile()); if (compile == nullptr) goto error; Shiboken::AutoDecRef code_obj(PyObject_CallFunction(compile, "Oss", @@ -509,7 +519,7 @@ init_phase_1(void) goto error; // Initialize the module PyObject *mdict = PyModule_GetDict(p->helper_module); - if (PyDict_SetItemString(mdict, "__builtins__", PyEval_GetBuiltins()) < 0) + if (PyDict_SetItem(mdict, Shiboken::PyMagicName::builtins(), PyEval_GetBuiltins()) < 0) goto error; /* * Unpack an embedded ZIP file with more signature modules. @@ -555,7 +565,7 @@ init_phase_1(void) } error: PyErr_Print(); - PyErr_SetString(PyExc_SystemError, "could not initialize part 1"); + Py_FatalError("could not initialize part 1"); return nullptr; } @@ -597,11 +607,10 @@ init_phase_2(safe_globals_struc *p, PyMethodDef *methods) if (p->finish_import_func == nullptr) goto error; return 0; - return 0; } error: PyErr_Print(); - PyErr_SetString(PyExc_SystemError, "could not initialize part 2"); + Py_FatalError("could not initialize part 2"); return -1; } @@ -799,7 +808,7 @@ static PyGetSetDef new_PyWrapperDescr_getsets[] = { // static PyObject * -get_signature_intern(PyObject *ob, const char *modifier) +get_signature_intern(PyObject *ob, PyObject *modifier) { if (PyType_IsSubtype(Py_TYPE(ob), &PyCFunction_Type)) return pyside_cf_get___signature__(ob, modifier); @@ -818,11 +827,11 @@ static PyObject * get_signature(PyObject * /* self */, PyObject *args) { PyObject *ob; - const char *modifier = nullptr; + PyObject *modifier = nullptr; init_module_1(); - if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier)) + if (!PyArg_ParseTuple(args, "O|O", &ob, &modifier)) return nullptr; if (Py_TYPE(ob) == PepFunction_TypePtr) Py_RETURN_NONE; @@ -1106,13 +1115,14 @@ _build_func_to_type(PyObject *obtype) * "{name}.overload". */ PyObject *descr = PyDict_GetItemString(dict, meth->ml_name); - const char *look_attr = meth->ml_flags & METH_STATIC ? "__func__" : "__name__"; + PyObject *look_attr = meth->ml_flags & METH_STATIC + ? Shiboken::PyMagicName::func() : Shiboken::PyMagicName::name(); int check_name = meth->ml_flags & METH_STATIC ? 0 : 1; if (descr == nullptr) return -1; // We first check all methods if one is hidden by something else. - Shiboken::AutoDecRef look(PyObject_GetAttrString(descr, look_attr)); + Shiboken::AutoDecRef look(PyObject_GetAttr(descr, look_attr)); Shiboken::AutoDecRef given(Py_BuildValue("s", meth->ml_name)); if (look.isNull() || (check_name && PyObject_RichCompareBool(look, given, Py_EQ) != 1)) { @@ -1219,7 +1229,7 @@ SetError_Argument(PyObject *args, const char *func_name) */ PyObject * -Sbk_TypeGet___signature__(PyObject *ob, const char *modifier) +Sbk_TypeGet___signature__(PyObject *ob, PyObject *modifier) { return pyside_tp_get___signature__(ob, modifier); } diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h index 57fd4047a..b22a78497 100644 --- a/sources/shiboken2/libshiboken/signature.h +++ b/sources/shiboken2/libshiboken/signature.h @@ -48,7 +48,7 @@ extern "C" LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char *[]); LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *[]); LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *); -LIBSHIBOKEN_API PyObject *Sbk_TypeGet___signature__(PyObject *, const char *); +LIBSHIBOKEN_API PyObject *Sbk_TypeGet___signature__(PyObject *, PyObject *); LIBSHIBOKEN_API PyObject *Sbk_TypeGet___doc__(PyObject *); } // extern "C" diff --git a/sources/shiboken2/libshiboken/typespec.cpp b/sources/shiboken2/libshiboken/typespec.cpp index 6dc5b00bc..510ed51e6 100644 --- a/sources/shiboken2/libshiboken/typespec.cpp +++ b/sources/shiboken2/libshiboken/typespec.cpp @@ -39,6 +39,7 @@ #include "sbkpython.h" #include "typespec.h" +#include "sbkstaticstrings.h" #include <structmember.h> #if PY_MAJOR_VERSION < 3 @@ -730,7 +731,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) } // no PyId_ things in Python 2 // err = _PyDict_SetItemId(type->tp_dict, &PyId___module__, modname); - err = PyDict_SetItemString(type->tp_dict, "__module__", modname); + err = PyDict_SetItem(type->tp_dict, Shiboken::PyMagicName::module(), modname); Py_DECREF(modname); if (err != 0) goto fail; diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index 5e5cf21d7..e1eafa12f 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -30,7 +30,6 @@ if(WIN32) endif() target_link_libraries(shibokenmodule PUBLIC libshiboken) -add_dependencies(shibokenmodule shiboken2) create_generator_target(shibokenmodule) install(TARGETS shibokenmodule DESTINATION ${PYTHON_SITE_PACKAGES}/shiboken2) diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/backport_inspect.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/backport_inspect.py index c690493b6..1f6d70b31 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/backport_inspect.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/backport_inspect.py @@ -113,7 +113,9 @@ CO_NOFREE = 0x0040 # We use '__builtin__' and '__name__' instead. def formatannotation(annotation, base_module=None): if getattr(annotation, '__module__', None) == 'typing': - return repr(annotation).replace('typing.', '') + # The replace must not be done on Python 2.7 because it + # already happens somewhere else. + return repr(annotation) ##.replace('typing.', '') if isinstance(annotation, type): if annotation.__module__ in ('__builtin__', base_module): return annotation.__name__ diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py index bd827f1ee..384273d92 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py @@ -56,7 +56,7 @@ used literally as strings like "signature", "existence", etc. """ from textwrap import dedent -from shibokensupport.signature import inspect +from shibokensupport.signature import inspect, typing from shibokensupport.signature.mapping import ellipsis from shibokensupport.signature.lib.tool import SimpleNamespace @@ -162,6 +162,35 @@ def define_nameless_parameter(): NamelessParameter = define_nameless_parameter() +""" +Note on the "Optional" feature: + +When an annotation has a default value that is None, then the +type has to be wrapped into "typing.Optional". + +Note that only the None value creates an Optional expression, +because the None leaves the domain of the variable. +Defaults like integer values are ignored: They stay in the domain. + +That information would be lost when we use the "..." convention. + +Note that the typing module has the remarkable expansion + + Optional[T] is Variant[T, NoneType] + +We want to avoid that when generating the .pyi file. +This is done by a regex in generate_pyi.py . +The following would work in Python 3, but this is a version-dependent +hack that also won't work in Python 2 and would be _very_ complex. +""" +# import sys +# if sys.version_info[0] == 3: +# class hugo(list):pass +# typing._normalize_alias["hugo"] = "Optional" +# Optional = typing._alias(hugo, typing.T, inst=False) +# else: +# Optional = typing.Optional + def make_signature_nameless(signature): """ @@ -174,6 +203,13 @@ def make_signature_nameless(signature): signature.parameters[key].__class__ = NamelessParameter +_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY +_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD +_VAR_POSITIONAL = inspect._VAR_POSITIONAL +_KEYWORD_ONLY = inspect._KEYWORD_ONLY +_VAR_KEYWORD = inspect._VAR_KEYWORD +_empty = inspect._empty + def create_signature(props, key): if not props: # empty signatures string @@ -204,26 +240,39 @@ def create_signature(props, key): elif sig_kind == "classmethod": varnames = ("klass",) + varnames else: - raise SystemError("Methods must be function, method, staticmethod or " - "classmethod") + raise SystemError("Methods must be function, method, staticmethod" + " or classmethod") # calculate the modifications defaults = props["defaults"][:] if not layout.defaults: defaults = () - if layout.ellipsis: - defaults = (ellipsis,) * len(defaults) annotations = props["annotations"].copy() if not layout.return_annotation and "return" in annotations: del annotations["return"] - # attach parameters to a fake function and build a signature - argstr = ", ".join(varnames) - fakefunc = eval("lambda {}: None".format(argstr)) - fakefunc.__name__ = props["name"] - fakefunc.__defaults__ = defaults - fakefunc.__kwdefaults__ = props["kwdefaults"] - fakefunc.__annotations__ = annotations - sig = inspect._signature_from_function(inspect.Signature, fakefunc) + # Build a signature. + kind = inspect._POSITIONAL_OR_KEYWORD + params = [] + for idx, name in enumerate(varnames): + if name.startswith("**"): + kind = _VAR_KEYWORD + elif name.startswith("*"): + kind = _VAR_POSITIONAL + ann = annotations.get(name, _empty) + name = name.lstrip("*") + defpos = idx - len(varnames) + len(defaults) + default = defaults[defpos] if defpos >= 0 else _empty + if default is None: + ann = typing.Optional[ann] + if default is not _empty and layout.ellipsis: + default = ellipsis + param = inspect.Parameter(name, kind, annotation=ann, default=default) + params.append(param) + if kind == _VAR_POSITIONAL: + kind = _KEYWORD_ONLY + sig = inspect.Signature(params, + return_annotation=annotations.get('return', _empty), + __validate_parameters__=False) # the special case of nameless parameters if not layout.parameter_names: diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py index e6f6dc379..f11f3cf3d 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py @@ -64,61 +64,109 @@ class ExactEnumerator(object): """ def __init__(self, formatter, result_type=dict): + global EnumType + try: + # Lazy import + from PySide2.QtCore import Qt + EnumType = type(Qt.Key) + except ImportError: + EnumType = None + self.fmt = formatter self.result_type = result_type + self.fmt.level = 0 + self.fmt.after_enum = self.after_enum + self._after_enum = False + + def after_enum(self): + ret = self._after_enum + self._after_enum = False def module(self, mod_name): __import__(mod_name) + self.fmt.mod_name = mod_name with self.fmt.module(mod_name): module = sys.modules[mod_name] members = inspect.getmembers(module, inspect.isclass) functions = inspect.getmembers(module, inspect.isroutine) ret = self.result_type() self.fmt.class_name = None - for func_name, func in functions: - ret.update(self.function(func_name, func)) for class_name, klass in members: ret.update(self.klass(class_name, klass)) + if isinstance(klass, EnumType): + raise SystemError("implement enum instances at module level") + for func_name, func in functions: + ret.update(self.function(func_name, func)) return ret def klass(self, class_name, klass): - if not "Shiboken" in repr(klass.mro()): - # don't look into any foreign classes! - ret = self.result_type() - return ret bases_list = [] for base in klass.__bases__: name = base.__name__ - if name == "object": + if name in ("object", "type"): pass else: modname = base.__module__ name = modname + "." + base.__name__ bases_list.append(name) class_str = "{}({})".format(class_name, ", ".join(bases_list)) + ret = self.result_type() + # class_members = inspect.getmembers(klass) + # gives us also the inherited things. + class_members = sorted(list(klass.__dict__.items())) + subclasses = [] + functions = [] + enums = [] + + for thing_name, thing in class_members: + if inspect.isclass(thing): + subclass_name = ".".join((class_name, thing_name)) + subclasses.append((subclass_name, thing)) + elif inspect.isroutine(thing): + func_name = thing_name.split(".")[0] # remove ".overload" + signature = getattr(thing, "__signature__", None) + if signature is not None: + functions.append((func_name, thing)) + elif type(type(thing)) is EnumType: + enums.append((thing_name, thing)) + init_signature = getattr(klass, "__signature__", None) + enums.sort(key=lambda tup: tup[1]) # sort by enum value + self.fmt.have_body = bool(subclasses or functions or enums or init_signature) + with self.fmt.klass(class_name, class_str): - ret = self.function("__init__", klass) - # class_members = inspect.getmembers(klass) - # gives us also the inherited things. - class_members = sorted(list(klass.__dict__.items())) - subclasses = [] - for thing_name, thing in class_members: - if inspect.isclass(thing): - subclass_name = ".".join((class_name, thing_name)) - subclasses.append((subclass_name, thing)) - else: - func_name = thing_name.split(".")[0] # remove ".overload" - ret.update(self.function(func_name, thing)) + self.fmt.level += 1 + self.fmt.class_name = class_name + if hasattr(self.fmt, "enum"): + # this is an optional feature + for enum_name, value in enums: + with self.fmt.enum(class_name, enum_name, int(value)): + pass for subclass_name, subclass in subclasses: + if klass == subclass: + # this is a side effect of the typing module for Python 2.7 + # via the "._gorg" property, which we can safely ignore. + print("Warning: {class_name} points to itself via {subclass_name}, skipped!" + .format(**locals())) + continue ret.update(self.klass(subclass_name, subclass)) - return ret + self.fmt.class_name = class_name + ret.update(self.function("__init__", klass)) + for func_name, func in functions: + func_kind = get_signature(func, "__func_kind__") + modifier = func_kind if func_kind in ( + "staticmethod", "classmethod") else None + ret.update(self.function(func_name, func, modifier)) + self.fmt.level -= 1 + return ret - def function(self, func_name, func): + def function(self, func_name, func, modifier=None): + self.fmt.level += 1 ret = self.result_type() - signature = getattr(func, '__signature__', None) + signature = func.__signature__ if signature is not None: - with self.fmt.function(func_name, signature) as key: + with self.fmt.function(func_name, signature, modifier) as key: ret[key] = signature + self.fmt.level -= 1 return ret @@ -142,7 +190,7 @@ class SimplifyingEnumerator(ExactEnumerator): is desired. """ - def function(self, func_name, func): + def function(self, func_name, func, modifier=None): ret = self.result_type() signature = get_signature(func, 'existence') sig = stringify(signature) if signature is not None else None @@ -159,11 +207,11 @@ class HintingEnumerator(ExactEnumerator): hinting stubs. Only default values are replaced by "...". """ - def function(self, func_name, func): + def function(self, func_name, func, modifier=None): ret = self.result_type() signature = get_signature(func, 'hintingstub') if signature is not None: - with self.fmt.function(func_name, signature) as key: + with self.fmt.function(func_name, signature, modifier) as key: ret[key] = signature return ret diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py index b34bfb404..3b0825049 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py @@ -43,6 +43,8 @@ from __future__ import print_function, absolute_import tool.py Some useful stuff, see below. +On the function with_metaclass see the answer from Martijn Pieters on +https://stackoverflow.com/questions/18513821/python-metaclass-understanding-the-with-metaclass """ from textwrap import dedent @@ -132,4 +134,21 @@ def build_brace_pattern(level, separators=""): indent = idx * " ", **locals()) return pattern.replace("C", "{").replace("D", "}") + +# Copied from the six module: +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + # eof diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py index 3d25c5690..a0367883a 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py @@ -85,20 +85,6 @@ def formatannotation(annotation, base_module=None): return annotation.__module__ + '.' + annotation.__qualname__ return repr(annotation) -# patching __repr__ to disable the __repr__ of typing.TypeVar: -""" - def __repr__(self): - if self.__covariant__: - prefix = '+' - elif self.__contravariant__: - prefix = '-' - else: - prefix = '~' - return prefix + self.__name__ -""" -def _typevar__repr__(self): - return "typing." + self.__name__ - # Note also that during the tests we have a different encoding that would # break the Python license decorated files without an encoding line. @@ -160,12 +146,18 @@ def list_modules(message): print(" {:23}".format(name), repr(module)[:70]) +orig_typing = True if sys.version_info >= (3,): import typing import inspect inspect.formatannotation = formatannotation else: - from shibokensupport import typing27 as typing + if "typing" not in sys.modules: + orig_typing = False + from shibokensupport import typing27 as typing + sys.modules["typing"] = typing + else: + import typing import inspect namespace = inspect.__dict__ from shibokensupport import backport_inspect as inspect @@ -174,7 +166,6 @@ else: inspect.__doc__ += _doc # force inspect to find all attributes. See "heuristic" in pydoc.py! inspect.__all__ = list(x for x in dir(inspect) if not x.startswith("_")) -typing.TypeVar.__repr__ = _typevar__repr__ # Fix the module names in typing if possible. This is important since # the typing names should be I/O compatible, so that typing.Dict @@ -206,7 +197,7 @@ def move_into_pyside_package(): put_into_package(PySide2.support.signature, importhandler) put_into_package(PySide2.support.signature.lib, enum_sig) - put_into_package(PySide2.support.signature, typing) + put_into_package(None if orig_typing else PySide2.support.signature, typing) put_into_package(PySide2.support.signature, inspect) from shibokensupport.signature import mapping diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 5d6a24016..2110ebe7a 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -55,6 +55,7 @@ import os from shibokensupport.signature import typing from shibokensupport.signature.typing import TypeVar, Generic +from shibokensupport.signature.lib.tool import with_metaclass class ellipsis(object): def __repr__(self): @@ -66,27 +67,15 @@ Variant = typing.Any ModelIndexList = typing.List[int] QImageCleanupFunction = typing.Callable +# unfortunately, typing.Optional[t] expands to typing.Union[t, NoneType] +# Until we can force it to create Optional[t] again, we use this. +NoneType = type(None) + _S = TypeVar("_S") # Building our own Char type, which is much nicer than # Char = typing.Union[str, int] # how do I model the limitation to 1 char? -# Copied from the six module: -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - class _CharMeta(type): def __repr__(self): return '%s.%s' % (self.__module__, self.__name__) @@ -208,7 +197,7 @@ class _Parameterized(object): class ResultVariable(_Parameterized): pass -# Mark the primitive variables to become Sequence, Iterator or List +# Mark the primitive variables to become Sequence, Iterable or List # (decided in the parser). class ArrayLikeVariable(_Parameterized): pass @@ -275,6 +264,7 @@ type_map = {} namespace = globals() # our module's __dict__ type_map.update({ + "...": ellipsis, "bool": bool, "char": Char, "char*": str, @@ -337,6 +327,7 @@ type_map.update({ "unsigned long": int, "unsigned short int": int, # 5.6, RHEL 6.6 "unsigned short": int, + "Unspecified": None, "ushort": int, "void": int, # be more specific? "WId": WId, @@ -357,6 +348,7 @@ type_map.update({ "array GLuint*" : ArrayLikeVariable(int), "array int*" : ArrayLikeVariable(int), "array long long*" : ArrayLikeVariable(int), + "array long*" : ArrayLikeVariable(int), "array short*" : ArrayLikeVariable(int), "array signed char*" : bytes, "array unsigned char*" : bytes, @@ -420,6 +412,7 @@ def init_sample(): "Foo.HANDLE": int, "HANDLE": int, "Null": None, + "nullptr": None, "ObjectType.Identifier": Missing("sample.ObjectType.Identifier"), "OddBool": bool, "PStr": str, diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 204f37384..8d970956b 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -106,7 +106,8 @@ def _parse_line(line): $ """ ret = SimpleNamespace(**re.match(line_re, line, re.VERBOSE).groupdict()) - argstr = ret.arglist + # PYSIDE-1095: Handle arbitrary default expressions + argstr = ret.arglist.replace("->", ".deref.") arglist = _parse_arglist(argstr) args = [] for arg in arglist: @@ -116,7 +117,7 @@ def _parse_line(line): print("KEYWORD", ret) name = name + "_" if "=" in ann: - ann, default = ann.split("=") + ann, default = ann.split("=", 1) tup = name, ann, default else: tup = name, ann @@ -167,7 +168,7 @@ def try_to_guess(thing, valtype): def _resolve_value(thing, valtype, line): if thing in ("0", "None") and valtype: - if valtype.startswith("PySide2."): + if valtype.startswith("PySide2.") or valtype.startswith("typing."): return None map = type_map[valtype] # typing.Any: '_SpecialForm' object has no attribute '__name__' @@ -315,9 +316,9 @@ def calculate_props(line): for idx, tup in enumerate(arglist): name, ann = tup[:2] if ann == "...": - name = "*args" - # copy the fields back :() - ann = 'NULL' # maps to None + name = "*args" if name.startswith("arg_") else "*" + name + # copy the pathed fields back + ann = 'nullptr' # maps to None tup = name, ann arglist[idx] = tup annotations[name] = _resolve_type(ann, line, 0, handle_argvar) diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py index 786a84ecb..44d78c433 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py @@ -86,6 +86,10 @@ PSF LICENSE AGREEMENT FOR PYTHON 3.7.0 to be bound by the terms and conditions of this License Agreement. """ +# This is the typing module for Python 2.7 +# https://github.com/python/typing +# 2019-08-22 + from __future__ import absolute_import, unicode_literals import abc @@ -108,8 +112,11 @@ __all__ = [ 'Any', 'Callable', 'ClassVar', + 'Final', 'Generic', + 'Literal', 'Optional', + 'Protocol', 'Tuple', 'Type', 'TypeVar', @@ -141,6 +148,7 @@ __all__ = [ 'SupportsAbs', 'SupportsComplex', 'SupportsFloat', + 'SupportsIndex', 'SupportsInt', # Concrete collection types. @@ -152,17 +160,20 @@ __all__ = [ 'Set', 'FrozenSet', 'NamedTuple', # Not really a type. + 'TypedDict', # Not really a type. 'Generator', # One-off things. 'AnyStr', 'cast', + 'final', 'get_type_hints', 'NewType', 'no_type_check', 'no_type_check_decorator', 'NoReturn', 'overload', + 'runtime_checkable', 'Text', 'TYPE_CHECKING', ] @@ -447,7 +458,7 @@ def _type_check(arg, msg): if ( type(arg).__name__ in ('_Union', '_Optional') and not getattr(arg, '__origin__', None) or - isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol) + isinstance(arg, TypingMeta) and arg._gorg in (Generic, Protocol) ): raise TypeError("Plain %s is not valid as type argument" % arg) return arg @@ -466,7 +477,7 @@ def _type_repr(obj): return _qualname(obj) return '%s.%s' % (obj.__module__, _qualname(obj)) if obj is Ellipsis: - return('...') + return '...' if isinstance(obj, types.FunctionType): return obj.__name__ return repr(obj) @@ -537,6 +548,157 @@ class _ClassVar(_FinalTypingBase): ClassVar = _ClassVar(_root=True) +class _FinalMeta(TypingMeta): + """Metaclass for _Final""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + self = super(_FinalMeta, cls).__new__(cls, name, bases, namespace) + return self + + +class _Final(_FinalTypingBase): + """A special typing construct to indicate that a name + cannot be re-assigned or overridden in a subclass. + For example: + + MAX_SIZE: Final = 9000 + MAX_SIZE += 1 # Error reported by type checker + + class Connection: + TIMEOUT: Final[int] = 10 + class FastConnector(Connection): + TIMEOUT = 1 # Error reported by type checker + + There is no runtime checking of these properties. + """ + + __metaclass__ = _FinalMeta + __slots__ = ('__type__',) + + def __init__(self, tp=None, **kwds): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(_type_check(item, + '{} accepts only single type.'.format(cls.__name__[1:])), + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + new_tp = _eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) + + def __repr__(self): + r = super(_Final, self).__repr__() + if self.__type__ is not None: + r += '[{}]'.format(_type_repr(self.__type__)) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _Final): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + +Final = _Final(_root=True) + + +def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. + """ + return f + + +class _LiteralMeta(TypingMeta): + """Metaclass for _Literal""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + self = super(_LiteralMeta, cls).__new__(cls, name, bases, namespace) + return self + + +class _Literal(_FinalTypingBase): + """A type that can be used to indicate to type checkers that the + corresponding value has a value literally equivalent to the + provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to the + value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime checking + verifying that the parameter is actually a value instead of a type. + """ + + __metaclass__ = _LiteralMeta + __slots__ = ('__values__',) + + def __init__(self, values=None, **kwds): + self.__values__ = values + + def __getitem__(self, item): + cls = type(self) + if self.__values__ is None: + if not isinstance(item, tuple): + item = (item,) + return cls(values=item, + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + return self + + def __repr__(self): + r = super(_Literal, self).__repr__() + if self.__values__ is not None: + r += '[{}]'.format(', '.join(map(_type_repr, self.__values__))) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__values__)) + + def __eq__(self, other): + if not isinstance(other, _Literal): + return NotImplemented + if self.__values__ is not None: + return self.__values__ == other.__values__ + return self is other + + +Literal = _Literal(_root=True) + + class AnyMeta(TypingMeta): """Metaclass for Any.""" @@ -1122,10 +1284,11 @@ class GenericMeta(TypingMeta, abc.ABCMeta): if base is Generic: raise TypeError("Cannot inherit from plain Generic") if (isinstance(base, GenericMeta) and - base.__origin__ is Generic): + base.__origin__ in (Generic, Protocol)): if gvars is not None: raise TypeError( - "Cannot inherit from Generic[...] multiple types.") + "Cannot inherit from Generic[...] or" + " Protocol[...] multiple times.") gvars = base.__parameters__ if gvars is None: gvars = tvars @@ -1135,8 +1298,10 @@ class GenericMeta(TypingMeta, abc.ABCMeta): if not tvarset <= gvarset: raise TypeError( "Some type variables (%s) " - "are not listed in Generic[%s]" % + "are not listed in %s[%s]" % (", ".join(str(t) for t in tvars if t not in gvarset), + "Generic" if any(b.__origin__ is Generic + for b in bases) else "Protocol", ", ".join(str(g) for g in gvars))) tvars = gvars @@ -1285,25 +1450,21 @@ class GenericMeta(TypingMeta, abc.ABCMeta): "Parameter list to %s[...] cannot be empty" % _qualname(self)) msg = "Parameters to generic types must be types." params = tuple(_type_check(p, msg) for p in params) - if self is Generic: + if self in (Generic, Protocol): # Generic can only be subscripted with unique type variables. if not all(isinstance(p, TypeVar) for p in params): raise TypeError( - "Parameters to Generic[...] must all be type variables") + "Parameters to %s[...] must all be type variables" % self.__name__) if len(set(params)) != len(params): raise TypeError( - "Parameters to Generic[...] must all be unique") + "Parameters to %s[...] must all be unique" % self.__name__) tvars = params args = params elif self in (Tuple, Callable): tvars = _type_vars(params) args = params - elif self is _Protocol: - # _Protocol is internal, don't check anything. - tvars = params - args = params - elif self.__origin__ in (Generic, _Protocol): - # Can't subscript Generic[...] or _Protocol[...]. + elif self.__origin__ in (Generic, Protocol): + # Can't subscript Generic[...] or Protocol[...]. raise TypeError("Cannot subscript already-subscripted %s" % repr(self)) else: @@ -1343,7 +1504,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): # latter, we must extend __instancecheck__ too. For simplicity # we just skip the cache check -- instance checks for generic # classes are supposed to be rare anyways. - if not isinstance(instance, type): + if hasattr(instance, "__class__"): return issubclass(instance.__class__, self) return False @@ -1690,85 +1851,175 @@ def overload(func): return _overload_dummy +_PROTO_WHITELIST = ['Callable', 'Iterable', 'Iterator', + 'Hashable', 'Sized', 'Container', 'Collection', + 'Reversible', 'ContextManager'] + + class _ProtocolMeta(GenericMeta): - """Internal metaclass for _Protocol. + """Internal metaclass for Protocol. - This exists so _Protocol classes can be generic without deriving + This exists so Protocol classes can be generic without deriving from Generic. """ + def __init__(cls, *args, **kwargs): + super(_ProtocolMeta, cls).__init__(*args, **kwargs) + if not cls.__dict__.get('_is_protocol', None): + cls._is_protocol = any(b is Protocol or + isinstance(b, _ProtocolMeta) and + b.__origin__ is Protocol + for b in cls.__bases__) + if cls._is_protocol: + for base in cls.__mro__[1:]: + if not (base in (object, Generic) or + base.__module__ == '_abcoll' and + base.__name__ in _PROTO_WHITELIST or + isinstance(base, TypingMeta) and base._is_protocol or + isinstance(base, GenericMeta) and base.__origin__ is Generic): + raise TypeError('Protocols can only inherit from other protocols,' + ' got %r' % base) + cls._callable_members_only = all(callable(getattr(cls, attr)) + for attr in cls._get_protocol_attrs()) + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + cls.__init__ = _no_init + + def _proto_hook(cls, other): + if not cls.__dict__.get('_is_protocol', None): + return NotImplemented + if not isinstance(other, type): + # Similar error as for issubclass(1, int) + # (also not a chance for old-style classes) + raise TypeError('issubclass() arg 1 must be a new-style class') + for attr in cls._get_protocol_attrs(): + for base in other.__mro__: + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + else: + return NotImplemented + return True + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = classmethod(_proto_hook) - def __instancecheck__(self, obj): - if _Protocol not in self.__bases__: - return super(_ProtocolMeta, self).__instancecheck__(obj) - raise TypeError("Protocols cannot be used with isinstance().") + def __instancecheck__(self, instance): + # We need this method for situations where attributes are assigned in __init__ + if isinstance(instance, type): + # This looks like a fundamental limitation of Python 2. + # It cannot support runtime protocol metaclasses, On Python 2 classes + # cannot be correctly inspected as instances of protocols. + return False + if ((not getattr(self, '_is_protocol', False) or + self._callable_members_only) and + issubclass(instance.__class__, self)): + return True + if self._is_protocol: + if all(hasattr(instance, attr) and + (not callable(getattr(self, attr)) or + getattr(instance, attr) is not None) + for attr in self._get_protocol_attrs()): + return True + return super(GenericMeta, self).__instancecheck__(instance) def __subclasscheck__(self, cls): - if not self._is_protocol: - # No structural checks since this isn't a protocol. - return NotImplemented + if (self.__dict__.get('_is_protocol', None) and + not self.__dict__.get('_is_runtime_protocol', None)): + if (sys._getframe(1).f_globals['__name__'] in ['abc', 'functools'] or + # This is needed because we remove subclasses from unions on Python 2. + sys._getframe(2).f_globals['__name__'] == 'typing'): + return False + raise TypeError("Instance and class checks can only be used with" + " @runtime_checkable protocols") + if (self.__dict__.get('_is_runtime_protocol', None) and + not self._callable_members_only): + if sys._getframe(1).f_globals['__name__'] in ['abc', 'functools']: + return super(GenericMeta, self).__subclasscheck__(cls) + raise TypeError("Protocols with non-method members" + " don't support issubclass()") + return super(_ProtocolMeta, self).__subclasscheck__(cls) - if self is _Protocol: - # Every class is a subclass of the empty protocol. - return True + def _get_protocol_attrs(self): + attrs = set() + for base in self.__mro__[:-1]: # without object + if base.__name__ in ('Protocol', 'Generic'): + continue + annotations = getattr(base, '__annotations__', {}) + for attr in list(base.__dict__.keys()) + list(annotations.keys()): + if (not attr.startswith('_abc_') and attr not in ( + '__abstractmethods__', '__annotations__', '__weakref__', + '_is_protocol', '_is_runtime_protocol', '__dict__', + '__args__', '__slots__', '_get_protocol_attrs', + '__next_in_mro__', '__parameters__', '__origin__', + '__orig_bases__', '__extra__', '__tree_hash__', + '__doc__', '__subclasshook__', '__init__', '__new__', + '__module__', '_MutableMapping__marker', + '__metaclass__', '_gorg', '_callable_members_only')): + attrs.add(attr) + return attrs - # Find all attributes defined in the protocol. - attrs = self._get_protocol_attrs() - for attr in attrs: - if not any(attr in d.__dict__ for d in cls.__mro__): - return False - return True +class Protocol(object): + """Base class for protocol classes. Protocol classes are defined as:: - def _get_protocol_attrs(self): - # Get all Protocol base classes. - protocol_bases = [] - for c in self.__mro__: - if getattr(c, '_is_protocol', False) and c.__name__ != '_Protocol': - protocol_bases.append(c) + class Proto(Protocol): + def meth(self): + # type: () -> int + pass - # Get attributes included in protocol. - attrs = set() - for base in protocol_bases: - for attr in base.__dict__.keys(): - # Include attributes not defined in any non-protocol bases. - for c in self.__mro__: - if (c is not base and attr in c.__dict__ and - not getattr(c, '_is_protocol', False)): - break - else: - if (not attr.startswith('_abc_') and - attr != '__abstractmethods__' and - attr != '_is_protocol' and - attr != '_gorg' and - attr != '__dict__' and - attr != '__args__' and - attr != '__slots__' and - attr != '_get_protocol_attrs' and - attr != '__next_in_mro__' and - attr != '__parameters__' and - attr != '__origin__' and - attr != '__orig_bases__' and - attr != '__extra__' and - attr != '__tree_hash__' and - attr != '__module__'): - attrs.add(attr) + Such classes are primarily used with static type checkers that recognize + structural subtyping (static duck-typing), for example:: - return attrs + class C: + def meth(self): + # type: () -> int + return 0 + + def func(x): + # type: (Proto) -> int + return x.meth() + + func(C()) # Passes static type check + See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable + act as simple-minded runtime protocols that checks only the presence of + given attributes, ignoring their type signatures. -class _Protocol(object): - """Internal base class for protocol classes. + Protocol classes can be generic, they are defined as:: - This implements a simple-minded structural issubclass check - (similar but more general than the one-offs in collections.abc - such as Hashable). + class GenProto(Protocol[T]): + def meth(self): + # type: () -> T + pass """ __metaclass__ = _ProtocolMeta __slots__ = () - _is_protocol = True + def __new__(cls, *args, **kwds): + if cls._gorg is Protocol: + raise TypeError("Type Protocol cannot be instantiated; " + "it can be used only as a base class") + return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) + + +def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol, so that it + can be used with isinstance() and issubclass(). Raise TypeError + if applied to a non-protocol class. + + This allows a simple-minded structural check very similar to the + one-offs in collections.abc such as Hashable. + """ + if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: + raise TypeError('@runtime_checkable can be only applied to protocol classes,' + ' got %r' % cls) + cls._is_runtime_protocol = True + return cls + # Various ABCs mimicking those in collections.abc. # A few are simply re-exported for completeness. @@ -1786,7 +2037,8 @@ class Iterator(Iterable[T_co]): __extra__ = collections_abc.Iterator -class SupportsInt(_Protocol): +@runtime_checkable +class SupportsInt(Protocol): __slots__ = () @abstractmethod @@ -1794,7 +2046,8 @@ class SupportsInt(_Protocol): pass -class SupportsFloat(_Protocol): +@runtime_checkable +class SupportsFloat(Protocol): __slots__ = () @abstractmethod @@ -1802,7 +2055,8 @@ class SupportsFloat(_Protocol): pass -class SupportsComplex(_Protocol): +@runtime_checkable +class SupportsComplex(Protocol): __slots__ = () @abstractmethod @@ -1810,7 +2064,17 @@ class SupportsComplex(_Protocol): pass -class SupportsAbs(_Protocol[T_co]): +@runtime_checkable +class SupportsIndex(Protocol): + __slots__ = () + + @abstractmethod + def __index__(self): + pass + + +@runtime_checkable +class SupportsAbs(Protocol[T_co]): __slots__ = () @abstractmethod @@ -1823,7 +2087,8 @@ if hasattr(collections_abc, 'Reversible'): __slots__ = () __extra__ = collections_abc.Reversible else: - class Reversible(_Protocol[T_co]): + @runtime_checkable + class Reversible(Protocol[T_co]): __slots__ = () @abstractmethod @@ -1996,21 +2261,6 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): return _generic_new(collections.defaultdict, cls, *args, **kwds) -############################ -# Insertion by CT 2019-02-21 -# -class OrderedDict(collections.OrderedDict, MutableMapping[KT, VT]): - __slots__ = () - __extra__ = collections.OrderedDict - - def __new__(cls, *args, **kwds): - if cls._gorg is OrderedDict: - return collections.OrderedDict(*args, **kwds) - return _generic_new(collections.OrderedDict, cls, *args, **kwds) -# -############################ - - class Counter(collections.Counter, Dict[T, int]): __slots__ = () __extra__ = collections.Counter @@ -2100,6 +2350,87 @@ def NamedTuple(typename, fields): return cls +def _check_fails(cls, other): + try: + if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools', 'typing']: + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + except (AttributeError, ValueError): + pass + return False + + +def _dict_new(cls, *args, **kwargs): + return dict(*args, **kwargs) + + +def _typeddict_new(cls, _typename, _fields=None, **kwargs): + total = kwargs.pop('total', True) + if _fields is None: + _fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + + ns = {'__annotations__': dict(_fields), '__total__': total} + try: + # Setting correct module is necessary to make typed dict classes pickleable. + ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return _TypedDictMeta(_typename, (), ns) + + +class _TypedDictMeta(type): + def __new__(cls, name, bases, ns, total=True): + # Create new typed dict class object. + # This method is called directly when TypedDict is subclassed, + # or via _typeddict_new when TypedDict is instantiated. This way + # TypedDict supports all three syntaxes described in its docstring. + # Subclasses and instances of TypedDict return actual dictionaries + # via _dict_new. + ns['__new__'] = _typeddict_new if name == b'TypedDict' else _dict_new + tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns) + + anns = ns.get('__annotations__', {}) + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + anns = {n: _type_check(tp, msg) for n, tp in anns.items()} + for base in bases: + anns.update(base.__dict__.get('__annotations__', {})) + tp_dict.__annotations__ = anns + if not hasattr(tp_dict, '__total__'): + tp_dict.__total__ = total + return tp_dict + + __instancecheck__ = __subclasscheck__ = _check_fails + + +TypedDict = _TypedDictMeta(b'TypedDict', (dict,), {}) +TypedDict.__module__ = __name__ +TypedDict.__doc__ = \ + """A simple typed name space. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type that expects all of its + instances to have a certain set of keys, with each key + associated with a value of a consistent type. This expectation + is not checked at runtime but is only enforced by type checkers. + Usage:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info could be accessed via Point2D.__annotations__. TypedDict + supports an additional equivalent form:: + + Point2D = TypedDict('Point2D', x=int, y=int, label=str) + """ + + def NewType(name, tp): """NewType creates simple unique types with almost zero runtime overhead. NewType(name, tp) is considered a subtype of tp diff --git a/sources/shiboken2/tests/CMakeLists.txt b/sources/shiboken2/tests/CMakeLists.txt index 085a4344c..464707a9a 100644 --- a/sources/shiboken2/tests/CMakeLists.txt +++ b/sources/shiboken2/tests/CMakeLists.txt @@ -1,3 +1,7 @@ +if(BUILD_TESTS) + find_package(Qt5Test 5.12 REQUIRED) +endif() + add_subdirectory(libminimal) if(NOT DEFINED MINIMAL_TESTS) add_subdirectory(libsample) @@ -74,24 +78,24 @@ add_subdirectory(dumpcodemodel) if (NOT APIEXTRACTOR_DOCSTRINGS_DISABLED) # project(sphinxtabletest) -# +# # # TODO # set(sphinxtabletest_SRC sphinxtabletest.cpp) # qt4_automoc(${sphinxtabletest_SRC}) -# +# # include_directories(${QT_INCLUDE_DIR} # ${QT_QTCORE_INCLUDE_DIR} # ${CMAKE_CURRENT_BINARY_DIR} # ${qtdoc_generator_SOURCE_DIR}) -# +# # add_executable(sphinxtabletest ${sphinxtabletest_SRC}) -# +# # target_link_libraries(sphinxtabletest # ${QT_QTTEST_LIBRARY} # ${APIEXTRACTOR_LIBRARY} # qtdoc_generator # genrunner) -# +# # add_test("sphinxtable" sphinxtabletest) # if (INSTALL_TESTS) # install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/sphinxtabletest DESTINATION ${TEST_INSTALL_DIR}) diff --git a/sources/shiboken2/tests/libsample/modifications.cpp b/sources/shiboken2/tests/libsample/modifications.cpp index 98b22f09b..56ba81875 100644 --- a/sources/shiboken2/tests/libsample/modifications.cpp +++ b/sources/shiboken2/tests/libsample/modifications.cpp @@ -149,3 +149,19 @@ Modifications::nonConversionRuleForArgumentWithDefaultValue(ObjectType** object) *object = m_object; return true; } + +void Modifications::setEnumValue(TestEnum e) +{ + m_enumValue = e; +} + +Modifications::TestEnum Modifications::enumValue() const +{ + return m_enumValue; +} + +Modifications::TestEnum Modifications::defaultEnumValue() const +{ + return TestEnumValue2; +} + diff --git a/sources/shiboken2/tests/libsample/modifications.h b/sources/shiboken2/tests/libsample/modifications.h index fa32bdec3..674a05f27 100644 --- a/sources/shiboken2/tests/libsample/modifications.h +++ b/sources/shiboken2/tests/libsample/modifications.h @@ -51,6 +51,11 @@ public: Overloaded_ibPP }; + enum TestEnum { + TestEnumValue1, + TestEnumValue2 + }; + // those overloaded methods should be heavily modified // to push the overload decisor to its limits inline OverloadedModFunc overloaded(int a0, bool b0, int c0, double d0) { return Overloaded_ibid; } @@ -123,8 +128,13 @@ public: // Inject code with a %CONVERTTOPYTHON that receives an user's primitive type. static inline OddBool passOddBool(OddBool ob) { return ob; } + void setEnumValue(TestEnum e = TestEnumValue1); + TestEnum enumValue() const; + TestEnum defaultEnumValue() const; + private: ObjectType* m_object; + TestEnum m_enumValue = TestEnumValue1; }; class LIBSAMPLE_API AbstractModifications : public Modifications diff --git a/sources/shiboken2/tests/samplebinding/modifications_test.py b/sources/shiboken2/tests/samplebinding/modifications_test.py index 1dcd50359..e9a1eaf7f 100644 --- a/sources/shiboken2/tests/samplebinding/modifications_test.py +++ b/sources/shiboken2/tests/samplebinding/modifications_test.py @@ -220,5 +220,15 @@ class ModificationsTest(unittest.TestCase): self.assertTrue(isinstance(res, float)) self.assertEqual(res, em.increment) + def testDefaultValueModifications(self): + # PSYIDE-1095: setEnumValue() has the default value modified to + # calling defaultEnumValue() which returns Modifications.TestEnumValue2. + # This used to generated broken code since defaultEnumValue() was + # qualified by the enum scope. + modifications = Modifications() + modifications.setEnumValue() + self.assertEqual(modifications.enumValue(), Modifications.TestEnumValue2) + + if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml index 19b4948a1..30ad5def7 100644 --- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml @@ -227,7 +227,6 @@ </modify-argument> <modify-argument index="1"> <replace-type modified-type="PStr"/> - <remove-default-expression/> <replace-default-expression with="PStr()"/> </modify-argument> <inject-code class="target" position="end"> @@ -956,6 +955,7 @@ <object-type name="Modifications"> <enum-type name="OverloadedModFunc"/> + <enum-type name="TestEnum"/> <modify-function signature="overloaded(int, bool, int, double)"> <modify-argument index="2"> @@ -1287,6 +1287,11 @@ %PYARG_0 = %CONVERTTOPYTHON[OddBool](%0); </inject-code> </modify-function> + <modify-function signature="setEnumValue(Modifications::TestEnum)"> + <modify-argument index="1"> + <replace-default-expression with="cppSelf->defaultEnumValue()"/> + </modify-argument> + </modify-function> </object-type> <object-type name="AbstractModifications"> |