aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/generator/shiboken2
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2017-05-23 17:10:26 +0200
committerChristian Tismer <tismer@stackless.com>2017-09-20 21:52:50 +0000
commit30a1c9c41e5e6d4166f171b9477c6f46cafa782f (patch)
tree7aaa7b100d3fcc0cb8ad2f583928148b6abfbee5 /sources/shiboken2/generator/shiboken2
parent6678fc1a6353e596965f0daff74afec9d1605c57 (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/shiboken2')
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.cpp131
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.h5
-rw-r--r--sources/shiboken2/generator/shiboken2/shibokengenerator.cpp3
-rw-r--r--sources/shiboken2/generator/shiboken2/shibokengenerator.h3
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);