aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2020-11-18 18:25:07 +0100
committerChristian Tismer <tismer@stackless.com>2020-11-30 23:48:23 +0000
commitfca3aada26d4ec3f9431d2983bf7135ecb0cb547 (patch)
tree22e2b4b88dfc25c7154f19be598e7a2c6c66344c
parent3d137f78105fe71bffeabfd6dd763b5502c58cf5 (diff)
cppgenerator: rework keyword handling regarding unknown keywords
PySide has a distinction between functions with simple arguments and functions with keyword arguments for optional keywords. When a function has keywords specified in one or more signature branch, it gets the METH_KEYWORDS flag. In this case, it is checked that no argument is given positional and per keyword at the same time. Completely forgotten was to check which keywords are allowed in that branch, if at all. The problem was much complicated because constructors can contain extra signals and properties. At the same time, all further error messages are generated with Python. This adds necessary flexibility when features are active. All PyBuildValue objects were refcount leaking. That has been replaced by static createStaticString expressions. The `argNames` structure is no longer needed by the improved algorithm. Change-Id: Ic297912c47231720f61c7d4b79b46a1e376a9941 Fixes: PYSIDE-1305 Task-number: PYSIDE-1019 Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io> (cherry picked from commit b6e57864e5f4d470196f7425c631981de6d1aeb4) Reviewed-by: Christian Tismer <tismer@stackless.com>
-rw-r--r--sources/pyside2/libpyside/pyside.cpp44
-rw-r--r--sources/pyside2/libpyside/pyside.h4
-rw-r--r--sources/pyside2/libpyside/pysidestaticstrings.cpp6
-rw-r--r--sources/pyside2/libpyside/pysidestaticstrings.h6
-rw-r--r--sources/pyside2/tests/pysidetest/constructor_properties_test.py34
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.cpp130
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.h3
-rw-r--r--sources/shiboken2/generator/shiboken2/shibokengenerator.cpp10
-rw-r--r--sources/shiboken2/generator/shiboken2/shibokengenerator.h2
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.cpp5
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.h6
-rw-r--r--sources/shiboken2/libshiboken/sbkstaticstrings.cpp1
-rw-r--r--sources/shiboken2/libshiboken/sbkstaticstrings.h1
-rw-r--r--sources/shiboken2/libshiboken/signature.h2
-rw-r--r--sources/shiboken2/libshiboken/signature/signature.cpp110
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py26
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py4
17 files changed, 298 insertions, 96 deletions
diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp
index 219b99d48..297e5a3b2 100644
--- a/sources/pyside2/libpyside/pyside.cpp
+++ b/sources/pyside2/libpyside/pyside.cpp
@@ -121,38 +121,36 @@ static bool _setProperty(PyObject *qObj, PyObject *name, PyObject *value, bool *
return true;
}
-bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds, const char **blackList, unsigned int blackListSize)
+bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds)
{
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(kwds, &pos, &key, &value)) {
- if (!blackListSize || !std::binary_search(blackList, blackList + blackListSize, std::string(Shiboken::String::toCString(key)))) {
- QByteArray propName(Shiboken::String::toCString(key));
- bool accept = false;
- if (metaObj->indexOfProperty(propName) != -1) {
- if (!_setProperty(qObj, key, value, &accept))
- return false;
- } else {
- propName.append("()");
- if (metaObj->indexOfSignal(propName) != -1) {
- accept = true;
- propName.prepend('2');
- if (!PySide::Signal::connect(qObj, propName, value))
- return false;
- }
- }
- if (!accept) {
- // PYSIDE-1019: Allow any existing attribute in the constructor.
- if (!_setProperty(qObj, key, value, &accept))
+ QByteArray propName(Shiboken::String::toCString(key));
+ bool accept = false;
+ if (metaObj->indexOfProperty(propName) != -1) {
+ if (!_setProperty(qObj, key, value, &accept))
+ return false;
+ } else {
+ propName.append("()");
+ if (metaObj->indexOfSignal(propName) != -1) {
+ accept = true;
+ propName.prepend('2');
+ if (!PySide::Signal::connect(qObj, propName, value))
return false;
}
- if (!accept) {
- PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal",
- propName.constData());
+ }
+ if (!accept) {
+ // PYSIDE-1019: Allow any existing attribute in the constructor.
+ if (!_setProperty(qObj, key, value, &accept))
return false;
- }
+ }
+ if (!accept) {
+ PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal",
+ propName.constData());
+ return false;
}
}
return true;
diff --git a/sources/pyside2/libpyside/pyside.h b/sources/pyside2/libpyside/pyside.h
index c1a298cc8..a465fec47 100644
--- a/sources/pyside2/libpyside/pyside.h
+++ b/sources/pyside2/libpyside/pyside.h
@@ -71,12 +71,10 @@ inline Py_ssize_t hash(const T& value)
* Fill QObject properties and do signal connections using the values found in \p kwds dictonary.
* \param qObj PyObject fot the QObject.
* \param metaObj QMetaObject of \p qObj.
- * \param blackList keys to be ignored in kwds dictionary, this string list MUST be sorted.
- * \param blackListSize numbe rof elements in blackList.
* \param kwds key->value dictonary.
* \return True if everything goes well, false with a Python error setted otherwise.
*/
-PYSIDE_API bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds, const char **blackList, unsigned int blackListSize);
+PYSIDE_API bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj, PyObject *kwds);
/**
* If the type \p T was registered on Qt meta type system with Q_DECLARE_METATYPE macro, this class will initialize
diff --git a/sources/pyside2/libpyside/pysidestaticstrings.cpp b/sources/pyside2/libpyside/pysidestaticstrings.cpp
index 760d77632..2dab2caa9 100644
--- a/sources/pyside2/libpyside/pysidestaticstrings.cpp
+++ b/sources/pyside2/libpyside/pysidestaticstrings.cpp
@@ -60,4 +60,10 @@ STATIC_STRING_IMPL(name, "name")
STATIC_STRING_IMPL(property, "property")
STATIC_STRING_IMPL(select_id, "select_id")
} // namespace PyName
+namespace PyMagicName
+{
+STATIC_STRING_IMPL(doc, "__doc__")
+STATIC_STRING_IMPL(name, "__name__")
+STATIC_STRING_IMPL(property_methods, "__property_methods__")
+} // namespace PyMagicName
} // namespace PySide
diff --git a/sources/pyside2/libpyside/pysidestaticstrings.h b/sources/pyside2/libpyside/pysidestaticstrings.h
index 1222d8f47..54d1ab9cd 100644
--- a/sources/pyside2/libpyside/pysidestaticstrings.h
+++ b/sources/pyside2/libpyside/pysidestaticstrings.h
@@ -55,6 +55,12 @@ PyObject *name();
PyObject *property();
PyObject *select_id();
} // namespace PyName
+namespace PyMagicName
+{
+PyObject *doc();
+PyObject *name();
+PyObject *property_methods();
+} // namespace PyMagicName
} // namespace PySide
#endif // PYSIDESTRINGS_H
diff --git a/sources/pyside2/tests/pysidetest/constructor_properties_test.py b/sources/pyside2/tests/pysidetest/constructor_properties_test.py
index 139091fed..5d1027048 100644
--- a/sources/pyside2/tests/pysidetest/constructor_properties_test.py
+++ b/sources/pyside2/tests/pysidetest/constructor_properties_test.py
@@ -47,11 +47,13 @@ init_test_paths(False)
from helper.usesqapplication import UsesQApplication
from PySide2.QtCore import Qt
-from PySide2.QtWidgets import QApplication, QLabel, QFrame
+from PySide2.QtGui import QColor
+from PySide2.QtWidgets import QAction, QApplication, QFrame, QLabel
class ConstructorPropertiesTest(UsesQApplication):
+ # PYSIDE-1019: First property extension was support by the constructor.
def testCallConstructor(self):
label = QLabel(
frameStyle=QFrame.Panel | QFrame.Sunken,
@@ -65,6 +67,34 @@ class ConstructorPropertiesTest(UsesQApplication):
))
+class DiverseKeywordsTest(UsesQApplication):
+
+ def testDuplicateKeyword(self):
+ r, g, b, a = 1, 2, 3, 4
+ with self.assertRaises(TypeError) as cm:
+ QColor(r, g, b, a, a=0)
+ self.assertTrue("multiple" in cm.exception.args[0])
+
+ # PYSIDE-1305: Handle keyword args correctly.
+ def testUndefinedKeyword(self):
+ r, g, b, a = 1, 2, 3, 4
+ # From the jira issue:
+ with self.assertRaises(AttributeError) as cm:
+ QColor(r, g, b, a, alpha=0)
+ self.assertTrue("unsupported" in cm.exception.args[0])
+
+ # PYSIDE-1305: Handle keyword args correctly.
+ def testUndefinedConstructorKeyword(self):
+ # make sure that the given attribute lands in the constructor
+ x = QAction(autoRepeat=False)
+ self.assertEqual(x.autoRepeat(), False)
+ x = QAction(autoRepeat=True)
+ self.assertEqual(x.autoRepeat(), True)
+ # QAction derives from QObject, and so the missing attributes
+ # in the constructor are reported as AttributeError.
+ with self.assertRaises(AttributeError):
+ QAction(some_other_name=42)
+
+
if __name__ == '__main__':
unittest.main()
-
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
index 30430c01f..ff44db955 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
@@ -1831,6 +1831,11 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over
usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue();
}
+ s << INDENT << "PyObject *errInfo{};\n";
+ s << INDENT << "SBK_UNUSED(errInfo)\n";
+ s << INDENT << "static const char *fullName = \""
+ << fullPythonFunctionName(rfunc, true) << "\";\n";
+ s << INDENT << "SBK_UNUSED(fullName)\n";
if (maxArgs > 0) {
s << INDENT << "int overloadId = -1;\n";
s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR;
@@ -1866,28 +1871,8 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun
s << "static int\n";
s << cpythonFunctionName(rfunc) << "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n";
- QSet<QString> argNamesSet;
- if (usePySideExtensions() && metaClass->isQObject()) {
- // Write argNames variable with all known argument names.
- const OverloadData::MetaFunctionList &overloads = overloadData.overloads();
- for (const AbstractMetaFunction *func : overloads) {
- const AbstractMetaArgumentList &arguments = func->arguments();
- for (const AbstractMetaArgument *arg : arguments) {
- if (arg->defaultValueExpression().isEmpty() || func->argumentRemoved(arg->argumentIndex() + 1))
- continue;
- argNamesSet << arg->name();
- }
- }
- QStringList argNamesList = argNamesSet.values();
- std::sort(argNamesList.begin(), argNamesList.end());
- if (argNamesList.isEmpty()) {
- s << INDENT << "const char **argNames{};\n";
- } else {
- s << INDENT << "const char *argNames[] = {\""
- << argNamesList.join(QLatin1String("\", \"")) << "\"};\n";
- }
+ if (usePySideExtensions() && metaClass->isQObject())
s << INDENT << "const QMetaObject *metaObject;\n";
- }
s << INDENT << "SbkObject *sbkSelf = reinterpret_cast<SbkObject *>(self);\n";
@@ -1927,6 +1912,8 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun
{
Indentation indent(INDENT);
s << INDENT << "delete cptr;\n";
+ if (overloadData.maxArgs() > 0)
+ s << INDENT << "Py_XDECREF(errInfo);\n";
s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
}
s << INDENT << "}\n";
@@ -1947,19 +1934,24 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun
s << INDENT << "if (Shiboken::BindingManager::instance().hasWrapper(cptr)) {\n";
{
Indentation indent(INDENT);
- s << INDENT << "Shiboken::BindingManager::instance().releaseWrapper(Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n";
+ s << INDENT << "Shiboken::BindingManager::instance().releaseWrapper("
+ "Shiboken::BindingManager::instance().retrieveWrapper(cptr));\n";
}
s << INDENT << "}\n";
s << INDENT << "Shiboken::BindingManager::instance().registerWrapper(sbkSelf, cptr);\n";
// Create metaObject and register signal/slot
+ bool errHandlerNeeded = overloadData.maxArgs() > 0;
if (metaClass->isQObject() && usePySideExtensions()) {
+ errHandlerNeeded = true;
s << Qt::endl << INDENT << "// QObject setup\n";
s << INDENT << "PySide::Signal::updateSourceObject(self);\n";
s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties\n";
- s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, "
- << argNamesSet.count() << "))\n" << indent(INDENT)
- << INDENT << returnStatement(m_currentErrorCode) << '\n' << outdent(INDENT);
+ s << INDENT << "if (errInfo && PyDict_Check(errInfo)) {\n" << indent(INDENT)
+ << INDENT << "if (!PySide::fillQtProperties(self, metaObject, errInfo))\n" << indent(INDENT)
+ << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n" << outdent(INDENT)
+ << INDENT << "Py_DECREF(errInfo);\n" << outdent(INDENT)
+ << INDENT << "};\n";
}
// Constructor code injections, position=end
@@ -1997,7 +1989,7 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun
s << Qt::endl;
s << Qt::endl << INDENT << "return 1;\n";
- if (overloadData.maxArgs() > 0)
+ if (errHandlerNeeded)
writeErrorSection(s, overloadData);
s<< "}\n\n";
}
@@ -2149,8 +2141,11 @@ void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overl
s << INDENT << "if (numArgs > " << maxArgs << ") {\n";
{
Indentation indent(INDENT);
- s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): too many arguments\");\n";
- s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ s << INDENT << "static PyObject *const too_many = "
+ "Shiboken::String::createStaticString(\">\");\n";
+ s << INDENT << "errInfo = too_many;\n";
+ s << INDENT << "Py_INCREF(errInfo);\n";
+ s << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n";
}
s << INDENT << '}';
}
@@ -2162,8 +2157,11 @@ void CppGenerator::writeArgumentsInitializer(QTextStream &s, OverloadData &overl
s << "if (numArgs < " << minArgs << ") {\n";
{
Indentation indent(INDENT);
- s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): not enough arguments\");\n";
- s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ s << INDENT << "static PyObject *const too_few = "
+ "Shiboken::String::createStaticString(\"<\");\n";
+ s << INDENT << "errInfo = too_few;\n";
+ s << INDENT << "Py_INCREF(errInfo);\n";
+ s << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;\n";
}
s << INDENT << '}';
}
@@ -2297,11 +2295,13 @@ void CppGenerator::writeErrorSection(QTextStream &s, OverloadData &overloadData)
const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
s << Qt::endl << INDENT << cpythonFunctionName(rfunc) << "_TypeError:\n";
Indentation indentation(INDENT);
- QString funcName = fullPythonFunctionName(rfunc);
+ QString funcName = fullPythonFunctionName(rfunc, true);
QString argsVar = pythonFunctionWrapperUsesListOfArguments(overloadData)
? QLatin1String("args") : QLatin1String(PYTHON_ARG);
- s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\");\n";
+ s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar
+ << ", fullName, errInfo);\n";
+ s << INDENT << "Py_XDECREF(errInfo);\n";
s << INDENT << "return " << m_currentErrorCode << ";\n";
}
@@ -2905,7 +2905,7 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s,
bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData);
// Handle named arguments.
- writeNamedArgumentResolution(s, func, usePyArgs);
+ writeNamedArgumentResolution(s, func, usePyArgs, overloadData);
bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func);
bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc;
@@ -3228,33 +3228,46 @@ void CppGenerator::writeAddPythonToCppConversion(QTextStream &s, const QString &
s << ");\n";
}
-void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func, bool usePyArgs)
+void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func,
+ bool usePyArgs, const OverloadData &overloadData)
{
const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func);
- if (args.isEmpty())
+ if (args.isEmpty()) {
+ if (overloadData.hasArgumentWithDefaultValue()) {
+ s << INDENT << "if (kwds) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "errInfo = kwds;\n";
+ s << INDENT << "Py_INCREF(errInfo);\n";
+ s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;\n";
+ }
+ s << INDENT << "}\n";
+ }
return;
-
- QString pyErrString(QLatin1String("PyErr_SetString(PyExc_TypeError, \"") + fullPythonFunctionName(func)
- + QLatin1String("(): got multiple values for keyword argument '%1'.\");"));
+ }
s << INDENT << "if (kwds) {\n";
{
Indentation indent(INDENT);
- s << INDENT << "PyObject *keyName = nullptr;\n";
- s << INDENT << "PyObject *value = nullptr;\n";
+ s << INDENT << "PyObject *value{};\n";
+ s << INDENT << "PyObject *kwds_dup = PyDict_Copy(kwds);\n";
for (const AbstractMetaArgument *arg : args) {
- int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex());
+ const int pyArgIndex = arg->argumentIndex()
+ - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex());
QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG);
- s << INDENT << "keyName = Py_BuildValue(\"s\",\"" << arg->name() << "\");\n";
- s << INDENT << "if (PyDict_Contains(kwds, keyName)) {\n";
+ QString pyKeyName = QLatin1String("key_") + arg->name();
+ s << INDENT << "static PyObject *const " << pyKeyName
+ << " = Shiboken::String::createStaticString(\"" << arg->name() << "\");\n";
+ s << INDENT << "if (PyDict_Contains(kwds, " << pyKeyName << ")) {\n";
{
Indentation indent(INDENT);
- s << INDENT << "value = PyDict_GetItem(kwds, keyName);\n";
+ s << INDENT << "value = PyDict_GetItem(kwds, " << pyKeyName << ");\n";
s << INDENT << "if (value && " << pyArgName << ") {\n";
{
Indentation indent(INDENT);
- s << INDENT << pyErrString.arg(arg->name()) << Qt::endl;
- s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl;
+ s << INDENT << "errInfo = " << pyKeyName << ";\n";
+ s << INDENT << "Py_INCREF(errInfo);\n";
+ s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;\n";
}
s << INDENT << "}\n";
s << INDENT << "if (value) {\n";
@@ -3262,7 +3275,8 @@ void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMe
Indentation indent(INDENT);
s << INDENT << pyArgName << " = value;\n";
s << INDENT << "if (!";
- writeTypeCheck(s, arg->type(), pyArgName, isNumber(arg->type()->typeEntry()), func->typeReplaced(arg->argumentIndex() + 1));
+ writeTypeCheck(s, arg->type(), pyArgName, isNumber(arg->type()->typeEntry()),
+ func->typeReplaced(arg->argumentIndex() + 1));
s << ")\n";
{
Indentation indent(INDENT);
@@ -3270,9 +3284,29 @@ void CppGenerator::writeNamedArgumentResolution(QTextStream &s, const AbstractMe
}
}
s << INDENT << "}\n";
+ s << INDENT << "PyDict_DelItem(kwds_dup, " << pyKeyName << ");\n";
}
s << INDENT << "}\n";
}
+ // PYSIDE-1305: Handle keyword args correctly.
+ // Normal functions handle their parameters immediately.
+ // For constructors that are QObject, we need to delay that
+ // until extra keyword signals and properties are handled.
+ s << INDENT << "if (PyDict_Size(kwds_dup) > 0) {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "errInfo = kwds_dup;\n";
+ if (!(func->isConstructor() && func->ownerClass()->isQObject()))
+ s << INDENT << "goto " << cpythonFunctionName(func) << "_TypeError;\n";
+ else
+ s << INDENT << "// fall through to handle extra keyword signals and properties\n";
+ }
+ s << INDENT << "} else {\n";
+ {
+ Indentation indent(INDENT);
+ s << INDENT << "Py_DECREF(kwds_dup);\n";
+ }
+ s << INDENT << "}\n";
}
s << INDENT << "}\n";
}
@@ -4892,7 +4926,7 @@ void CppGenerator::writeSignatureInfo(QTextStream &s, const AbstractMetaFunction
{
OverloadData overloadData(overloads, this);
const AbstractMetaFunction *rfunc = overloadData.referenceFunction();
- QString funcName = fullPythonFunctionName(rfunc);
+ QString funcName = fullPythonFunctionName(rfunc, false);
int idx = overloads.length() - 1;
bool multiple = idx > 0;
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.h b/sources/shiboken2/generator/shiboken2/cppgenerator.h
index 41bd17f21..25bb51ef5 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.h
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.h
@@ -247,7 +247,8 @@ private:
void writeAddPythonToCppConversion(QTextStream &s, const QString &converterVar, const QString &pythonToCppFunc, const QString &isConvertibleFunc);
- void writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func, bool usePyArgs);
+ void writeNamedArgumentResolution(QTextStream &s, const AbstractMetaFunction *func,
+ bool usePyArgs, const OverloadData &overloadData);
/// Returns a string containing the name of an argument for the given function and argument index.
QString argumentNameFromIndex(const AbstractMetaFunction *func, int argIndex, const AbstractMetaClass **wrappedClass);
diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
index 5d8685b90..0f5f09d60 100644
--- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
@@ -356,7 +356,7 @@ QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClass *metaClas
return fullClassName;
}
-QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction *func)
+QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction *func, bool forceFunc)
{
QString funcName;
if (func->isOperatorOverload())
@@ -365,10 +365,14 @@ QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction *fu
funcName = func->name();
if (func->ownerClass()) {
QString fullClassName = fullPythonClassName(func->ownerClass());
- if (func->isConstructor())
+ if (func->isConstructor()) {
funcName = fullClassName;
- else
+ if (forceFunc)
+ funcName.append(QLatin1String(".__init__"));
+ }
+ else {
funcName.prepend(fullClassName + QLatin1Char('.'));
+ }
}
else {
funcName = packageName() + QLatin1Char('.') + func->name();
diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.h b/sources/shiboken2/generator/shiboken2/shibokengenerator.h
index 6d36026cd..cbe796313 100644
--- a/sources/shiboken2/generator/shiboken2/shibokengenerator.h
+++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.h
@@ -234,7 +234,7 @@ protected:
QString wrapperName(const AbstractMetaClass *metaClass) const;
QString fullPythonClassName(const AbstractMetaClass *metaClass);
- QString fullPythonFunctionName(const AbstractMetaFunction *func);
+ QString fullPythonFunctionName(const AbstractMetaFunction *func, bool forceFunc);
bool wrapperDiagnostics() const { return m_wrapperDiagnostics; }
diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp
index d866d133c..4b1e6e564 100644
--- a/sources/shiboken2/libshiboken/basewrapper.cpp
+++ b/sources/shiboken2/libshiboken/basewrapper.cpp
@@ -968,9 +968,10 @@ void init()
}
// setErrorAboutWrongArguments now gets overload info from the signature module.
-void setErrorAboutWrongArguments(PyObject *args, const char *funcName)
+// Info can be nullptr and contains extra info.
+void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject *info)
{
- SetError_Argument(args, funcName);
+ SetError_Argument(args, funcName, info);
}
class FindBaseTypeVisitor : public HierarchyVisitor
diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h
index 31083522b..204c4c1c3 100644
--- a/sources/shiboken2/libshiboken/basewrapper.h
+++ b/sources/shiboken2/libshiboken/basewrapper.h
@@ -157,8 +157,10 @@ void callCppDestructor(void *cptr)
delete reinterpret_cast<T *>(cptr);
}
-// setErrorAboutWrongArguments now gets overload info from the signature module.
-LIBSHIBOKEN_API void setErrorAboutWrongArguments(PyObject *args, const char *funcName);
+// setErrorAboutWrongArguments now gets overload information from the signature module.
+// The extra info argument can contain additional data about the error.
+LIBSHIBOKEN_API void setErrorAboutWrongArguments(PyObject *args, const char *funcName,
+ PyObject *info);
namespace ObjectType {
diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
index 5559d58d6..2c1a9b891 100644
--- a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
+++ b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
@@ -87,6 +87,7 @@ STATIC_STRING_IMPL(get, "__get__")
STATIC_STRING_IMPL(members, "__members__")
STATIC_STRING_IMPL(module, "__module__")
STATIC_STRING_IMPL(name, "__name__")
+STATIC_STRING_IMPL(property_methods, "__property_methods__")
STATIC_STRING_IMPL(qualname, "__qualname__")
STATIC_STRING_IMPL(self, "__self__")
diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.h b/sources/shiboken2/libshiboken/sbkstaticstrings.h
index b72fa989b..0dd533e32 100644
--- a/sources/shiboken2/libshiboken/sbkstaticstrings.h
+++ b/sources/shiboken2/libshiboken/sbkstaticstrings.h
@@ -73,6 +73,7 @@ LIBSHIBOKEN_API PyObject *get();
LIBSHIBOKEN_API PyObject *members();
LIBSHIBOKEN_API PyObject *module();
LIBSHIBOKEN_API PyObject *name();
+LIBSHIBOKEN_API PyObject *property_methods();
LIBSHIBOKEN_API PyObject *qualname();
LIBSHIBOKEN_API PyObject *self();
} // namespace PyMagicName
diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h
index b77cc0f4c..0459d8661 100644
--- a/sources/shiboken2/libshiboken/signature.h
+++ b/sources/shiboken2/libshiboken/signature.h
@@ -45,7 +45,7 @@ extern "C"
LIBSHIBOKEN_API int InitSignatureStrings(PyTypeObject *, const char *[]);
LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *[]);
-LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *);
+LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *, PyObject *);
LIBSHIBOKEN_API PyObject *Sbk_TypeGet___signature__(PyObject *, PyObject *);
LIBSHIBOKEN_API PyObject *Sbk_TypeGet___doc__(PyObject *);
LIBSHIBOKEN_API PyObject *GetFeatureDict();
diff --git a/sources/shiboken2/libshiboken/signature/signature.cpp b/sources/shiboken2/libshiboken/signature/signature.cpp
index 085d751aa..1f36a09f3 100644
--- a/sources/shiboken2/libshiboken/signature/signature.cpp
+++ b/sources/shiboken2/libshiboken/signature/signature.cpp
@@ -209,8 +209,12 @@ PyObject *GetSignature_Wrapper(PyObject *ob, PyObject *modifier)
if (dict == nullptr)
return nullptr;
PyObject *props = PyDict_GetItem(dict, func_name);
- if (props == nullptr)
+ if (props == nullptr) {
+ // handle `__init__` like the class itself
+ if (strcmp(String::toCString(func_name), "__init__") == 0)
+ return GetSignature_TypeMod(objclass, modifier);
Py_RETURN_NONE;
+ }
return _GetSignature_Cached(props, PyName::method(), modifier);
}
@@ -431,7 +435,86 @@ void FinishSignatureInitialization(PyObject *module, const char *signatures[])
}
}
-void SetError_Argument(PyObject *args, const char *func_name)
+static PyObject *adjustFuncName(const char *func_name)
+{
+ /*
+ * PYSIDE-1019: Modify the function name expression according to feature.
+ *
+ * - snake_case
+ * The function name must be converted.
+ * - full_property
+ * The property name must be used and "fset" appended.
+ *
+ * modname.subname.classsname.propname.fset
+ *
+ * Class properties must use the expression
+ *
+ * modname.subname.classsname.__dict__['propname'].fset
+ *
+ * Note that fget is impossible because there are no parameters.
+ */
+ static const char mapping_name[] = "shibokensupport.signature.mapping";
+ static PyObject *sys_modules = PySys_GetObject("modules");
+ static PyObject *mapping = PyDict_GetItemString(sys_modules, mapping_name);
+ static PyObject *ns = PyModule_GetDict(mapping);
+
+ char _path[200 + 1] = {};
+ const char *_name = strrchr(func_name, '.');
+ strncat(_path, func_name, _name - func_name);
+ ++_name;
+
+ // This is a very cheap call into `mapping.py`.
+ PyObject *update_mapping = PyDict_GetItemString(ns, "update_mapping");
+ AutoDecRef res(PyObject_CallFunctionObjArgs(update_mapping, nullptr));
+ if (res.isNull())
+ return nullptr;
+
+ // Run `eval` on the type string to get the object.
+ AutoDecRef obtype(PyRun_String(_path, Py_eval_input, ns, ns));
+ if (PyModule_Check(obtype.object())) {
+ // This is a plain function. Return the unmangled name.
+ return String::fromCString(func_name);
+ }
+ assert(PyType_Check(obtype)); // This was not true for __init__!
+
+ // Find the feature flags
+ auto type = reinterpret_cast<PyTypeObject *>(obtype.object());
+ auto dict = type->tp_dict;
+ int id = SbkObjectType_GetReserved(type);
+ id = id < 0 ? 0 : id; // if undefined, set to zero
+ auto lower = id & 0x01;
+ auto is_prop = id & 0x02;
+ bool is_class_prop = false;
+
+ // Compute all needed info.
+ PyObject *name = String::getSnakeCaseName(_name, lower);
+ PyObject *prop_name;
+ if (is_prop) {
+ PyObject *prop_methods = PyDict_GetItem(dict, PyMagicName::property_methods());
+ prop_name = PyDict_GetItem(prop_methods, name);
+ if (prop_name != nullptr) {
+ PyObject *prop = PyDict_GetItem(dict, prop_name);
+ is_class_prop = Py_TYPE(prop) != &PyProperty_Type;
+ }
+ }
+
+ // Finally, generate the correct path expression.
+ char _buf[200 + 1] = {};
+ if (is_prop) {
+ auto _prop_name = String::toCString(prop_name);
+ if (is_class_prop)
+ sprintf(_buf, "%s.__dict__['%s'].fset", _path, _prop_name);
+ else
+ sprintf(_buf, "%s.%s.fset", _path, _prop_name);
+ }
+ else {
+ auto _name = String::toCString(name);
+ sprintf(_buf, "%s.%s", _path, _name);
+ }
+ return String::fromCString(_buf);
+}
+
+void SetError_Argument(PyObject *args, const char *func_name, PyObject *info)
{
/*
* This function replaces the type error construction with extra
@@ -440,8 +523,26 @@ void SetError_Argument(PyObject *args, const char *func_name)
*/
init_module_1();
init_module_2();
- AutoDecRef res(PyObject_CallFunction(pyside_globals->seterror_argument_func,
- const_cast<char *>("(Os)"), args, func_name));
+
+ // PYSIDE-1305: Handle errors set by fillQtProperties.
+ PyObject *err_val{};
+ if (PyErr_Occurred()) {
+ PyObject *e, *t;
+ PyErr_Fetch(&e, &err_val, &t);
+ info = err_val;
+ Py_XDECREF(&e);
+ Py_XDECREF(&t);
+ }
+ // PYSIDE-1019: Modify the function name expression according to feature.
+ AutoDecRef new_func_name(adjustFuncName(func_name));
+ if (new_func_name.isNull()) {
+ PyErr_Print();
+ Py_FatalError("seterror_argument failed to call update_mapping");
+ }
+ if (info == nullptr)
+ info = Py_None;
+ AutoDecRef res(PyObject_CallFunctionObjArgs(pyside_globals->seterror_argument_func,
+ args, new_func_name.object(), info, nullptr));
if (res.isNull()) {
PyErr_Print();
Py_FatalError("seterror_argument did not receive a result");
@@ -451,6 +552,7 @@ void SetError_Argument(PyObject *args, const char *func_name)
PyErr_Print();
Py_FatalError("unexpected failure in seterror_argument");
}
+ Py_XDECREF(err_val);
PyErr_SetObject(err, msg);
}
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py
index 6ed4c0edd..352644f7a 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py
@@ -95,15 +95,33 @@ def matched_type(args, sigs):
return None
-def seterror_argument(args, func_name):
- update_mapping()
+def seterror_argument(args, func_name, info):
func = None
try:
func = eval(func_name, namespace)
except Exception as e:
- msg = "Internal error evaluating " + func_name + " :" + str(e)
+ msg = "Internal error evaluating {func_name}: {e}".format(**locals())
return TypeError, msg
+ if info and type(info) is str:
+ err = TypeError
+ if info == "<":
+ msg = "{func_name}(): not enough arguments".format(**locals())
+ elif info == ">":
+ msg = "{func_name}(): too many arguments".format(**locals())
+ elif info.isalnum():
+ msg = "{func_name}(): got multiple values for keyword argument '{info}'".format(**locals())
+ else:
+ msg = "{func_name}(): {info}".format(**locals())
+ err = AttributeError
+ return err, msg
+ if info and type(info) is dict:
+ keyword = tuple(info)[0]
+ msg = "{func_name}(): unsupported keyword '{keyword}'".format(**locals())
+ return AttributeError, msg
sigs = get_signature(func, "typeerror")
+ if not sigs:
+ msg = "{func_name}({args}) is wrong (missing signature)".format(**locals())
+ return TypeError, msg
if type(sigs) != list:
sigs = [sigs]
if type(args) != tuple:
@@ -144,7 +162,7 @@ def make_helptext(func):
sigs = [sigs]
try:
func_name = func.__name__
- except AttribureError:
+ except AttributeError:
func_name = func.__func__.__name__
sigtext = "\n".join(func_name + str(sig) for sig in sigs)
msg = sigtext + "\n\n" + existing_doc if check_string_type(existing_doc) else sigtext
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
index 6cee54680..a6c3e420d 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
@@ -101,8 +101,8 @@ def create_signature(props, key):
return layout.create_signature(props, key)
# name used in signature.cpp
-def seterror_argument(args, func_name):
- return errorhandler.seterror_argument(args, func_name)
+def seterror_argument(args, func_name, info):
+ return errorhandler.seterror_argument(args, func_name, info)
# name used in signature.cpp
def make_helptext(func):