Skip to content

Commit

Permalink
Support asynchronous calls (LEW21#58)
Browse files Browse the repository at this point in the history
Added support for asynchronous calls of methods. A method is called
synchronously unless its callback parameter is specified. A callback
is a function f(*args, returned=None, error=None), where args is
callback_args specified in the method call, returned is a return
value of the method and error is an exception raised by the method.

Example of an asynchronous call:

def func(x, y, returned=None, error=None):
  pass

proxy.Method(a, b, callback=func, callback_args=(x, y))
  • Loading branch information
poncovka committed Jul 28, 2017
1 parent 6be6273 commit 47c8f3e
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 6 deletions.
44 changes: 38 additions & 6 deletions pydbus/proxy_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,54 @@ def __call__(self, instance, *args, **kwargs):

# Python 2 sux
for kwarg in kwargs:
if kwarg not in ("timeout",):
if kwarg not in ("timeout", "callback", "callback_args"):
raise TypeError(self.__qualname__ + " got an unexpected keyword argument '{}'".format(kwarg))
timeout = kwargs.get("timeout", None)
callback = kwargs.get("callback", None)
callback_args = kwargs.get("callback_args", tuple())

call_args = (
instance._bus_name,
instance._path,
self._iface_name,
self.__name__,
GLib.Variant(self._sinargs, args),
GLib.VariantType.new(self._soutargs),
0,
timeout_to_glib(timeout),
None
)

if callback:
call_args += (self._finish_async_call, (callback, callback_args))
instance._bus.con.call(*call_args)
return None
else:
ret = instance._bus.con.call_sync(*call_args)
return self._unpack_return(ret)

ret = instance._bus.con.call_sync(
instance._bus_name, instance._path,
self._iface_name, self.__name__, GLib.Variant(self._sinargs, args), GLib.VariantType.new(self._soutargs),
0, timeout_to_glib(timeout), None).unpack()

def _unpack_return(self, values):
ret = values.unpack()
if len(self._outargs) == 0:
return None
elif len(self._outargs) == 1:
return ret[0]
else:
return ret

def _finish_async_call(self, source, result, user_data):
error = None
return_args = None

try:
ret = source.call_finish(result)
return_args = self._unpack_return(ret)
except Exception as err:
error = err

callback, callback_args = user_data
callback(*callback_args, returned=return_args, error=error)

def __get__(self, instance, owner):
if instance is None:
return self
Expand Down
67 changes: 67 additions & 0 deletions tests/publish_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from pydbus import SessionBus
from gi.repository import GLib
from threading import Thread
import sys

done = 0
loop = GLib.MainLoop()

class TestObject(object):
'''
<node>
<interface name='net.lew21.pydbus.tests.publish_async'>
<method name='HelloWorld'>
<arg type='i' name='x' direction='in'/>
<arg type='s' name='response' direction='out'/>
</method>
<method name='Quit'/>
</interface>
</node>
'''
def __init__(self, id):
self.id = id

def HelloWorld(self, x):
res = self.id + ": " + str(x)
print(res)
return res

def Quit(self):
loop.quit()

bus = SessionBus()

with bus.publish("net.lew21.pydbus.tests.publish_async", TestObject("Obj")):
remote = bus.get("net.lew21.pydbus.tests.publish_async")

def callback(x, returned=None, error=None):
print("asyn: " + returned)
assert (returned is not None)
assert(error is None)
assert(x == int(returned.split()[1]))

global done
done += 1
if done == 3:
loop.quit()

def t1_func():
remote.HelloWorld(1, callback=callback, callback_args=(1,))
remote.HelloWorld(2, callback=callback, callback_args=(2,))
print("sync: " + remote.HelloWorld(3))
remote.HelloWorld(4, callback=callback, callback_args=(4,))

t1 = Thread(None, t1_func)
t1.daemon = True

def handle_timeout():
print("ERROR: Timeout.")
sys.exit(1)

GLib.timeout_add_seconds(2, handle_timeout)

t1.start()

loop.run()

t1.join()
1 change: 1 addition & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ then
"$PYTHON" $TESTS_DIR/publish.py
"$PYTHON" $TESTS_DIR/publish_properties.py
"$PYTHON" $TESTS_DIR/publish_multiface.py
"$PYTHON" $TESTS_DIR/publish_async.py
fi

0 comments on commit 47c8f3e

Please sign in to comment.