Minimal example of GDBus in Python

Sorry forgot to include the screenshot

Have you been able to make any progress? I’m really interested in this and I hope that you succeed and maybe even contribute some examples somewhere or to documentation because It was really confusing when I looked into it.

1 Like

I’m surely doing something, don’t worry I will soon give you all a good result.

1 Like

Success!! After centuries of battle with many things, I was able to make a working example ! Yeah! I have included the source code below.

server.py
from gi.repository import Gio, GLib


# Thanks to  Gio test files at GNOME repo: glib/blob/master/gio/tests/gdbus-example-server.c
# Generating interface from XML is easy. Look at the above file on how it is done.
# A better resource : http://maemo.org/maemo_training_material/maemo4.x/html/
#                     maemo_Platform_Development_Chinook/
#                     Chapter_03_Using_the_GLib_wrappers_for_DBus.html
#                     #DBusinterfacedefinitionusingXML


xml = (
    "<node>"
    "  <interface name='org.examples.gdbus'>"
    "    <method name='PrintMsg'>"
    "      <arg type='s' name='msg' direction='in'>"
    "      </arg>"
    "    </method>"
    "    <method name='SayHello'>"
    "      <arg type='s' name='name' direction='in'/>"
    "      <arg type='s' name='greeting' direction='out'/>"
    "    </method>"
    "    <method name='Quit'/>"
    "  </interface>"
    "</node>"
)

node = Gio.DBusNodeInfo.new_for_xml(xml)  # We make a node for the xml
loop = GLib.MainLoop() # A loop to handle API

def handle_method_call(
    connection, sender, object_path, interface_name, method_name, params, invocation
):
    """
    This is the top-level function that handles all the method calls to our server.
    The first four parameters are self-explanatory.
    `method_name` is a string that describes our method name.
    `params` is a GLib.Variant that are inputs/parameters to the method.
    `invocation` is a Gio.DBusMethodInvocation, something like a messenger that transports
    our reply back to sender.
    """

    print("CALLED")
    # We need to unpack GLib.Variant to a Python object. The unpacked one is always a
    # tuple.
    if method_name == "PrintMsg":
        msg = params.unpack()[0]  # First argument is what we need
        print(f"FROM {sender} : {msg}")
        invocation.return_value(None)  # Nothing to say, so just return None.

    elif method_name == "SayHello":
        name = params.unpack()[0]
        print(f"FROM {sender} : GREET {name}")
        greeting = GLib.Variant(
            "(s)", (f"Long live {name}",)
        )  # All form of return should be a
        # variant.
        invocation.return_value(greeting)

    elif method_name == "Quit":
        loop.quit()
        invocation.return_value(None)

    # No need of an else part to handle methods which we didn't mention in XML.
    # When a client does something like that, they get an error.
    # The same holds for parameters.
    # Always return something (actually return value specified in XML) via invocation,
    # otherwise client get a response-timeout error.


def on_bus_acquired(connection, name):

    """
    The function that introduces our server to the world. It is called automatically
    when we get the bus we asked.
    """

    # Remember the node we made earlier? That has a list as interfaces attribute.
    # From that get our interface. We made only one interface, so we get the first
    # interface.

    print("Bus acquired for name, ", name)
    reg_id = connection.register_object(
        "/org/examples/gdbus", node.interfaces[0], handle_method_call, None, None
    )


def on_name_acquired(connection, name):

    """
    What to do after name acquired?
    """

    print("Name acquired :", name)


def on_name_lost(connection, name):

    """
    What to do after our name is lost? May be just exit.
    """

    print("Name lost :", name)
    exit(0)


if __name__ == "__main__":

    # Now we request the name and run the server.

    owner_id = Gio.bus_own_name(
        Gio.BusType.SESSION,  # What kinda bus is it?
        "org.examples.gdbus",  # It's name ?
        Gio.BusNameOwnerFlags.NONE,  # If other has same name, what to do?
        on_bus_acquired,
        on_name_acquired,
        on_name_lost,
    )

    loop.run()  # Like a web server, ours has to be active for client access
    Gio.bus_unown_name(owner_id)
    print("Exiting...")

client.py
from gi.repository import Gio, GLib

# Client is pretty straight-forward
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)  # What is the bus type?

# Below we are doing something like, 'Hey Gio, heard there is a DBus server
# with name org.examples.gdbus and type SESSION, get me a connection to it'
proxy = Gio.DBusProxy.new_sync(
    bus,
    Gio.DBusProxyFlags.NONE,
    None,
    "org.examples.gdbus",
    "/org/examples/gdbus",
    "org.examples.gdbus",
    None,
)

# proxy.call_sync is the way to call the method names of server.


def print_msg(msg):

    var = GLib.Variant("(s)", (msg,))  # Parameters should be variant.
    proxy.call_sync(
        "PrintMsg",  # Method name
        var,  # Parameters for method
        Gio.DBusCallFlags.NO_AUTO_START,  # Flags for call APIs
        500,  # How long to wait for reply? (in milliseconds)
        None,  # Cancellable, to cancel the call if you changed mind in middle)
    )


def say_hello(name):

    var = GLib.Variant("(s)", (name,))
    ret_var = proxy.call_sync(
        "SayHello", var, Gio.DBusCallFlags.NO_AUTO_START, 500, None
    )
    greeting = ret_var.unpack()[0]
    print(greeting)

def quit_server():

    ret_var = proxy.call_sync(
        "Quit", None, Gio.DBusCallFlags.NO_AUTO_START, 500, None
    )
    print("Server quit")

Fire up terminal and run server.py like python3 server.py. Client is an interactive one, so run it like this : python3 -i client.py. Then in interactive prompt, do whatever you want with defined methods.

>>> say_hello("GTK")
Long live GTK
>>> print_msg("It's fun") # Go to server.py console to see message
>>> quit_server() # Quits server
Server quit

Never thought this would be so easy ! This bare-bone example covers only method calls. So in my future research, I will learn and cover about signals, authentication etc.

Also we need this example (or a better one) to be available for Python GDBus in docs and other places. So I will also take a look at that.

Thanks @ebassi @lb90 @sthursfield @curioussavage and other wonderful contributors of GNOME :smile:

4 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.