SImple (but specific) listener and sender Python 3 DBus example

前端 未结 1 585
情深已故
情深已故 2021-02-03 16:09

I want to make a program that has two parts. A listener (a server, if you will) and a sender (the client). I did some research and learned that this is done via a method program

相关标签:
1条回答
  • 2021-02-03 16:36

    There's not a lot of documentation for dbus in python3, but I managed to figure it out so I'll document it here: The major difference from all the python2 examples is replacing import gobject with import gi.repository.GLib.

    You can find more examples (which use more features than I needed) in the dbus-python examples directory.

    I didn't implement self-backgrounding in the server because that style of daemon has gone out of style recently.

    common.py:

    # well-known name for our program
    ECHO_BUS_NAME = 'com.stackoverflow.question_21793826.EchoService'
    
    # interfaces implemented by some objects in our program
    ECHO_INTERFACE = 'com.stackoverflow.question_21793826.EchoInterface'
    QUIT_INTERFACE = 'com.stackoverflow.question_21793826.QuitInterface'
    
    # paths to some objects in our program
    ECHO_OBJECT_PATH = '/EchoServerObject'
    

    server.py:

    #!/usr/bin/env python3
    
    # standard includes
    import sys
    
    # dbus includes
    import gi.repository.GLib
    import dbus
    import dbus.service
    import dbus.mainloop.glib
    
    # project includes
    import common
    
    
    class EchoServerObject(dbus.service.Object):
    
        # TODO it would be nice to make a better decorator using annotations:
        #   def foo(self, a: 's', b: 's') -> '': pass
        # but the existing dbus decorator does its own reflection which
        # fails if there are any annotations (or keyword-only arguments)
        @dbus.service.method(common.ECHO_INTERFACE,
                in_signature='s', out_signature='')
        def echo(self, message):
            message = str(message) # get rid of subclass for repr
            print('server: a client said %r' % message)
    
        @dbus.service.method(common.QUIT_INTERFACE,
                in_signature='', out_signature='')
        def quit(self):
            # this should be a separate object, but I'm
            # showing how one object can have multiple interfaces
            self.mainloop.quit()
    
    def stop():
        bus = dbus.SessionBus()
    
        proxy = bus.get_object(common.ECHO_BUS_NAME, common.ECHO_OBJECT_PATH)
        iface = dbus.Interface(proxy, common.QUIT_INTERFACE)
    
        iface.quit()
    
    def server():
        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    
        bus = dbus.SessionBus()
        try:
            name = dbus.service.BusName(common.ECHO_BUS_NAME, bus, do_not_queue=True)
        except dbus.NameExistsException:
            sys.exit('Server is already running.')
        else:
            print('Server is not running yet. Putting on listening ears.')
        echo = EchoServerObject(bus, common.ECHO_OBJECT_PATH)
    
        mainloop = gi.repository.GLib.MainLoop()
        echo.mainloop = mainloop
        mainloop.run()
    
    def main(exe, args):
        if args == ['stop']:
            stop()
        elif not args:
            server()
        else:
            sys.exit('Usage: %s [stop]' % exe)
    
    if __name__ == '__main__':
        main(sys.argv[0], sys.argv[1:])
    

    client.py:

    #!/usr/bin/env python3
    
    # standard includes
    import sys
    
    # dbus includes
    import dbus
    
    # project includes
    import common
    
    
    def client(mes):
        bus = dbus.SessionBus()
    
        try:
            proxy = bus.get_object(common.ECHO_BUS_NAME, common.ECHO_OBJECT_PATH)
        except dbus.DBusException as e:
            # There are actually two exceptions thrown:
            # 1: org.freedesktop.DBus.Error.NameHasNoOwner
            #   (when the name is not registered by any running process)
            # 2: org.freedesktop.DBus.Error.ServiceUnknown
            #   (during auto-activation since there is no .service file)
            # TODO figure out how to suppress the activation attempt
            # also, there *has* to be a better way of managing exceptions
            if e._dbus_error_name != 'org.freedesktop.DBus.Error.ServiceUnknown':
                raise
            if e.__context__._dbus_error_name != 'org.freedesktop.DBus.Error.NameHasNoOwner':
                raise
            print('client: No one can hear me!!')
        else:
            iface = dbus.Interface(proxy, common.ECHO_INTERFACE)
            iface.echo(mes)
    
    def main(exe, args):
        if args:
            client(' '.join(args))
        else:
            sys.exit('Usage: %s message...' % exe)
    
    if __name__ == '__main__':
        main(sys.argv[0], sys.argv[1:])
    
    0 讨论(0)
提交回复
热议问题