I believe it is common practice (at least it is for me) to be able to wrap function calls.
For example, as an example, a minimilistic wrapping function looks like:
def wrap(fn, *args, **kwargs):
return fn(*args, **kwargs)
And you could call an arbitrary method via
wrap(qt_method, 1, 2, foo='bar')
which would be equivalent to directly calling
qt_method(1,2, foo='bar')
This generally works for me. However, I've come across a case where it doesn't.
QWebView.load()
doesn't seem to like having an empty dictionary expanded into its call signature. E.g. wrap(my_webview.load, QUrl('http://istonyabbottstillprimeminister.com'))
fails with the exception:
TypeError: QWebView.load(QUrl): argument 1 has unexpected type 'QUrl'
.
Below is a minimilistic working example that demonstrates things that work, and don't. I have yet to find another Qt method that fails to wrap like this.
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4.QtWebKit import QWebView
qapplication = QtGui.QApplication(sys.argv)
webview = QWebView()
url = QtCore.QUrl('http://istonyabbottstillprimeminister.com')
# basic wrapping function
def wraps(fn, *args, **kwargs):
return fn(*args, **kwargs)
args = ()
kwargs = {}
wraps(webview.load, url) # Doesn't work
webview.load(url, **kwargs) # Doesn't work
webview.load(url) # works
webview.url(*args, **kwargs) # works
webview.show()
qapplication.exec_()
This problem also applies when you subclass QWebView
and override the load
method like this:
def load(self *args, **kwargs):
return QWebView.load(self, *args, **kwargs)
Of course, if you instead call QWebView.load(self, *args)
or do not use *args, **kwargs
in the methods signature, then you don't get the exception (which follows what is seen from the minimilistic working example ebove)
Any insight into this would be appreciated.
I'm unable to reproduce this except with overloaded PyQt methods that have a specific combination of call signatures. Only when one signature has a single argument, and another has a single argument and the rest keyword arguments with default values, does this bug seem to be hit.
There are exactly nine such methods in PyQt4:
QtNetwork
QSslSocket
addDefaultCaCertificates(QString, QSsl.EncodingFormat format=QSsl.Pem, QRegExp.PatternSyntax syntax=QRegExp.FixedString)
addDefaultCaCertificates(list-of-QSslCertificate)
addCaCertificates(QString, QSsl.EncodingFormat format=QSsl.Pem, QRegExp.PatternSyntax syntax=QRegExp.FixedString)
addCaCertificates(list-of-QSslCertificate)
setPrivateKey(QSslKey)
setPrivateKey(QString, QSsl.KeyAlgorithm algorithm=QSsl.Rsa, QSsl.EncodingFormat format=QSsl.Pem, QByteArray passPhrase=QByteArray())
setLocalCertificate(QSslCertificate)
setLocalCertificate(QString, QSsl.EncodingFormat format=QSsl.Pem)
QtWebKit
QWebFrame
load(QUrl)
load(QNetworkRequest, QNetworkAccessManager.Operation operation=QNetworkAccessManager.GetOperation, QByteArray body=QByteArray())
QGraphicsWebView
load(QUrl)
load(QNetworkRequest, QNetworkAccessManager.Operation operation=QNetworkAccessManager.GetOperation, QByteArray body=QByteArray())
QWebView
load(QUrl)
load(QNetworkRequest, QNetworkAccessManager.Operation operation=QNetworkAccessManager.GetOperation, QByteArray body=QByteArray())
QtGui
QGraphicsScene
items(Qt.SortOrder)
QGraphicsScene.items(QPointF)
QGraphicsScene.items(QRectF, Qt.ItemSelectionMode mode=Qt.IntersectsItemShape)
QGraphicsScene.items(QPolygonF, Qt.ItemSelectionMode mode=Qt.IntersectsItemShape)
QGraphicsScene.items(QPainterPath, Qt.ItemSelectionMode mode=Qt.IntersectsItemShape)
QGraphicsView
items(QPoint)
QGraphicsView.items(QRect, Qt.ItemSelectionMode mode=Qt.IntersectsItemShape)
QGraphicsView.items(QPolygon, Qt.ItemSelectionMode mode=Qt.IntersectsItemShape)
QGraphicsView.items(QPainterPath, Qt.ItemSelectionMode mode=Qt.IntersectsItemShape)
and you just happened to hit on one. I would report this as a bug against SIP. Its heuristics for figuring out which method to call seem in general very good, and it happens to be failing just for this particular combination or call signatures. If you want to maximise confidence that things won't break when fed arbitrary functions that might be parsing their arguments poorly, I'd use something like this your code:
def call_method_considerately(method, *args, **kwargs):
if args and kwargs:
return method(*args, **kwargs)
elif args:
return method(*args)
elif kwargs:
return method(**kwargs)
else:
return method()