LEW21 / pydbus

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

method proxy; interact with system bus while handling signal on session bus? #81

Open bewest opened 5 years ago

bewest commented 5 years ago

Thanks for pydbus.

I have a main loop set up like this:

from threading import Thread, Event
from gi.repository import GLib, Gio
# ...
loop = GLib.MainLoop( )
bus = SessionBus( )

# setup exported classes, bound to session bus
class Main (object):
   def __init__ (self):
     # ...
  def __call__ (self, *args, **kwds):
    bus.publish(...)

# BluezHandler on system bus
class BluezHandler (object):
  def __init__ (self, system_bus, parent):
    self.system = SystemBus( )
    self.remote = parent
    # ...
    self.remote = self.system.get('org.bluez', path)
  def Connect (self):
    self.remote.Connect( )
    # other methods set up PropertiesOwned signals of other system owned objects
  def MakeBLEThing (self):
    path = "{}/Thing".format(self._path)
    # self.thing = RespondsWhileDoingThingsOnDbus(self)

if __name__ == "__main__":
  GLib.threads_init( )
  main = Main( )
  thread = Thread(target=main)
  thread.daemon = True
  thread.start( )
  loop.run( )

When I handle method calls from exported to dbus via Main, I create a reference to the system bus. While I'm handling a method during one of Main's, I need to block until PropertiesReceived and other signals occur on BluezHandler's references.

I've read https://github.com/LEW21/pydbus/issues/42, https://github.com/LEW21/pydbus/commit/9d8d8c1d237cee70614e1df9f62e74ad7308f2fe, https://github.com/LEW21/pydbus/pull/15, and https://github.com/LEW21/pydbus/issues/68

My use case is providing a session-bound application that makes use of a bluetooth device. This requires that while a method is dispatched to my application code, I need to defer the return value of my method until after other dbus messages and properties (to connect, authorize, and enumerate services, write, read and respond to notifications) can happen as well. In other words, while handling a method call on an exported object, I need to respond and make use of dbus in other ways in the meantime.

There are suggestions to use GLib.add_idle in order to yield time back to the mainloop, including some clever generators, https://wiki.gnome.org/Projects/PyGObject/Threading, http://code.activestate.com/recipes/577129-run-asynchronous-tasks-using-coroutines/, however I don't think these work because the form still ultimately blocks looking at updated properties or deadlocks, even when using add_idle explicitly.

Hopefully this adds some color to the use cases @akruis advocated for. In addition, this works using python-dbus; https://dbus.freedesktop.org/doc/dbus-python/dbus.decorators.html; the async_callbacks keyword provides a tuple for (success, error) callbacks. In reading through https://lazka.github.io/pgi-docs/#Gio-2.0/classes/DBusConnection.html#gio-dbusconnection, it's not clear to me what the async dispatch to a method would look like.

For example, as I see something like this:

Traceback (most recent call last):
  File "shared.py", line 347, in later
    self.remote.Connect( )
  File "/home/bewest/src/pydbus/pydbus/proxy_method.py", line 75, in __call__
    0, timeout_to_glib(timeout), None).unpack()
GLib.Error: g-io-error-quark: GDBus.Error:org.bluez.Error.Failed: Software caused connection abort (36)

I can see by using gdbus monitor that events I'm interested in are happening. Hopefully that's more clear than mud? Any advice welcome. This delta looks interesting, but I can't tell if it helps solve this problem? https://github.com/LEW21/pydbus/compare/master...greenglib-dev?expand=1 Any examples of how to react to changes on the bus during a method call?