pydbus icon indicating copy to clipboard operation
pydbus copied to clipboard

Support async methods

Open pespin opened this issue 8 years ago • 5 comments

I could not find any API which exports or uses async methods from Gio. I was able to workaround the issue by implementing the following snippet in my code:

def __async_result_handler(obj, result, user_data):
    '''Generic callback dispatcher called from glib loop when an async method
    call has returned. This callback is set up by method dbus_async_call.'''
    (result_callback, error_callback, real_user_data) = user_data
    try:
        ret = obj.call_finish(result)
    except Exception:
        etype, e = sys.exc_info()[:2]
        # return exception as value
        if error_callback:
            error_callback(obj, e, real_user_data)
        else:
            result_callback(obj, e, real_user_data)
        return

    ret = ret.unpack()
    # to be compatible with standard Python behaviour, unbox
    # single-element tuples and return None for empty result tuples
    if len(ret) == 1:
        ret = ret[0]
    elif len(ret) == 0:
        ret = None
    result_callback(obj, ret, real_user_data)

def dbus_async_call(proxymethod, instance, *args, **kwargs):
    '''pydbus doesn't support asynchronous methods. This method adds support for
    it until pydbus implements it'''
    argdiff = len(args) - len(proxymethod._inargs)
    if argdiff < 0:
        raise TypeError(proxymethod.__qualname__ + " missing {} required positional argument(s)".format(-argdiff))
    elif argdiff > 0:
        raise TypeError(proxymethod.__qualname__ + " takes {} positional argument(s) but {} was/were given".format(len(proxymethod._inargs), len(args)))

    timeout = kwargs.get("timeout", 30) * 1000
    user_data = (kwargs['result_handler'], kwargs.get('error_handler'), kwargs.get('user_data'))

    ret = instance._bus.con.call(
        instance._bus_name, instance._path,
        proxymethod._iface_name, proxymethod.__name__,
        GLib.Variant(proxymethod._sinargs, args), GLib.VariantType.new(proxymethod._soutargs),
        0, timeout, None,
        __async_result_handler, user_data)

Then call it with something like this:

        dbus_async_call(nr.Scan, nr, timeout=30, result_handler=self.scan_result_cb,
                        error_handler=self.raise_exn_cb, user_data=None)

    def raise_exn_cb(self, obj, e, user_data):
        pass

    def scan_result_cb(self, obj, result, user_data):
        self.register(result)

It would be nice that pydbus could support this out of the box, by using something like dbus_object.foo_method(param0, ..., param N, timeout=XYZ, reply_handler=myfunction, error_handler=myotherfunction)

pespin avatar May 31 '17 11:05 pespin

I've got the system so all the parameters can be named, with defaults, along with the arglist. Since I created a patch so that pydbus works on even older versions, I worked through that routine. I'll see about breaking out the callback / error callback. The timeout is set already at registration time.

On 05/31/2017 06:00 AM, Pau Espin Pedrol wrote:

I could not find any API which exports or uses async methods from Gio. I was able to workaround the issue by implementing the following snippet in my code:

|def __async_result_handler(obj, result, user_data): '''Generic callback dispatcher called from glib loop when an async method call has returned. This callback is set up by method dbus_async_call.''' (result_callback, error_callback, real_user_data) = user_data try: ret = obj.call_finish(result) except Exception: etype, e = sys.exc_info()[:2] # return exception as value if error_callback: error_callback(obj, e, real_user_data) else: result_callback(obj, e, real_user_data) return ret = ret.unpack() # to be compatible with standard Python behaviour, unbox # single-element tuples and return None for empty result tuples if len(ret) == 1: ret = ret[0] elif len(ret) == 0: ret = None result_callback(obj, ret, real_user_data) def dbus_async_call(proxymethod, instance, *args, **kwargs): '''pydbus doesn't support asynchronous methods. This method adds support for it until pydbus implements it''' argdiff = len(args) - len(proxymethod._inargs) if argdiff < 0: raise TypeError(proxymethod.qualname + " missing {} required positional argument(s)".format(-argdiff)) elif argdiff > 0: raise TypeError(proxymethod.qualname + " takes {} positional argument(s) but {} was/were given".format(len(proxymethod._inargs), len(args))) timeout = kwargs.get("timeout", 30) * 1000 user_data = (kwargs['result_handler'], kwargs.get('error_handler'), kwargs.get('user_data')) ret = instance._bus.con.call( instance._bus_name, instance._path, proxymethod._iface_name, proxymethod.name, GLib.Variant(proxymethod._sinargs, args), GLib.VariantType.new(proxymethod._soutargs), 0, timeout, None, __async_result_handler, user_data) |

Then call it with something like this:

|dbus_async_call(nr.Scan, nr, timeout=30, result_handler=self.scan_result_cb, error_handler=self.raise_exn_cb, user_data=None) def raise_exn_cb(self, obj, e, user_data): pass def scan_result_cb(self, obj, result, user_data): self.register(result) |

It would be nice that pydbus could support this out of the box, by using something like dbus_object.foo_method(param0, ..., param N, timeout=XYZ, reply_handler=myfunction, error_handler=myotherfunction)

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/LEW21/pydbus/issues/58, or mute the thread https://github.com/notifications/unsubscribe-auth/AFtFs9WOYjtkcDaWlV4EnuB2N2WIc7P-ks5r_Ug_gaJpZM4NrfWI.

-- Regards,

Harry G. Coin, Manager Quiet Fountain LLC

hcoin avatar May 31 '17 13:05 hcoin

Hi, what's your current status and your target? Do you plan to merge those changes back in this repo?

Timeout being set at registration time means it's set once for the entire proxy object? imho it would be nice being able to set the timeout at least for each method, and possibly also each time we call the method.

pespin avatar Jun 08 '17 11:06 pespin

Thanks for this interest. Your answers (I hope) are here: https://github.com/hcoin/pydbus/blob/master/status.txt

hcoin avatar Jun 08 '17 14:06 hcoin

Much work done, only one major commit left. Plus install instructions known good for all major distros including publishing on pre 2.46 glibs. https://github.com/hcoin/pydbus I'm planning to have it be 'alpha release ready' 8/1/17. I will be looking for a few people with known pydbus experience to be committers along with me.

hcoin avatar Jul 25 '17 15:07 hcoin

Hopefully Linus too!

hcoin avatar Jul 25 '17 15:07 hcoin