LEW21 / pydbus

Pythonic DBus library
GNU Lesser General Public License v2.1
326 stars 76 forks source link

Fix for Issue #69, Methods returning non-trivial types fail #75

Closed nburkitt closed 6 years ago

nburkitt commented 6 years ago

ProxyMethod.__call__() does not correctly handle the case of a proxied method that returns a single dbus object. That is, a method that returns a value with a dbus signature of the form "(xyz)".

I found two problems. The first is in the code that builds the proxied method's return type's signature.

Consider the case of a proxied method with self._outargs = ["(dsii)"]. To build self._soutargs, ProxyMethod.__init__() unconditionally adds enclosing parentheses:

self._soutargs = "(" + "".join(self._outargs) + ")"

This will result in an error being returned from instance._bus.con.call_sync() when called from ProxyMethod.__call__():

gi._glib.GError: Method 'GetOrientation' returned type '(dsii)', but expected '((dsii))'

The second problem is in ProxyMethod.call(), in handling the value returned by call_sync(), which is a tuple, assigned to the variable ret:

if len(self._outargs) == 0:
    return None
elif len(self._outargs) == 1:
    return ret[0]
else:
    return ret

If the proxied method returns a single object, len(self._outargs) will be 1, and only the first element of the tuple will be returned from ProxyMethod.__call__().

To handle this particular case, I added a state variable self._ret_obj, which ProxyMethod.__init__() sets to True iff len(self._outargs) == 1 and self._outargs[0] starts with "(". In other words, if this flag is set, the proxied function will return a single argument, which will be a dbus object.

self._ret_obj = len(self._outargs) == 1 and self._outargs[0].startswith("(")

It then tests self._ret_obj and adds enclosing parentheses if it is False.

ProxyMethod.__call__() tests self._ret_obj when deciding whether to return the entire tuple or only the first element:

if len(self._outargs) == 0:
    return None
elif len(self._outargs) == 1 and not self._ret_obj:
    return ret[0]
else:
    return ret
nburkitt commented 6 years ago

The real problem was an misbehaving application.