[PyQt] PyQt should not ignore functools.partial signature

Phil Thompson phil at riverbankcomputing.com
Thu Oct 13 14:32:04 BST 2011


On Wed, 12 Oct 2011 15:57:17 +0200, Luper Rouch <luper.rouch at gmail.com>
wrote:
> PyQt seems to ignore the signature of functools.partial objects (the
'args'
> and 'keywords' attributes [1]), when connecting a callable. Here is an
> example demonstrating the problem :
> 
> import functools
> from PyQt4.QtCore import QObject, pyqtSignal
> 
> class Sender(QObject):
> 
>     hello = pyqtSignal(bool)
> 
> def receiver():
>     print "foo"
> 
> def decorator(func):
>     @functools.wraps(func)
>     def wrapped(*args, **kwargs):
>         return func(*args, **kwargs)
>     return wrapped
> 
> decorated_receiver = decorator(receiver)
> 
> if __name__ == "__main__":
>     sender = Sender()
>     sender.hello.connect(receiver)
>     sender.hello.connect(decorated_receiver)
>     sender.hello.emit(True)
> 
> When executed, the script gives the following error :
> 
> $ python test_signature.py
> foo
> Traceback (most recent call last):
>   File "test_signature.py", line 17, in wrapped
>     return func(*args, **kwargs)
> TypeError: receiver() takes no arguments (1 given)
> 
> Connecting to a lambda is not a good solution, because PyQt increases
its
> reference count to keep it alive ("However, if a slot is a lambda
function
> or a partial function then its reference count is automatically
incremented
> to prevent it from being immediately garbage collected", see [2]).
> 
> If you do this in a widget that is later deleted, the lambda stays
alive,
> leading to complex bugs. The only solution is to connect to a normal
method
> calling the decorated method, which can quickly become cumbersome.
> 
> It would be nice if PyQt did some additional checks when connecting to a
> functools.partial object.

Like what?

The problem isn't when connecting but when emitting. PyQt emulates the Qt
behaviour of allowing a slot to take fewer arguments than the signal is
providing. It does this by detecting a TypeError raised by the act of
calling the slot. In this case the exception is raised in the body of the
slot and there is no way for PyQt to distinguish between that and any other
TypeError raised while executing the slot.

Phil


More information about the PyQt mailing list