[PyQt] Slot type validation: int/qint64

VA dev+pyqt at indigo.re
Tue May 3 09:30:19 BST 2016


Le 02/05/2016 16:55, Phil Thompson a écrit :
> On 30 Apr 2016, at 3:18 pm, VA <dev+pyqt at indigo.re> wrote:
>>
>> Le 30/04/2016 12:54, Phil Thompson a écrit :
>>> On 29 Apr 2016, at 7:36 pm, Florian Bruhin <me at the-compiler.org> wrote:
>>>>
>>>> * Florian Bruhin <me at the-compiler.org> [2016-04-26 20:11:49 +0200]:
>>>>> Hi!
>>>>>
>>>>> Thanks to the slot type validation of PyQt 5.6 I discovered a lot of cases
>>>>> where my decorations where plain wrong...
>>>>>
>>>>> However in this case I'm unsure what's going on:
>>>>>
>>>>> 	Traceback (most recent call last):
>>>>> 	  File ".../qutebrowser/browser/webpage.py", line 327, in on_unsupported_content
>>>>> 		suggested_filename=suggested_filename)
>>>>> 	  File ".../qutebrowser/browser/downloads.py", line 884, in fetch
>>>>> 		download = DownloadItem(reply, self._win_id, self)
>>>>> 	  File ".../qutebrowser/browser/downloads.py", line 332, in __init__
>>>>> 		self.init_reply(reply)
>>>>> 	  File ".../qutebrowser/browser/downloads.py", line 430, in init_reply
>>>>> 		reply.downloadProgress.connect(self.stats.on_download_progress)
>>>>> 	TypeError: decorated slot has no signature compatible with downloadProgress(qint64,qint64)
>>>>>
>>>>> [...]
>>>>>
>>>>> Using 'qint64', 'qint64' instead works.
>>>>>
>>>>> I've tried to create a minimal example:
>>>>>
>>>>> [...]
>>>>>
>>>>> But that seems to work fine for some reason...
>>>>
>>>> I can reproduce the issue with the attached example (thanks to ntome
>>>> in the #pyqt IRC channel).
>>>
>>> The problem is that a Python int is mapped to a C++ int in the slot signature, even though a Python int is capable of storing much bigger values. Connecting a qint64 to a C++ int is invalid, hence the error.
>>>
>>> A fix for this would be map a Python int to the biggest C++ integer type. However I am uncomfortable making such a change without a lot more thought and experimentation (particularly when handling quint64).
>>>
>>> In the meantime the workaround (which should always work) is to use 'qint64' as you are doing.
>>
>> How come the mapping used to work, and now it doesn't anymore?
> 
> It never worked. Instead the code path for an undecorated method was followed.
> 
>> Was the validation done silently but not enforced, falling back to
>> connecting the signal to a regular Python callable, rather than a
>> promoting the callback to a full-fledged Qt slot? Was the fallback
>> abandoned, to force removing the @Slot when the signature doesn't match?
> 
> The fallback wasn't intentional in the first place.

Ok

>> I used to do @Slot(object) to perform a kind of "catch all" (for one
>> argument) but this new decorator validation just prevents it.
>> Is there a way to disable the validation?
> 
> Just remove the decorator - it was having no effect apart from defining a C++ slot that wasn't being used.
> 
>> I'm using Qscintilla. With PyQt 5.5, I could successfully connect:
>> - SCN_MODIFIED (int, int, const char *, int, int, int, int, int, int,
>> int) to @Slot(int, int, str, int, int, int, int, int, int, int)
>> - SCN_MACRORECORD (unsigned int, unsigned long, void *) to @Slot(int,
>> int, object)
>> Now, not only slot validation prevents it from working, but if I repeat
>> the C++ signature (quoting the type names), it may work but after a few
>> signal emissions and the slot getting called a few times, Python segfaults.
>> If I completely omit the @Slot decorator, the connection works too, but
>> it still quickly segfaults after a few emissions.
>> Even if I'm not doing anything with the slots arguments (the slot just
>> containing "pass"), it still segfaults.
>> What could cause those segfaults?
> 
> I can't reproduce any crashes when the decorator is removed.

The crashes were in fact due to a packaging mismatch in Debian between
Python QScintilla 1.9.2 and PyQt 5.6.

> The above is a good example of why this needed fixing as it hides at least two bugs. First, your signature for the SCN_MODIFIED slot is wrong - it should be a bytes not a str argument. Second, PyQt doesn't handle a bytes argument properly. I suspect it also doesn't handle void* properly either.

The signature example was in Python 2, so str was equivalent to bytes.
Regarding the "void *", I was receiving a sip.voidptr, so I convert it
to a string with this very naive helper:

def sipvoid_as_str(v):
    i = 1
    while True:
        s = v.asstring(i)
        if s[-1] == '\x00':
            return s[:-1]
        i += 1

>> In the meantime, it would be a good thing to update
>> http://pyqt.sourceforge.net/Docs/PyQt5/incompatibilities.html and warn
>> about the problems, and I hope there's a fix for those.


More information about the PyQt mailing list