ukBaz / python-bluezero

A simple Python interface to Bluez
MIT License
395 stars 112 forks source link

Handle dbus signature "y" in dbus_to_python #372

Closed ukBaz closed 2 years ago

ukBaz commented 2 years ago

Currently dbus_to_python converts a dbus.Array with signature y to a python list and not a bytearray so struct.unpack doesn't work? e.g.

from bluezero import dbus_tools
import dbus
import struct
x = dbus.Array([1,2,3,4,5,6,7,8], signature='y')
#  x = dbus.Array([1, 2, 3, 4, 5, 6, 7, 8], signature=dbus.Signature('y'))
y = dbus_tools.dbus_to_python(x)
#  y = [1, 2, 3, 4, 5, 6, 7, 8]
z = struct.unpack('<II', y)
Traceback (most recent call last):
  File "/usr/lib/python3.8/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 1, in <module>
TypeError: a bytes-like object is required, not 'list'

It would be more accurate to say that there is a bug in dbus_to_python and it is not doing the conversion correctly. There is probably some debate to be had if it should return bytes or bytearray. For reference:

python3's bytes and bytearray classes both hold arrays of bytes, where each byte can take on a value between 0 and 255. The primary difference is that a bytes object is immutable, meaning that oncce created, you cannot modify its elements. By contrast, a bytearray object allows you to modify its elements.

Initially I'm favouring bytearray but I'm interested in other opinions.

This would result in the code going from:

    elif isinstance(data, dbus.Array):
        data = [dbus_to_python(value) for value in data]

to

    elif isinstance(data, dbus.Array):
        if data.signature == dbus.Signature('y'):
            data = bytearray(data)
        else:
            data = [dbus_to_python(value) for value in data]

Rerunning the above example after making that change I get:

from bluezero import dbus_tools
import dbus
import struct
x = dbus.Array([1,2,3,4,5,6,7,8], signature='y')
#  x= dbus.Array([1, 2, 3, 4, 5, 6, 7, 8], signature=dbus.Signature('y'))
y = dbus_tools.dbus_to_python(x)
#  y = bytearray(b'\x01\x02\x03\x04\x05\x06\x07\x08')
z = struct.unpack('<II', y)
#  z = (67305985, 134678021)

Originally posted by @ukBaz in https://github.com/ukBaz/python-bluezero/issues/334#issuecomment-860014808