diff options
Diffstat (limited to 'sources/shiboken2')
-rw-r--r-- | sources/shiboken2/generator/shiboken2/cppgenerator.cpp | 131 | ||||
-rw-r--r-- | sources/shiboken2/generator/shiboken2/cppgenerator.h | 5 | ||||
-rw-r--r-- | sources/shiboken2/generator/shiboken2/shibokengenerator.cpp | 3 | ||||
-rw-r--r-- | sources/shiboken2/generator/shiboken2/shibokengenerator.h | 3 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/CMakeLists.txt | 2 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/basewrapper.cpp | 15 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/basewrapper.h | 4 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/signature.cpp | 655 | ||||
-rw-r--r-- | sources/shiboken2/libshiboken/signature.h | 53 |
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 |