Search code examples
pythonstringencodingpyside6qstringdecoder

QStringDecoder not callable in PySide6


I can't get the example from the PySide6 documentation working on my system. I am running PySide-6.2 on Ubuntu.

toUtf16 = QStringDecoder(QStringConverter.Utf8)
toUtf16('test')

Result:

TypeError: 'PySide6.QtCore.QStringDecoder' object is not callable

Solution

  • It looks like a bug in PySide6. The returned object only has the inherited methods from QStringConverter, as can be seen by using dir:

    >>> dec = QtCore.QStringDecoder(QtCore.QStringConverter.Encoding.Utf16)
    >>> for x in dir(dec):
    ...     if x[0].islower(): print(x)
    ...
    hasError
    isValid
    name
    nameForEncoding
    requiredSpace
    resetState
    state
    

    So the decode methods are missing, and the TypeError indicates that the ()-operator overload is missing as well. The QStringEncoder class has similar problems.

    In PyQt6, everything works as expected:

    >>> enc = QtCore.QStringEncoder(QtCore.QStringConverter.Encoding.Utf16)
    >>> dec = QtCore.QStringDecoder(QtCore.QStringConverter.Encoding.Utf16)
    >>> x = enc('text')
    >>> x
    PyQt6.QtCore.QByteArray(b't\x00e\x00x\x00t\x00')
    >>> dec(x)
    'text'
    

    Of course, you don't really need to use these Qt classes, since the equivalent functionality already exists in Python:

    >>> x = 'text'.encode('utf-16')
    >>> x
    b'\xff\xfet\x00e\x00x\x00t\x00'
    >>> x.decode('utf-16')
    'text'
    

    Note that Python automatically prepends a BOM, whereas Qt requires an additional flag to get the same behaviour. To get a Qt-style callable object in Python, you can either use functools.partial or a lambda:

    >>> from functools import partial
    >>> enc = partial(str.encode, encoding='utf-16')
    >>> # or enc = lambda s: s.encode('utf-16')
    >>> dec = partial(bytes.decode, encoding='utf-16')
    >>> # or dec = lambda b: b.decode('utf-16')
    >>> x = enc('text')
    b'\xff\xfet\x00e\x00x\x00t\x00'
    >>> dec(x)
    'text'
    

    NB: to convert a QByteArray to a Python bytes object, use bytes(qtbytearray) or qtbytearray.data() (see also: How to convert a QByteArray to a python string).