无法包装 QWebView.load 方法
Cannot wrap QWebView.load method
我相信能够包装函数调用是常见的做法(至少对我来说是这样)。
举个例子,一个简约的包装函数看起来像:
def wrap(fn, *args, **kwargs):
return fn(*args, **kwargs)
并且您可以通过
调用任意方法
wrap(qt_method, 1, 2, foo='bar')
相当于直接调用
qt_method(1,2, foo='bar')
这通常对我有用。但是,我遇到了一个没有的情况。
QWebView.load()
似乎不喜欢将空字典扩展到其调用签名中。例如。 wrap(my_webview.load, QUrl('http://istonyabbottstillprimeminister.com'))
失败,异常:
TypeError: QWebView.load(QUrl): argument 1 has unexpected type 'QUrl'
.
下面是一个简单的工作示例,它演示了哪些东西可以工作,哪些不可以。我还没有找到另一个无法像这样包装的 Qt 方法。
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_()
当您子类化 QWebView
并像这样重写 load
方法时,这个问题也会出现:
def load(self *args, **kwargs):
return QWebView.load(self, *args, **kwargs)
当然,如果您改为调用 QWebView.load(self, *args)
或不在方法签名中使用 *args, **kwargs
,那么您不会得到异常(这遵循从简约工作示例中看到的内容以上)
如有任何见解,我们将不胜感激。
我认为这是 PyQt 的产物,它是 C++ 库的 Python 包装器。请注意,QWebView.load
不是用 Python 编写的函数,而是编译扩展提供的 "builtin method"。 The documentation for QWebView.load
显示具有不同参数签名的两个版本。这种方法重载在 Python 中是不可能的,所以大概 QWebView.load
正在使用一些试探法来确定你想调用哪个版本,而当你使用 kwargs 时,这些试探法会失败。 (Python 函数无法区分根本不传递 kwargs 和传递空 kwargs 之间的区别,但 C 扩展模块可以。)换句话说,当你调用 load(url, **kwargs)
时,它认为你'重新尝试使用接受 QNetworkRequest 而不是 QUrl 的 load
版本。
不幸的是,在为 C++ 库使用 Python 包装器时,有时会出现此类问题,因为 C++ API 可能会使用重载函数签名,必须以某种方式将其转换为 Python 世界超载是不可能的。可能值得在 PyQt4 错误跟踪器上提出问题。据推测,这可以通过使底层启发式更智能来解决(例如,它应该将空 kwargs 与省略的 kwargs 一样对待)。同时,您必须通过显式检查是否要调用 QWebView.load
来解决它,如果是,则不传递 kwargs。
请注意,该问题与您在程序中描述的函数包装没有任何关系。 webview.load(url, **{})
自己失败了,根本没有写任何包装器。这意味着问题出在 QWebView.load
处理关键字参数的方式上。你发现这个是因为你围绕它写了一个包装器,但是无论有没有包装器都存在问题。
由于python不支持函数重载,所以python函数的签名通常包含*args或**kwargs来模拟"overloaded"。 C++ Qt 实现了很多重载函数,如下所示:
http://qt-project.org/doc/qt-4.8/qwebview.html
他们有:
void load ( const QUrl & url )
void load ( const QNetworkRequest & request, QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, const QByteArray & body = QByteArray() )
implements the overloaded functions.
但是,如果你查看 PyQt4 函数签名(函数原型,它们并不总是有 **kwargs),以负载为例,PyQt4 中定义的负载实际上是:
def load(self, *__args): # real signature unknown; restored from __doc__ with multiple overloads
"""
QWebView.load(QUrl)
QWebView.load(QNetworkRequest, QNetworkAccessManager.Operation operation=QNetworkAccessManager.GetOperation, QByteArray body=QByteArray())
"""
pass
所以你可以试试这个:
qapplication = QtGui.QApplication(sys.argv)
webview = QWebView()
url = QtCore.QUrl('http://istonyabbottstillprimeminister.com')
# basic wrapping function
def wraps(fn, *args, **kwargs):
if not len(kwargs):
return fn(*args)
else:
return fn(*args, **kwargs)
args = ()
kwargs = {}
wraps(webview.load, url)
webview.load(url)
webview.show()
qapplication.exec_()
因此,通过这种方式,您可以使用 doc 来检测可能的函数签名。
print webview.load.__doc__
输出是
QWebView.load(QUrl)
QWebView.load(QNetworkRequest, QNetworkAccessManager.Operation operation=QNetworkAccessManager.GetOperation, QByteArray body=QByteArray())
我无法重现这个,除非使用具有特定调用签名组合的重载 PyQt 方法。只有当一个签名只有一个参数,而另一个签名只有一个参数并且其余关键字参数具有默认值时,这个错误似乎会被击中。
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)
你刚好碰上了一个。我会将此报告为针对 SIP 的错误。它用于确定要调用哪个方法的启发式方法总体上看起来非常好,而它恰好只是针对这个特定的组合或调用签名而失败。如果您想最大程度地相信,当输入可能无法很好地解析其参数的任意函数时,事情不会中断,我会使用类似这样的代码:
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()
我相信能够包装函数调用是常见的做法(至少对我来说是这样)。
举个例子,一个简约的包装函数看起来像:
def wrap(fn, *args, **kwargs):
return fn(*args, **kwargs)
并且您可以通过
调用任意方法wrap(qt_method, 1, 2, foo='bar')
相当于直接调用
qt_method(1,2, foo='bar')
这通常对我有用。但是,我遇到了一个没有的情况。
QWebView.load()
似乎不喜欢将空字典扩展到其调用签名中。例如。 wrap(my_webview.load, QUrl('http://istonyabbottstillprimeminister.com'))
失败,异常:
TypeError: QWebView.load(QUrl): argument 1 has unexpected type 'QUrl'
.
下面是一个简单的工作示例,它演示了哪些东西可以工作,哪些不可以。我还没有找到另一个无法像这样包装的 Qt 方法。
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_()
当您子类化 QWebView
并像这样重写 load
方法时,这个问题也会出现:
def load(self *args, **kwargs):
return QWebView.load(self, *args, **kwargs)
当然,如果您改为调用 QWebView.load(self, *args)
或不在方法签名中使用 *args, **kwargs
,那么您不会得到异常(这遵循从简约工作示例中看到的内容以上)
如有任何见解,我们将不胜感激。
我认为这是 PyQt 的产物,它是 C++ 库的 Python 包装器。请注意,QWebView.load
不是用 Python 编写的函数,而是编译扩展提供的 "builtin method"。 The documentation for QWebView.load
显示具有不同参数签名的两个版本。这种方法重载在 Python 中是不可能的,所以大概 QWebView.load
正在使用一些试探法来确定你想调用哪个版本,而当你使用 kwargs 时,这些试探法会失败。 (Python 函数无法区分根本不传递 kwargs 和传递空 kwargs 之间的区别,但 C 扩展模块可以。)换句话说,当你调用 load(url, **kwargs)
时,它认为你'重新尝试使用接受 QNetworkRequest 而不是 QUrl 的 load
版本。
不幸的是,在为 C++ 库使用 Python 包装器时,有时会出现此类问题,因为 C++ API 可能会使用重载函数签名,必须以某种方式将其转换为 Python 世界超载是不可能的。可能值得在 PyQt4 错误跟踪器上提出问题。据推测,这可以通过使底层启发式更智能来解决(例如,它应该将空 kwargs 与省略的 kwargs 一样对待)。同时,您必须通过显式检查是否要调用 QWebView.load
来解决它,如果是,则不传递 kwargs。
请注意,该问题与您在程序中描述的函数包装没有任何关系。 webview.load(url, **{})
自己失败了,根本没有写任何包装器。这意味着问题出在 QWebView.load
处理关键字参数的方式上。你发现这个是因为你围绕它写了一个包装器,但是无论有没有包装器都存在问题。
由于python不支持函数重载,所以python函数的签名通常包含*args或**kwargs来模拟"overloaded"。 C++ Qt 实现了很多重载函数,如下所示: http://qt-project.org/doc/qt-4.8/qwebview.html 他们有:
void load ( const QUrl & url )
void load ( const QNetworkRequest & request, QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, const QByteArray & body = QByteArray() )
implements the overloaded functions.
但是,如果你查看 PyQt4 函数签名(函数原型,它们并不总是有 **kwargs),以负载为例,PyQt4 中定义的负载实际上是:
def load(self, *__args): # real signature unknown; restored from __doc__ with multiple overloads
"""
QWebView.load(QUrl)
QWebView.load(QNetworkRequest, QNetworkAccessManager.Operation operation=QNetworkAccessManager.GetOperation, QByteArray body=QByteArray())
"""
pass
所以你可以试试这个:
qapplication = QtGui.QApplication(sys.argv)
webview = QWebView()
url = QtCore.QUrl('http://istonyabbottstillprimeminister.com')
# basic wrapping function
def wraps(fn, *args, **kwargs):
if not len(kwargs):
return fn(*args)
else:
return fn(*args, **kwargs)
args = ()
kwargs = {}
wraps(webview.load, url)
webview.load(url)
webview.show()
qapplication.exec_()
因此,通过这种方式,您可以使用 doc 来检测可能的函数签名。
print webview.load.__doc__
输出是 QWebView.load(QUrl) QWebView.load(QNetworkRequest, QNetworkAccessManager.Operation operation=QNetworkAccessManager.GetOperation, QByteArray body=QByteArray())
我无法重现这个,除非使用具有特定调用签名组合的重载 PyQt 方法。只有当一个签名只有一个参数,而另一个签名只有一个参数并且其余关键字参数具有默认值时,这个错误似乎会被击中。
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)
你刚好碰上了一个。我会将此报告为针对 SIP 的错误。它用于确定要调用哪个方法的启发式方法总体上看起来非常好,而它恰好只是针对这个特定的组合或调用签名而失败。如果您想最大程度地相信,当输入可能无法很好地解析其参数的任意函数时,事情不会中断,我会使用类似这样的代码:
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()