[PyQt] PyQt should not ignore functools.partial signature
Phil Thompson
phil at riverbankcomputing.com
Fri Oct 14 10:21:34 BST 2011
On Fri, 14 Oct 2011 11:10:03 +0200, Luper Rouch <luper.rouch at gmail.com>
wrote:
> 2011/10/13 Phil Thompson <phil at riverbankcomputing.com>
>
>> 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.
>>
>>
> Maybe with a special path for partials and the likes:
>
> if hasattr(slot, "args"):
> # use slot.args to adapt slot call
> else:
> # call the slot the normally
>
> Could you please point me where the slot calling code lives so I can
have a
> better understanding of how things work ?
It's part of sip. sip_api_invoke_slot() in qtlib.c
Phil
More information about the PyQt
mailing list