aboutsummaryrefslogtreecommitdiffstats
path: root/sources/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
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')
-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
-rw-r--r--sources/shiboken2/libshiboken/CMakeLists.txt2
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.cpp15
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.h4
-rw-r--r--sources/shiboken2/libshiboken/signature.cpp655
-rw-r--r--sources/shiboken2/libshiboken/signature.h53
9 files changed, 852 insertions, 19 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);
diff --git a/sources/shiboken2/libshiboken/CMakeLists.txt b/sources/shiboken2/libshiboken/CMakeLists.txt
index 6d2d1c88f..3ea7a9f3b 100644
--- a/sources/shiboken2/libshiboken/CMakeLists.txt
+++ b/sources/shiboken2/libshiboken/CMakeLists.txt
@@ -37,6 +37,7 @@ bindingmanager.cpp
threadstatesaver.cpp
typeresolver.cpp
shibokenbuffer.cpp
+signature.cpp
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
@@ -69,6 +70,7 @@ install(FILES
typeresolver.h
shibokenbuffer.h
sbkpython.h
+ signature.h
"${CMAKE_CURRENT_BINARY_DIR}/sbkversion.h"
DESTINATION include/shiboken2${shiboken2_SUFFIX})
install(TARGETS libshiboken EXPORT shiboken2
diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp
index b319fea7e..3f3bcc7f5 100644
--- a/sources/shiboken2/libshiboken/basewrapper.cpp
+++ b/sources/shiboken2/libshiboken/basewrapper.cpp
@@ -51,6 +51,7 @@
#include <sstream>
#include <algorithm>
#include "threadstatesaver.h"
+#include "signature.h"
namespace {
void _destroyParentInfo(SbkObject* obj, bool keepReference);
@@ -723,10 +724,12 @@ void initPrivateData(SbkObjectType* self)
memset(self->d, 0, sizeof(SbkObjectTypePrivate));
}
-bool introduceWrapperType(PyObject* enclosingObject,
- const char* typeName, const char* originalName,
- SbkObjectType* type, ObjectDestructor cppObjDtor,
- SbkObjectType* baseType, PyObject* baseTypes,
+bool introduceWrapperType(PyObject *enclosingObject,
+ const char *typeName, const char *originalName,
+ SbkObjectType *type,
+ const char *signaturesString,
+ ObjectDestructor cppObjDtor,
+ SbkObjectType *baseType, PyObject *baseTypes,
bool isInnerClass)
{
initPrivateData(type);
@@ -744,7 +747,9 @@ bool introduceWrapperType(PyObject* enclosingObject,
}
}
- if (PyType_Ready(reinterpret_cast<PyTypeObject *>(type)) < 0)
+ // PySide-510
+ // here is the single change to support signatures.
+ if (SbkSpecial_Type_Ready(enclosingObject, reinterpret_cast<PyTypeObject *>(type), signaturesString) < 0)
return false;
if (isInnerClass)
diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h
index 002337f3c..a230c1337 100644
--- a/sources/shiboken2/libshiboken/basewrapper.h
+++ b/sources/shiboken2/libshiboken/basewrapper.h
@@ -207,7 +207,9 @@ LIBSHIBOKEN_API void initPrivateData(SbkObjectType* self);
*/
LIBSHIBOKEN_API bool introduceWrapperType(PyObject* enclosingObject,
const char* typeName, const char* originalName,
- SbkObjectType* type, ObjectDestructor cppObjDtor = 0,
+ SbkObjectType* type,
+ const char* signaturesString,
+ ObjectDestructor cppObjDtor = 0,
SbkObjectType* baseType = 0, PyObject* baseTypes = 0,
bool isInnerClass = false);
diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp
new file mode 100644
index 000000000..7333870dd
--- /dev/null
+++ b/sources/shiboken2/libshiboken/signature.cpp
@@ -0,0 +1,655 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $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 "basewrapper.h"
+
+extern "C"
+{
+
+/***************************************************************************
+ ***************************************************************************
+
+
+ The signature C extension
+ =========================
+
+ This module is a C extension for CPython 3.4 and up, and CPython 2.7.
+ It's purpose is to provide support for the __signature__ attribute
+ of builtin PyCFunction objects.
+
+
+ Short excursion on the topic
+ ----------------------------
+
+ Beginning with CPython 3.5, Python functions began to grow a __signature__
+ attribute for normal Python functions. This is totally optional and just
+ a nice-to-have feature in Python.
+
+ PySide, on the other hand, could use __signature__ very much, because the
+ typing info for the 14000+ PySide functions is really missing, and it
+ would be nice to have this info available directly in Python.
+
+
+ How this code works
+ -------------------
+
+ The basic idea is to create a dummy Python function and to use the inspect
+ module to create a signature object. Then, this object is ge to python:
+
+ I added the __signature__ attribute to every function.
+
+ That is a little change to Python that does not harm, but it saves us
+ tons of code, that was needed in the former versions.
+
+ The internal work is done in two steps:
+ All functions get their "signature text" when the module is imported.
+ The actual signature is created later, when the attribute is really used.
+
+ Example:
+
+ The PyCFunction 'QtWidgets.QApplication.palette' is interrogated for its
+ signature. That means 'pyside_sm_get___signature__()' is called.
+ It calls GetSignature_Function which returns the signature if it is found.
+
+ There are actually 2 locations where late initialization occurs:
+ - 'dict' can be no dict but a tuple. That is the argument tuple that
+ was saved by 'PySide_BuildSignatureArgs' at module load time.
+ If so, then 'pyside_type_init' in 'signature.py' will be called,
+ which parses the string and creates the dict.
+ - 'props' can be empty. Then 'create_signature' in 'signature_loader.py'
+ is called, which uses a dummy function to produce a signature instance
+ with the inspect module.
+
+ This module is dedicated to our lovebird "PĆ¼ppi", who died on 2017-09-15.
+
+ ****************************************************************************
+ ****************************************************************************/
+
+#include "signature.h"
+#include <structmember.h>
+
+#define EXTENSION_ENABLED \
+ PY_VERSION_HEX >= 0x03040000 || \
+ (PY_VERSION_HEX < 0x03000000 && PY_VERSION_HEX >= 0x02070000)
+
+#if EXTENSION_ENABLED
+
+// These constants were needed in former versions of the module:
+#define PYTHON_HAS_QUALNAME (PY_VERSION_HEX >= 0x03060000)
+#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_HAS_DESCR_REDUCE (PY_VERSION_HEX >= 0x03040000)
+#define PYTHON_HAS_METH_REDUCE (PYTHON_HAS_DESCR_REDUCE)
+#define PYTHON_NEEDS_ITERATOR_FLAG (!PYTHON_IS_PYTHON3)
+#define PYTHON_EXPOSES_METHODDESCR (PYTHON_IS_PYTHON3)
+
+// These constants are still in use:
+#define PYTHON_USES_D_COMMON (PY_VERSION_HEX >= 0x03020000)
+#define PYTHON_NO_TYPE_IN_FUNCTIONS (!PYTHON_IS_PYTHON3)
+
+typedef struct safe_globals_struc {
+ // init part 1: get arg_dict
+ PyObject *helper_module;
+ PyObject *arg_dict;
+ // init part 2: run module
+ PyObject *sigparse_func;
+ PyObject *createsig_func;
+} safe_globals_struc, *safe_globals;
+
+static safe_globals pyside_globals = 0;
+
+static PyObject *GetSignature_Function(PyCFunctionObject *);
+static PyObject *GetSignature_TypeMod(PyObject *);
+
+static PyObject *PySide_BuildSignatureProps(PyObject *class_mod);
+
+const char helper_module_name[] = "signature_loader";
+const char bootstrap_name[] = "bootstrap";
+const char arg_name[] = "pyside_arg_dict";
+const char func_name[] = "pyside_type_init";
+
+static PyObject *
+CreateSignature(PyObject *props, const char *sig_kind)
+{
+ /*
+ * Here is the new function to create all signatures. It simply calls
+ * into Python and creates a signature object for a dummy-function.
+ * This is so much simpler than using all the attributes explicitly
+ * to support '_signature_is_functionlike()'.
+ */
+ return PyObject_CallFunction(pyside_globals->createsig_func,
+ (char *)"(Os)", props, sig_kind);
+}
+
+static PyObject *
+pyside_cf_get___signature__(PyCFunctionObject *func)
+{
+ return GetSignature_Function(func);
+}
+
+static PyObject *
+pyside_sm_get___signature__(PyObject *sm)
+{
+ PyObject *func, *ret;
+
+ func = PyObject_GetAttrString(sm, "__func__");
+ ret = GetSignature_Function((PyCFunctionObject *)func);
+ Py_XDECREF(func);
+ return ret;
+}
+
+static PyObject *
+pyside_md_get___signature__(PyMethodDescrObject *descr)
+{
+ PyCFunctionObject *func;
+ PyObject *result;
+
+ func = (PyCFunctionObject *)
+ PyCFunction_NewEx(descr->d_method,
+#if PYTHON_USES_D_COMMON
+ (PyObject *)descr->d_common.d_type, NULL
+#else
+ (PyObject *)descr->d_type, NULL
+#endif
+ );
+ if (func == NULL)
+ return NULL;
+ result = pyside_cf_get___signature__(func);
+ Py_DECREF(func);
+ return result;
+}
+
+static PyObject *
+pyside_tp_get___signature__(PyObject *typemod)
+{
+ return GetSignature_TypeMod(typemod);
+}
+
+static PyObject *
+GetSignature_Function(PyCFunctionObject *func)
+{
+ PyObject *typemod, *type_name, *dict, *props, *value, *selftype;
+ PyObject *func_name = PyObject_GetAttrString((PyObject *)func, "__name__");
+ const char *sig_kind;
+ int flags;
+
+ selftype = func->m_self;
+ if (selftype == NULL) {
+#if PYTHON_NO_TYPE_IN_FUNCTIONS
+ selftype = PyDict_GetItem(pyside_globals->arg_dict, (PyObject *)func);
+ }
+ if (selftype == NULL) {
+#endif
+ if (!PyErr_Occurred()) {
+ PyErr_Format(PyExc_SystemError,
+ "the signature for \"%s\" should exist", func->m_ml->ml_name);
+ }
+ return NULL;
+ }
+ if ((PyType_Check(selftype) || PyModule_Check(selftype)))
+ typemod = selftype;
+ else
+ typemod = (PyObject *)Py_TYPE(selftype);
+ type_name = PyObject_GetAttrString(typemod, "__name__");
+ if (type_name == NULL)
+ Py_RETURN_NONE;
+ dict = PyDict_GetItem(pyside_globals->arg_dict, type_name);
+ Py_DECREF(type_name);
+ if (dict == NULL)
+ Py_RETURN_NONE;
+ if (PyTuple_Check(dict)) {
+ /*
+ * We do the initialization lazily.
+ * This has also the advantage that we can freely import PySide.
+ */
+ dict = PySide_BuildSignatureProps(typemod);
+ if (dict == NULL)
+ Py_RETURN_NONE;
+ }
+ props = PyDict_GetItem(dict, func_name);
+ if (props == NULL)
+ Py_RETURN_NONE;
+ flags = PyCFunction_GET_FLAGS(func);
+ if (flags & METH_CLASS)
+ sig_kind = "classmethod";
+ else if (flags & METH_STATIC)
+ sig_kind = "staticmethod";
+ else
+ sig_kind = "method";
+ value = PyDict_GetItemString(props, sig_kind);
+ if (value == NULL) {
+ // we need to compute a signature object
+ value = CreateSignature(props, sig_kind);
+ if (value != NULL) {
+ if (PyDict_SetItemString(props, sig_kind, value) < 0)
+ return NULL;
+ }
+ else
+ Py_RETURN_NONE;
+ }
+ return Py_INCREF(value), value;
+}
+
+static PyObject *
+GetSignature_TypeMod(PyObject *ob)
+{
+ PyObject *ob_name, *dict, *props, *value;
+ const char *sig_kind;
+
+ ob_name = PyObject_GetAttrString(ob, "__name__");
+ dict = PyDict_GetItem(pyside_globals->arg_dict, ob_name);
+ if (dict == NULL)
+ Py_RETURN_NONE;
+ if (PyTuple_Check(dict)) {
+ dict = PySide_BuildSignatureProps(ob);
+ if (dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ }
+ props = PyDict_GetItem(dict, ob_name);
+ Py_DECREF(ob_name);
+ if (props == NULL)
+ Py_RETURN_NONE;
+ sig_kind = "method";
+ value = PyDict_GetItemString(props, sig_kind);
+ if (value == NULL) {
+ // we need to compute a signature object
+ value = CreateSignature(props, sig_kind);
+ if (value != NULL) {
+ if (PyDict_SetItemString(props, sig_kind, value) < 0)
+ return NULL;
+ }
+ else
+ Py_RETURN_NONE;
+ }
+ return Py_INCREF(value), value;
+}
+
+
+const char *PySide_PythonCode = (const char *)
+ "from __future__ import print_function, absolute_import\n"
+ "import sys, os, traceback\n"
+
+ "pyside_package_dir = os.environ.get('PYSIDE_PACKAGE_DIR', '.')\n"
+ "__file__ = os.path.join(pyside_package_dir, 'support', 'signature', 'loader.py')\n"
+
+ "def bootstrap():\n"
+ " try:\n"
+ " with open(__file__) as _f:\n"
+ " exec(compile(_f.read(), __file__, 'exec'))\n"
+ " except Exception as e:\n"
+ " print('Exception:', e)\n"
+ " traceback.print_exc(file=sys.stdout)\n"
+ " globals().update(locals())\n"
+ ;
+
+static safe_globals_struc *
+init_phase_1(void)
+{
+ safe_globals_struc *p;
+ PyObject *d, *v;
+
+ p = (safe_globals_struc *)malloc(sizeof(safe_globals_struc));
+ if (p == NULL)
+ goto error;
+ p->helper_module = PyImport_AddModule((char *) helper_module_name);
+ if (p->helper_module == NULL)
+ goto error;
+
+ // Initialize the module
+ d = PyModule_GetDict(p->helper_module);
+ if (PyDict_SetItemString(d, "__builtins__", PyEval_GetBuiltins()) < 0)
+ goto error;
+ v = PyRun_String(PySide_PythonCode, Py_file_input, d, d);
+ if (v == NULL)
+ goto error;
+ Py_DECREF(v);
+
+ // Build a dict for the prepared arguments
+ p->arg_dict = PyDict_New();
+ if (p->arg_dict == NULL)
+ goto error;
+ if (PyObject_SetAttrString(p->helper_module, arg_name, p->arg_dict) < 0)
+ goto error;
+ return p;
+
+error:
+ PyErr_SetString(PyExc_SystemError, "could not initialize part 1");
+ return NULL;
+}
+
+static int
+init_phase_2(safe_globals_struc *p)
+{
+ PyObject *bootstrap_func;
+
+ bootstrap_func = PyObject_GetAttrString(p->helper_module, bootstrap_name);
+ if (bootstrap_func == NULL)
+ goto error;
+ if (PyObject_CallFunction(bootstrap_func, (char *)"()") == NULL)
+ goto error;
+ // now the loader is initialized
+ p->sigparse_func = PyObject_GetAttrString(p->helper_module, func_name);
+ if (p->sigparse_func == NULL)
+ goto error;
+ p->createsig_func = PyObject_GetAttrString(p->helper_module, "create_signature");
+ if (p->createsig_func == NULL)
+ goto error;
+ return 0;
+
+error:
+ PyErr_SetString(PyExc_SystemError, "could not initialize part 2");
+ return -1;
+}
+
+static int
+add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp)
+{
+ PyObject *dict = type->tp_dict;
+
+ for (; gsp->name != NULL; gsp++) {
+ PyObject *descr;
+ if (PyDict_GetItemString(dict, gsp->name))
+ continue;
+ descr = PyDescr_NewGetSet(type, gsp);
+ if (descr == NULL)
+ return -1;
+ if (PyDict_SetItemString(dict, gsp->name, descr) < 0) {
+ Py_DECREF(descr);
+ return -1;
+ }
+ Py_DECREF(descr);
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Augmenting builtin types with a __signature__ attribute.
+//
+// This is a harmless change to Python, similar like __text_signature__.
+// We could avoid it, but then we would need to copy quite some module
+// initialization functions which are pretty version- and word size
+// dependent. I think this little patch is the lesser of the two evils.
+//
+// Please note that in fact we are modifying 'type', the metaclass of all
+// objects, because we add new functionality.
+//
+static PyGetSetDef new_PyCFunction_getsets[] = {
+ {(char *) "__signature__", (getter)pyside_cf_get___signature__},
+ {0}
+};
+
+static PyGetSetDef new_PyStaticMethod_getsets[] = {
+ {(char *) "__signature__", (getter)pyside_sm_get___signature__},
+ {0}
+};
+
+static PyGetSetDef new_PyMethodDescr_getsets[] = {
+ {(char *) "__signature__", (getter)pyside_md_get___signature__},
+ {0}
+};
+
+static PyGetSetDef new_PyType_getsets[] = {
+ {(char *) "__signature__", (getter)pyside_tp_get___signature__},
+ {0}
+};
+
+////////////////////////////////////////////////////////////////////////////
+//
+// This special Type_Ready does certain initializations earlier with
+// our new version.
+//
+static int
+PySideType_Ready(PyTypeObject *type)
+{
+ PyObject *md;
+ static int init_done = 0;
+
+ if (!init_done) {
+ // Python2 does not expose certain types. We look them up:
+ // PyMethodDescr_Type 'type(str.__dict__["split"])'
+ // PyClassMethodDescr_Type. 'type(dict.__dict__["fromkeys"])'
+ // The latter is not needed until we use class methods in PySide.
+ md = PyDict_GetItemString(PyString_Type.tp_dict, "split");
+ if (md == NULL
+ || PyType_Ready(Py_TYPE(md)) < 0
+ || add_more_getsets(Py_TYPE(md), new_PyMethodDescr_getsets) < 0
+ || add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets) < 0
+ || add_more_getsets(&PyStaticMethod_Type, new_PyStaticMethod_getsets) < 0
+ || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0)
+ return -1;
+ init_done = 1;
+ }
+ return PyType_Ready(type);
+}
+
+#if PYTHON_NO_TYPE_IN_FUNCTIONS
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *sm_callable;
+ PyObject *sm_dict;
+} staticmethod;
+
+static int
+build_func_to_type(PyObject *obtype)
+{
+ PyTypeObject *type = (PyTypeObject *)obtype;
+ PyObject *dict = type->tp_dict;
+ PyMethodDef *meth = type->tp_methods;
+
+ if (meth == 0)
+ return 0;
+
+ for (; meth->ml_name != NULL; meth++) {
+ if (meth->ml_flags & METH_STATIC) {
+ PyObject *descr = PyDict_GetItemString(dict, meth->ml_name);
+ if (descr == NULL)
+ return -1;
+ staticmethod *sm = (staticmethod *)descr;
+ PyObject *cfunc = sm->sm_callable;
+ if (cfunc == NULL)
+ return -1;
+ if (PyDict_SetItem(pyside_globals->arg_dict, cfunc, obtype) < 0)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+#endif
+
+static int
+PySide_BuildSignatureArgs(PyObject *module, PyObject *type,
+ const char *signatures)
+{
+ PyObject *type_name, *arg_tup;
+ const char *name = NULL;
+ static int init_done = 0;
+
+ if (!init_done) {
+ pyside_globals = init_phase_1();
+ if (pyside_globals < 0)
+ return -1;
+ init_done = 1;
+ }
+ arg_tup = Py_BuildValue("(Os)", type, signatures);
+ if (arg_tup == NULL)
+ return -1;
+ if (!PyModule_Check(module))
+ return 0;
+ name = PyModule_GetName(module);
+ if (name == NULL)
+ return -1;
+ if (strncmp(name, "PySide2.Qt", 10) != 0)
+ return 0;
+ /*
+ * Normally, we would now just call the Python function with the
+ * arguments and then continue processing.
+ * But it is much better to delay the second part until it is
+ * really needed. Why?
+ *
+ * - by doing it late, we save initialization time when no signatures
+ * are requested,
+ * - by calling the python function late, we can freely import PySide
+ * without recursion problems.
+ */
+ type_name = PyObject_GetAttrString(type, "__name__");
+ if (type_name == NULL)
+ return -1;
+ if (PyDict_SetItem(pyside_globals->arg_dict, type_name, arg_tup) < 0)
+ return -1;
+ return 0;
+}
+
+static PyObject *
+PySide_BuildSignatureProps(PyObject *classmod)
+{
+ PyObject *arg_tup, *dict, *type_name;
+ static int init_done = 0;
+
+ if (!init_done) {
+ if (init_phase_2(pyside_globals) < 0)
+ return NULL;
+ init_done = 1;
+ }
+ /*
+ * Here is the second part of the function.
+ * This part will be called on-demand when needed by some attribute.
+ * We simply pick up the arguments that we stored here and replace
+ * them by the function result.
+ */
+ type_name = PyObject_GetAttrString(classmod, "__name__");
+ if (type_name == NULL)
+ return NULL;
+ arg_tup = PyDict_GetItem(pyside_globals->arg_dict, type_name);
+ if (arg_tup == NULL)
+ return NULL;
+ dict = PyObject_CallObject(pyside_globals->sigparse_func, arg_tup);
+ if (dict == NULL)
+ return NULL;
+
+ // We replace the arguments by the result dict.
+ if (PyDict_SetItem(pyside_globals->arg_dict, type_name, dict) < 0)
+ return NULL;
+ Py_DECREF(type_name);
+ return dict;
+}
+
+#endif // EXTENSION_ENABLED
+
+int
+SbkSpecial_Type_Ready(PyObject *module, PyTypeObject *type,
+ const char *signatures)
+{
+ int ret;
+#if EXTENSION_ENABLED
+ if (PySideType_Ready(type) < 0)
+ return -1;
+ ret = PySide_BuildSignatureArgs(module, (PyObject *)type, signatures);
+#else
+ ret = PyType_Ready(type);
+#endif
+ if (ret < 0) {
+ PyErr_Print();
+ PyErr_SetNone(PyExc_ImportError);
+ }
+ return ret;
+}
+
+static int
+PySide_FinishSignatures(PyObject *module, const char *signatures)
+{
+ const char *name = NULL;
+
+ // CRUCIAL: Do not call this on "testbinding":
+ // The module is different and should not get signatures, anyway.
+ name = PyModule_GetName(module);
+ if (name == NULL)
+ return -1;
+ if (strncmp(name, "PySide2.Qt", 10) != 0)
+ return 0;
+
+ // we abuse the call for types, since they both have a __name__ attribute.
+ if (PySide_BuildSignatureArgs(module, module, signatures) < 0)
+ return -1;
+
+#if PYTHON_NO_TYPE_IN_FUNCTIONS
+ /*
+ * Python2 does not abuse the 'm_self' field for the type. So we need to
+ * supply this for all static methods.
+ *
+ * Note: This function crashed when called from PySide_BuildSignatureArgs.
+ * Probably this was too early.
+ */
+ {
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ PyObject *dict = PyModule_GetDict(module);
+
+ if (dict == NULL)
+ return -1;
+
+ while (PyDict_Next(dict, &pos, &key, &value)) {
+ if (PyType_Check(value)) {
+ if (build_func_to_type(value) < 0)
+ return -1;
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+void
+FinishSignatureInitialization(PyObject *module, const char *signatures)
+{
+#if EXTENSION_ENABLED
+ if (PySide_FinishSignatures(module, signatures) < 0) {
+ PyErr_Print();
+ PyErr_SetNone(PyExc_ImportError);
+ }
+#endif
+}
+
+} //extern "C"
diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h
new file mode 100644
index 000000000..d134e4c82
--- /dev/null
+++ b/sources/shiboken2/libshiboken/signature.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $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 SIGNATURE_H
+#define SIGNATURE_H
+
+#include "sbkpython.h"
+
+extern "C"
+{
+
+LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char*);
+LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char*);
+
+} // extern "C"
+
+#endif // SIGNATURE_H