aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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):