Skip to content

Commit 9eeab0e

Browse files
committed
added support for kwargs by supporting QVariantMap kwargs as last argument, refactored from forum
1 parent f3c4f50 commit 9eeab0e

File tree

3 files changed

+133
-74
lines changed

3 files changed

+133
-74
lines changed

src/PythonQtSlot.cpp

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -341,68 +341,114 @@ PyObject *PythonQtMemberFunction_Call(PythonQtSlotInfo* info, PyObject* m_self,
341341

342342
PyObject *PythonQtSlotFunction_CallImpl(PythonQtClassInfo* classInfo, QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject * kw, void* firstArg, void** directReturnValuePointer, PythonQtPassThisOwnershipType* passThisOwnershipToCPP)
343343
{
344-
if (kw != NULL && PyDict_Check(kw) && (PyDict_Size(kw) > 0)) {
345-
QString e = QString("Calling C++ functions with Python keywords is not supported! Function: ") + info->fullSignature(true) + " Keywords: " + PythonQtConv::PyObjGetString(kw);
346-
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
347-
return NULL;
348-
}
349-
350344
int argc = args?PyTuple_Size(args):0;
345+
351346
if (passThisOwnershipToCPP) {
352347
*passThisOwnershipToCPP = IgnoreOwnership;
353348
}
354349

355-
#ifdef PYTHONQT_DEBUG
356-
std::cout << "called " << info->metaMethod()->typeName() << " " << info->signature() << std::endl;
357-
#endif
358-
359350
PyObject* r = NULL;
360351
bool ok = false;
352+
361353
if (directReturnValuePointer) {
362354
*directReturnValuePointer = NULL;
363355
}
364-
if (info->nextInfo()) {
365-
// overloaded slot call, try on all slots with strict conversion first
366-
bool strict = true;
367-
PythonQtSlotInfo* i = info;
368-
while (i) {
369-
bool skipFirst = i->isInstanceDecorator();
370-
if (i->parameterCount()-1-(skipFirst?1:0) == argc) {
371-
PyErr_Clear();
372-
ok = PythonQtCallSlot(classInfo, objectToCall, args, strict, i, firstArg, &r, directReturnValuePointer, passThisOwnershipToCPP);
373-
if (PyErr_Occurred() || ok) break;
356+
357+
if( (kw != NULL && PyDict_Check(kw) && (PyDict_Size(kw) > 0)) ) {
358+
// -------------------keyword args slot call -------------------------
359+
360+
// keyword arguments are given as dict, must be mapped to arguments in correct order
361+
// very complicated, so call them only on a slot with last variable name kwargs
362+
// slot must be implemented as
363+
// <type> <name>(any number of positional arguments, QVariantMap kwargs)
364+
int numCombinedArgs = argc + 1;
365+
PyObject* combinedArgs = PyTuple_New(numCombinedArgs);
366+
367+
for (int i = 0; i<argc; i++) {
368+
PyObject* p = PyTuple_GetItem(args,i);
369+
Py_INCREF(p);
370+
PyTuple_SetItem(combinedArgs,i,p);
371+
}
372+
373+
Py_INCREF(kw);
374+
PyTuple_SetItem(combinedArgs, numCombinedArgs - 1, kw);
375+
376+
bool kwSlotFound = false;
377+
378+
QList<QByteArray> parameterNames;
379+
PythonQtSlotInfo* slotInfo = info;
380+
static QByteArray kwargs = "kwargs";
381+
while (slotInfo) {
382+
parameterNames = slotInfo->metaMethod()->parameterNames();
383+
if (!parameterNames.isEmpty() && (parameterNames.last().constData() == kwargs)) {
384+
kwSlotFound = true;
385+
break;
374386
}
375-
i = i->nextInfo();
376-
if (!i) {
377-
if (strict) {
378-
// one more run without being strict
379-
strict = false;
380-
i = info;
381-
}
387+
}
388+
if (kwSlotFound) {
389+
#ifdef PYTHONQT_DEBUG
390+
std::cout << "called " << slotInfo->metaMethod()->typeName() << " " << slotInfo->signature().constData() << std::endl;
391+
#endif
392+
393+
ok = PythonQtCallSlot(classInfo, objectToCall, combinedArgs, false, slotInfo, firstArg, &r, directReturnValuePointer, passThisOwnershipToCPP);
394+
if (!ok && !PyErr_Occurred()) {
395+
QString e = QString("Called ") + info->fullSignature() + " with wrong arguments: " + PythonQtConv::PyObjGetString(args);
396+
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
382397
}
398+
} else {
399+
QString e = QString("Called ") + info->fullSignature() + " with keyword arguments, but called slot does not support kwargs.";
400+
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
383401
}
384-
if (!ok && !PyErr_Occurred()) {
385-
QString e = QString("Could not find matching overload for given arguments:\n" + PythonQtConv::PyObjGetString(args) + "\n The following slots are available:\n");
402+
403+
Py_DECREF(combinedArgs);
404+
} else {
405+
// -------------------Normal slot call -------------------------
406+
if (info->nextInfo()) {
407+
// overloaded slot call, try on all slots with strict conversion first
408+
bool strict = true;
386409
PythonQtSlotInfo* i = info;
387410
while (i) {
388-
e += QString(i->fullSignature()) + "\n";
411+
bool skipFirst = i->isInstanceDecorator();
412+
if (i->parameterCount()-1-(skipFirst?1:0) == argc) {
413+
PyErr_Clear();
414+
ok = PythonQtCallSlot(classInfo, objectToCall, args, strict, i, firstArg, &r, directReturnValuePointer, passThisOwnershipToCPP);
415+
if (PyErr_Occurred() || ok) break;
416+
}
389417
i = i->nextInfo();
418+
if (!i) {
419+
if (strict) {
420+
// one more run without being strict
421+
strict = false;
422+
i = info;
423+
}
424+
}
390425
}
391-
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
392-
}
393-
} else {
394-
// simple (non-overloaded) slot call
395-
bool skipFirst = info->isInstanceDecorator();
396-
if (info->parameterCount()-1-(skipFirst?1:0) == argc) {
397-
PyErr_Clear();
398-
ok = PythonQtCallSlot(classInfo, objectToCall, args, false, info, firstArg, &r, directReturnValuePointer, passThisOwnershipToCPP);
399426
if (!ok && !PyErr_Occurred()) {
400-
QString e = QString("Called ") + info->fullSignature() + " with wrong arguments: " + PythonQtConv::PyObjGetString(args);
427+
QString e = QString("Could not find matching overload for given arguments:\n" + PythonQtConv::PyObjGetString(args) + "\n The following slots are available:\n");
428+
PythonQtSlotInfo* i = info;
429+
while (i) {
430+
e += QString(i->fullSignature()) + "\n";
431+
i = i->nextInfo();
432+
}
401433
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
402434
}
403435
} else {
404-
QString e = QString("Called ") + info->fullSignature() + " with wrong number of arguments: " + PythonQtConv::PyObjGetString(args);
405-
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
436+
// simple (non-overloaded) slot call
437+
bool skipFirst = info->isInstanceDecorator();
438+
if (info->parameterCount()-1-(skipFirst?1:0) == argc) {
439+
PyErr_Clear();
440+
#ifdef PYTHONQT_DEBUG
441+
std::cout << "called " << info->metaMethod()->typeName() << " " << info->signature().constData() << std::endl;
442+
#endif
443+
ok = PythonQtCallSlot(classInfo, objectToCall, args, false, info, firstArg, &r, directReturnValuePointer, passThisOwnershipToCPP);
444+
if (!ok && !PyErr_Occurred()) {
445+
QString e = QString("Called ") + info->fullSignature() + " with wrong arguments: " + PythonQtConv::PyObjGetString(args);
446+
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
447+
}
448+
} else {
449+
QString e = QString("Called ") + info->fullSignature() + " with wrong number of arguments: " + PythonQtConv::PyObjGetString(args);
450+
PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
451+
}
406452
}
407453
}
408454

@@ -734,7 +780,7 @@ static PyObject*
734780
meth_richcompare(PythonQtSlotFunctionObject *a, PythonQtSlotFunctionObject *b, int op)
735781
{
736782
int x = meth_compare(a, b);
737-
bool r;
783+
bool r = false;
738784
if (op == Py_LT)
739785
r = x < 0;
740786
else if (op == Py_LE)

tests/PythonQtTests.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ void PythonQtTestSlotCalling::testOverloadedCall()
154154
QVERIFY(_helper->runScript("obj.overload(12,13); obj.setPassed();\n", 6));
155155
}
156156

157+
158+
void PythonQtTestSlotCalling::testKeywordCall()
159+
{
160+
QVERIFY(_helper->runScript("if obj.keywordInt(5,value=6)==11: obj.setPassed();\n"));
161+
QVERIFY(_helper->runScript("if obj.keywordOnly(value=6)==1: obj.setPassed();\n"));
162+
QVERIFY(_helper->runScript("if obj.keywordOnly(arg1='test1',arg2='test2')==2: obj.setPassed();\n"));
163+
}
164+
157165
void PythonQtTestSlotCalling::testPyObjectSlotCall()
158166
{
159167
QVERIFY(_helper->runScript("if obj.getPyObject(PythonQt)==PythonQt: obj.setPassed();\n"));
@@ -300,19 +308,19 @@ void PythonQtTestSlotCalling::testCppFactory()
300308

301309
}
302310

303-
PQCppObject2Decorator::TestEnumFlag PQCppObject2Decorator::testEnumFlag1(PQCppObject2* obj, PQCppObject2Decorator::TestEnumFlag flag) {
311+
PQCppObject2Decorator::TestEnumFlag PQCppObject2Decorator::testEnumFlag1(PQCppObject2* /*obj*/, PQCppObject2Decorator::TestEnumFlag flag) {
304312
return flag;
305313
}
306314

307-
PQCppObject2::TestEnumFlag PQCppObject2Decorator::testEnumFlag2(PQCppObject2* obj, PQCppObject2::TestEnumFlag flag) {
315+
PQCppObject2::TestEnumFlag PQCppObject2Decorator::testEnumFlag2(PQCppObject2* /*obj*/, PQCppObject2::TestEnumFlag flag) {
308316
return flag;
309317
}
310318

311319
// with int overload
312-
PQCppObject2Decorator::TestEnumFlag PQCppObject2Decorator::testEnumFlag3(PQCppObject2* obj, int flag) {
320+
PQCppObject2Decorator::TestEnumFlag PQCppObject2Decorator::testEnumFlag3(PQCppObject2* /*obj*/, int /*flag*/) {
313321
return (TestEnumFlag)-1;
314322
}
315-
PQCppObject2Decorator::TestEnumFlag PQCppObject2Decorator::testEnumFlag3(PQCppObject2* obj, PQCppObject2Decorator::TestEnumFlag flag) {
323+
PQCppObject2Decorator::TestEnumFlag PQCppObject2Decorator::testEnumFlag3(PQCppObject2* /*obj*/, PQCppObject2Decorator::TestEnumFlag flag) {
316324
return flag;
317325
}
318326

@@ -583,25 +591,25 @@ void PythonQtTestApi::testQColorDecorators()
583591
QVERIFY(colorClass.call("red", QVariantList() << QColor(255,0,0)).toInt() == 255);
584592
}
585593

586-
QByteArray PythonQtTestApiHelper::readFileAsBytes(const QString& filename)
594+
QByteArray PythonQtTestApiHelper::readFileAsBytes(const QString& /*filename*/)
587595
{
588596
QByteArray b;
589597
return b;
590598
}
591599

592-
QByteArray PythonQtTestApiHelper::readSourceFile(const QString& filename, bool& ok)
600+
QByteArray PythonQtTestApiHelper::readSourceFile(const QString& /*filename*/, bool& ok)
593601
{
594602
QByteArray b;
595603
ok = true;
596604
return b;
597605
}
598606

599-
bool PythonQtTestApiHelper::exists(const QString& filename)
607+
bool PythonQtTestApiHelper::exists(const QString& /*filename*/)
600608
{
601609
return true;
602610
}
603611

604-
QDateTime PythonQtTestApiHelper::lastModifiedDate(const QString& filename) {
612+
QDateTime PythonQtTestApiHelper::lastModifiedDate(const QString& /*filename*/) {
605613
return QDateTime::currentDateTime();
606614
}
607615

tests/PythonQtTests.h

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,10 @@ public Q_SLOTS:
239239
PQCppObjectNoWrap* new_PQCppObjectNoWrap() {
240240
return new PQCppObjectNoWrap(0);
241241
}
242-
PQCppObjectNoWrap* new_PQCppObjectNoWrap(const PQCppObjectNoWrap& other) {
242+
PQCppObjectNoWrap* new_PQCppObjectNoWrap(const PQCppObjectNoWrap& /*other*/) {
243243
return new PQCppObjectNoWrap(1);
244244
}
245-
PQCppObjectNoWrap* new_PQCppObjectNoWrap(double value) {
245+
PQCppObjectNoWrap* new_PQCppObjectNoWrap(double /*value*/) {
246246
return new PQCppObjectNoWrap(2);
247247
}
248248

@@ -324,6 +324,7 @@ private Q_SLOTS:
324324
void testMultiArgsSlotCall();
325325
void testPyObjectSlotCall();
326326
void testOverloadedCall();
327+
void testKeywordCall();
327328
void testCppFactory();
328329
void testInheritance();
329330
void testAutoConversion();
@@ -399,13 +400,17 @@ public Q_SLOTS:
399400
void testNoArg() { _called = true; }
400401

401402
//! overload test!
402-
void overload(bool a) { _calledOverload = 0; _called = true; }
403-
void overload(float a) { _calledOverload = 1; _called = true;}
404-
void overload(int a) { _calledOverload = 2; _called = true;}
405-
void overload(const QString& str) { _calledOverload = 3; _called = true;}
406-
void overload(const QStringList& str) { _calledOverload = 4; _called = true;}
407-
void overload(QObject* str) { _calledOverload = 5; _called = true;}
408-
void overload(float a, int b) { _calledOverload = 6; _called = true;}
403+
void overload(bool /*a*/) { _calledOverload = 0; _called = true; }
404+
void overload(float /*a*/) { _calledOverload = 1; _called = true;}
405+
void overload(int /*a*/) { _calledOverload = 2; _called = true;}
406+
void overload(const QString& /*str*/) { _calledOverload = 3; _called = true;}
407+
void overload(const QStringList& /*str*/) { _calledOverload = 4; _called = true;}
408+
void overload(QObject* /*str*/) { _calledOverload = 5; _called = true;}
409+
void overload(float /*a*/, int /*b*/) { _calledOverload = 6; _called = true;}
410+
411+
//!keyword argument tests
412+
int keywordInt(int i, const QVariantMap& kwargs = QVariantMap()) { _called = true; return (i + kwargs["value"].toInt()); }
413+
int keywordOnly(const QVariantMap& kwargs = QVariantMap()) { _called = true; return kwargs.count(); }
409414

410415
//! POD values:
411416
int getInt(int a) { _called = true; return a; }
@@ -474,8 +479,8 @@ public Q_SLOTS:
474479
// returned object needs to get an extra ref count!
475480
Py_XINCREF(obj);
476481
return obj;
477-
};
478-
QVariant getPyObjectFromVariant2(const QVariant& val) { _called = true; return val; };
482+
}
483+
QVariant getPyObjectFromVariant2(const QVariant& val) { _called = true; return val; }
479484

480485
//! testing pointer passing
481486
PythonQtTestSlotCallingHelper* getTestObject(PythonQtTestSlotCallingHelper* obj) { _called = true; return obj; }
@@ -522,10 +527,10 @@ public Q_SLOTS:
522527
ClassA* createClassDAsA() { _called = true; return new ClassD; }
523528
ClassB* createClassDAsB() { _called = true; return new ClassD; }
524529

525-
QColor setAutoConvertColor(const QColor& color) { _called = true; return color; };
526-
QBrush setAutoConvertBrush(const QBrush& brush) { _called = true; return brush; };
527-
QPen setAutoConvertPen(const QPen& pen) { _called = true; return pen; };
528-
QCursor setAutoConvertCursor(const QCursor& cursor) { _called = true; return cursor; };
530+
QColor setAutoConvertColor(const QColor& color) { _called = true; return color; }
531+
QBrush setAutoConvertBrush(const QBrush& brush) { _called = true; return brush; }
532+
QPen setAutoConvertPen(const QPen& pen) { _called = true; return pen; }
533+
QCursor setAutoConvertCursor(const QCursor& cursor) { _called = true; return cursor; }
529534

530535
private:
531536
bool _passed;
@@ -560,24 +565,24 @@ class PythonQtTestSignalHandlerHelper : public QObject
560565
public:
561566
PythonQtTestSignalHandlerHelper(PythonQtTestSignalHandler* test) {
562567
_test = test;
563-
};
568+
}
564569

565570
public Q_SLOTS:
566571
void setPassed() { _passed = true; }
567572

568-
bool emitIntSignal(int a) { _passed = false; emit intSignal(a); return _passed; };
569-
bool emitFloatSignal(float a) { _passed = false; emit floatSignal(a); return _passed; };
570-
bool emitEnumSignal(PQCppObject2::TestEnumFlag flag) { _passed = false; emit enumSignal(flag); return _passed; };
573+
bool emitIntSignal(int a) { _passed = false; emit intSignal(a); return _passed; }
574+
bool emitFloatSignal(float a) { _passed = false; emit floatSignal(a); return _passed; }
575+
bool emitEnumSignal(PQCppObject2::TestEnumFlag flag) { _passed = false; emit enumSignal(flag); return _passed; }
571576

572-
bool emitVariantSignal(const QVariant& v) { _passed = false; emit variantSignal(v); return _passed; };
577+
bool emitVariantSignal(const QVariant& v) { _passed = false; emit variantSignal(v); return _passed; }
573578
QVariant expectedVariant() { return _v; }
574579
void setExpectedVariant(const QVariant& v) { _v = v; }
575580

576-
bool emitComplexSignal(int a, float b, const QStringList& l, QObject* obj) { _passed = false; emit complexSignal(a,b,l,obj); return _passed; };
581+
bool emitComplexSignal(int a, float b, const QStringList& l, QObject* obj) { _passed = false; emit complexSignal(a,b,l,obj); return _passed; }
577582

578-
bool emitSignal1(int a) { _passed = false; emit signal1(a); return _passed; };
579-
bool emitSignal2(const QString& s) { _passed = false; emit signal2(s); return _passed; };
580-
bool emitSignal3(float a) { _passed = false; emit signal3(a); return _passed; };
583+
bool emitSignal1(int a) { _passed = false; emit signal1(a); return _passed; }
584+
bool emitSignal2(const QString& s) { _passed = false; emit signal2(s); return _passed; }
585+
bool emitSignal3(float a) { _passed = false; emit signal3(a); return _passed; }
581586

582587
Q_SIGNALS:
583588
void intSignal(int);

0 commit comments

Comments
 (0)