diff options
author | Christian Tismer <tismer@stackless.com> | 2017-05-23 17:10:26 +0200 |
---|---|---|
committer | Christian Tismer <tismer@stackless.com> | 2017-09-20 21:52:50 +0000 |
commit | 30a1c9c41e5e6d4166f171b9477c6f46cafa782f (patch) | |
tree | 7aaa7b100d3fcc0cb8ad2f583928148b6abfbee5 /sources/shiboken2/generator | |
parent | 6678fc1a6353e596965f0daff74afec9d1605c57 (diff) |
Implement introspection with __signature__ package
The signature module was turned into a package under
'PySide2/support/signature'. The package is completely isolated
so that nothing is leaking into the normal import machinery.
The package is also not initialized unless a __signature__ attribute
is accessed. The only change to Python during a PySide run is
the existence of the __signature__ attribute.
As a side effect, all tests run at the same speed as before
this extension.
The module does not actively import PySide modules. Instead,
it inspects sys.modules and reloads its mapping.py if needed.
Example usage:
>>> PySide2.QtWidgets.QGraphicsAnchorLayout.addAnchors.__signature__
>>> PySide2.QtWidgets.QGraphicsAnchorLayout.__signature__
The module has been thoroughly tested on macOS.
I consider this ready.
Task-number: PYSIDE-510
Change-Id: Ibb231a7fbb4ccc1a7249df55e3881a4e21a19c0d
Reviewed-by: Christian Tismer <tismer@stackless.com>
Diffstat (limited to 'sources/shiboken2/generator')
4 files changed, 129 insertions, 13 deletions
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 06300fc00..6334834d3 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -348,6 +348,8 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) QTextStream md(&methodsDefinitions); QString singleMethodDefinitions; QTextStream smd(&singleMethodDefinitions); + QString signaturesString; + QTextStream signatureStream(&signaturesString); s << endl << "// Target ---------------------------------------------------------" << endl << endl; s << "extern \"C\" {" << endl; @@ -383,10 +385,13 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (classContext.forSmartPointer()) continue; writeConstructorWrapper(s, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); } // call operators - else if (rfunc->name() == QLatin1String("operator()")) + else if (rfunc->name() == QLatin1String("operator()")) { writeMethodWrapper(s, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); + } else if (!rfunc->isOperatorOverload()) { if (classContext.forSmartPointer()) { @@ -417,6 +422,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) } writeMethodWrapper(s, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); if (OverloadData::hasStaticAndInstanceFunctions(overloads)) { QString methDefName = cpythonMethodDefinitionName(rfunc); smd << "static PyMethodDef " << methDefName << " = {" << endl; @@ -431,8 +437,10 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) QString className = cpythonTypeName(metaClass); className.remove(QRegExp(QLatin1String("_Type$"))); - if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) + if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { writeCopyFunction(s, classContext); + signatureStream << INDENT << metaClass->fullName() << ".__copy__()" << endl; + } // Write single method definitions s << singleMethodDefinitions; @@ -495,6 +503,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) continue; writeMethodWrapper(s, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); } } @@ -567,7 +576,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) s << endl; writeConverterFunctions(s, metaClass, classContext); - writeClassRegister(s, metaClass, classContext); + writeClassRegister(s, metaClass, classContext, signatureStream); // class inject-code native/end if (!metaClass->typeEntry()->codeSnips().isEmpty()) { @@ -1976,7 +1985,7 @@ void CppGenerator::writeErrorSection(QTextStream& s, OverloadData& overloadData) strArg = QLatin1String("str"); else if (strArg == QLatin1String("PyBytes")) strArg = QLatin1String("\" SBK_STR_NAME \""); - else if (strArg == QLatin1String("PySequece")) + else if (strArg == QLatin1String("PySequence")) strArg = QLatin1String("list"); else if (strArg == QLatin1String("PyTuple")) strArg = QLatin1String("tuple"); @@ -4302,6 +4311,69 @@ void CppGenerator::writeMethodDefinition(QTextStream& s, const AbstractMetaFunct s << ',' << endl; } +static QString resolveRetOrArgType(const AbstractMetaType *someType) +{ + QString strRetArg; + if (CppGenerator::isCString(someType)) { + strRetArg = QLatin1String("str"); + } else if (someType->isPrimitive()) { + const PrimitiveTypeEntry* ptp = static_cast<const PrimitiveTypeEntry*>(someType->typeEntry()); + while (ptp->referencedTypeEntry()) + ptp = ptp->referencedTypeEntry(); + strRetArg = ptp->name(); + } else { + strRetArg = someType->fullName(); + } + strRetArg.replace(QLatin1String("::"), QLatin1String(".")); + return strRetArg; +} + +void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads) +{ + OverloadData overloadData(overloads, this); + const AbstractMetaFunction* rfunc = overloadData.referenceFunction(); + QString funcName = fullPythonFunctionName(rfunc); + + int idx = overloads.length() - 1; + bool multiple = idx > 0; + +// after merging, the #if may be removed! +#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + for (const AbstractMetaFunction *f : overloads) { + QStringList args; + const AbstractMetaArgumentList &arguments = f->arguments(); + for (AbstractMetaArgument *arg : arguments) { +#else + foreach (const AbstractMetaFunction *f, overloads) { + QStringList args; + const AbstractMetaArgumentList &arguments = f->arguments(); + foreach (const AbstractMetaArgument *arg, arguments) { +#endif + QString strArg = resolveRetOrArgType(arg->type()); + if (!arg->defaultValueExpression().isEmpty()) { + strArg += QLatin1Char('='); + QString e = arg->defaultValueExpression(); + e.replace(QLatin1String("::"), QLatin1String(".")); + // the tests insert stuff like Str("<unknown>"): + e.replace(QLatin1Char('"'), QLatin1String("\\\"")); + strArg += e; + } + args << arg->name() + QLatin1Char(':') + strArg; + } + s << INDENT; + // mark the multiple signatures as such, to make it easier to generate different code + if (multiple) + s << idx-- << ':'; + // now calculate the return type. + s << funcName << '(' << args.join(QLatin1Char(',')) << ')'; + AbstractMetaType *returnType = getTypeWithoutContainer(f->type()); + if (returnType) { + s << "->" << resolveRetOrArgType(returnType); + } + s << endl; + } +} + void CppGenerator::writeEnumsInitialization(QTextStream& s, AbstractMetaEnumList& enums) { if (enums.isEmpty()) @@ -4604,7 +4676,8 @@ QString CppGenerator::getInitFunctionName(GeneratorContext &context) const void CppGenerator::writeClassRegister(QTextStream &s, const AbstractMetaClass *metaClass, - GeneratorContext &classContext) + GeneratorContext &classContext, + QTextStream &signatureStream) { const ComplexTypeEntry* classTypeEntry = metaClass->typeEntry(); @@ -4614,6 +4687,21 @@ void CppGenerator::writeClassRegister(QTextStream &s, QString pyTypeName = cpythonTypeName(metaClass); QString initFunctionName = getInitFunctionName(classContext); + + // PYSIDE-510: Create a signatures string for the introspection feature. + s << "// The signatures string for the functions." << endl; + s << "// Multiple signatures have their index \"n:\" in front." << endl; +#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + s << "const char " << initFunctionName << "_SignaturesString[] = R\"\"\"(\n" + << signatureStream.readAll() << ")\"\"\";" << endl << endl; +#else + s << "const char " << initFunctionName << "_SignaturesString[] = \"\"" << endl; + QString line; + while (signatureStream.readLineInto(&line)) { + s << '"' << line << "\\n\"" << endl; + } + s << ';' << endl << endl; +#endif s << "void init_" << initFunctionName; s << "(PyObject* " << enclosingObjectVariable << ")" << endl; s << '{' << endl; @@ -4640,11 +4728,11 @@ void CppGenerator::writeClassRegister(QTextStream &s, } if (!classContext.forSmartPointer()) - s << INDENT << cpythonTypeNameExt(classTypeEntry); + s << INDENT << cpythonTypeNameExt(classTypeEntry) << endl; else - s << INDENT << cpythonTypeNameExt(classContext.preciseType()); + s << INDENT << cpythonTypeNameExt(classContext.preciseType()) << endl; - s << " = reinterpret_cast<PyTypeObject*>(&" << pyTypeName << ");" << endl; + s << INDENT << " = reinterpret_cast<PyTypeObject*>(&" << pyTypeName << ");" << endl; s << endl; // Multiple inheritance @@ -4681,7 +4769,8 @@ void CppGenerator::writeClassRegister(QTextStream &s, s << "\"," << endl; { Indentation indent(INDENT); - s << INDENT << "&" << pyTypeName; + s << INDENT << "&" << pyTypeName << "," << endl; + s << INDENT << initFunctionName << "_SignaturesString"; // Set destructor function if (!metaClass->isNamespace() && !metaClass->hasPrivateDestructor()) { @@ -5069,6 +5158,8 @@ bool CppGenerator::finishGeneration() QTextStream s_globalFunctionImpl(&globalFunctionImpl); QString globalFunctionDecl; QTextStream s_globalFunctionDef(&globalFunctionDecl); + QString signaturesString; + QTextStream signatureStream(&signaturesString); Indentation indent(INDENT); @@ -5089,6 +5180,7 @@ bool CppGenerator::finishGeneration() // Dummy context to satisfy the API. GeneratorContext classContext; writeMethodWrapper(s_globalFunctionImpl, overloads, classContext); + writeSignatureInfo(signatureStream, overloads); writeMethodDefinition(s_globalFunctionDef, overloads); } @@ -5152,6 +5244,7 @@ bool CppGenerator::finishGeneration() if (usePySideExtensions()) { s << includeQDebug; s << "#include <pyside.h>" << endl; + s << "#include <signature.h>" << endl; } s << "#include \"" << getModuleHeaderFileName() << '"' << endl << endl; @@ -5458,7 +5551,25 @@ bool CppGenerator::finishGeneration() } // cleanup staticMetaObject attribute - s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);" << endl; + s << INDENT << "PySide::registerCleanupFunction(cleanTypesAttributes);" << endl << endl; + + // PYSIDE-510: Create a signatures string for the introspection feature. + s << "// The signatures string for the global functions." << endl; + s << "// Multiple signatures have their index \"n:\" in front." << endl; +#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + s << "const char " << moduleName() << "_SignaturesString[] = R\"\"\"(\n" + << signatureStream.readAll() << ")\"\"\";" << endl << endl; +#else + s << "const char " << moduleName() << "_SignaturesString[] = \"\"" << endl; + QString line; + while (signatureStream.readLineInto(&line)) { + s << '"' << line << "\\n\"" << endl; + } + s << ';' << endl; +#endif + // finish the rest of __signature__ initialization. + s << INDENT << "FinishSignatureInitialization(module, " << moduleName() + << "_SignaturesString);" << endl << endl; } s << "SBK_MODULE_INIT_FUNCTION_END" << endl; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.h b/sources/shiboken2/generator/shiboken2/cppgenerator.h index 5dc3f5a15..4877a1694 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.h +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.h @@ -231,13 +231,14 @@ private: void writeClassRegister(QTextStream &s, const AbstractMetaClass *metaClass, - GeneratorContext &classContext); + GeneratorContext &classContext, + QTextStream &signatureStream); void writeClassDefinition(QTextStream &s, const AbstractMetaClass *metaClass, GeneratorContext &classContext); void writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList overloads); void writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads); - + void writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads); /// Writes the implementation of all methods part of python sequence protocol void writeSequenceMethods(QTextStream &s, const AbstractMetaClass *metaClass, diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 7b664e105..670659854 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -324,6 +324,9 @@ QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction* fu else funcName.prepend(fullName + QLatin1Char('.')); } + else { + funcName = packageName() + QLatin1Char('.') + func->name(); + } return funcName; } diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.h b/sources/shiboken2/generator/shiboken2/shibokengenerator.h index 837e7d640..d36962cf1 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.h +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.h @@ -294,7 +294,8 @@ public: QString wrapperName(const AbstractMetaClass* metaClass) const; QString wrapperName(const AbstractMetaType *metaType) const; - static QString fullPythonFunctionName(const AbstractMetaFunction* func); + QString fullPythonFunctionName(const AbstractMetaFunction* func); + static QString protectedEnumSurrogateName(const AbstractMetaEnum* metaEnum); static QString protectedFieldGetterName(const AbstractMetaField* field); static QString protectedFieldSetterName(const AbstractMetaField* field); |