Minimal example of GDBus in Python

I want to make an application that will be using GDBus, but the API reference is too difficult (which function to start with? What is the best way? Am I doing it right?..) and the code examples I could find online are written in C. I saw a similar thread but it is for PyDBus and what I want is use of GDBus in Python.

Can anyone help?

Here you go:

3 Likes

Thanks that was great, but the server is written in C, can that be converted to Python? (Let me try my best in converting though)

1 Like

I’m stuck at handling signal connection, this is my progress :

import gi
from gi.repository import Gio, GLib, GObject


# Changed from Alarm to Printer for simplicity
class Printer(Gio.DBusInterfaceSkeleton):

    def __init__(self, **kwargs):
        Gio.DBusInterfaceSkeleton.__init__(self)
        self.msg = "Hello"

    def say_something(self):

        print(self.msg)


def on_handle_configure(printer, invocation, seconds):
    printer.say_something()


def on_name_acquired(conn, name):
    printer = Printer()
    Printer.connect("handle-configure", on_handle_configure, None) # => TypeError
    Printer.export(conn, "/com/movk/printer")


Gio.bus_own_name(Gio.BusType.SESSION,
                 "com.movk.printer",
                 Gio.BusNameOwnerFlags.NONE,
                 None,
                 on_name_acquired,
                 None)
GLib.MainLoop().run()

The error is :

File "service.py", line 22, in on_name_acquired
    Printer.connect("handle-configure", on_handle_configure, None)
TypeError: descriptor 'connect' requires a 'gi._gi.GObject' object but received a 'str'

Can anyone help?

Where the parameter type is a callback, you can pass a Python function and it will “just work”.

In your example - what happens if you pass the on_name_acquired function directly to bus_own_name() as the 5th parameter?

I’m sorry, the error was on line number 22:

File "service.py", line 22, in on_name_acquired
    Printer.connect("handle-configure", on_handle_configure, None)
TypeError: descriptor 'connect' requires a 'gi._gi.GObject' object but received a 'str'

Updated the code.

Try replacing Printer with printer on line 22.

Printer refers to the class, while printer refers to the instance of the class you created.

Thanks for that, a typo :joy: :man_facepalming:
Now I’m getting an unknown signal “handle-configure” error? Is it a valid signal for DBus-Skeleton?

Now I’m getting an unknown signal “handle-configure” error? Is it a valid signal for DBus-Skeleton?

No, I don’t see it in the documentation. You can use the .add_interface() method to implement the actual D-Bus interface that you want.

Ahhh its confusing. There are two things, Gio.DBusInterfaceSkeleton, which has export method but not add_interface and Gio.DBusObjectSkeleton which has your add_interface method but not export. Which one to use? How to use? Can you give an example?

I believe you should create an InterfaceSkeleton, add the methods you want to publish there, then add the interface to an ObjectSkeleton and export that.

Confusing… I’m confused how to export an interface. It’s already 12:33 AM here, let me continue fresh tomorrow…

Hello. How do I do that?

There is a lot to get your head around the first time! My advice is to experiment, and also to look at other similar projects even if they are written in a different languages :slight_smile:

What happens if you remove the line:

Printer.connect("handle-configure", on_handle_configure, None) 

is anything exported?

1 Like

Nope, Segmentation Fault.

I’m not going to give up, I can read C, so I’m looking at the examples included in the official API reference of DBus objects. Now I’m here : https://gitlab.gnome.org/GNOME/glib/-/blob/master/gio/tests/gdbus-example-server.c and looking for how to implement a Gio.DBusInterfaceInfo in Python.

If there’s a segmentation fault, you can use gdb to find out what happened. Use gdb --args python3 ... to run your program, type run to start execution, then backtrace to see where it crashed.

It’s reasonably common that PyGI code can trigger segfaults because it can call directly into the C code without checking that parameters you passed make sense, so if you pass an integer when the C code expects a string for example it’ll lead to a crash.

1 Like

Ok I will do it and report you asap.

@sthursfield I created the following scripts:

service.py
import gi
from gi.repository import Gio, GLib

node_xml = """
<node>
    <interface name='org.gtk.GDBus.TestInterface'>
      <annotation name='org.gtk.GDBus.Annotation' value='OnInterface'/>
      <annotation name='org.gtk.GDBus.Annotation' value='AlsoOnInterface'/>
      <method name='HelloWorld'>
        <annotation name='org.gtk.GDBus.Annotation' value='OnMethod'/>
        <arg type='s' name='greeting' direction='in'/>
        <arg type='s' name='response' direction='out'/>
      </method>
      <method name='EmitSignal'>
        <arg type='d' name='speed_in_mph' direction='in'>
          <annotation name='org.gtk.GDBus.Annotation' value='OnArg'/>
        </arg>
      </method>
      <method name='GimmeStdout'/>
      <signal name='VelocityChanged'>
        <annotation name='org.gtk.GDBus.Annotation' value='Onsignal'/>
        <arg type='d' name='speed_in_mph'/>
        <arg type='s' name='speed_as_string'>
          <annotation name='org.gtk.GDBus.Annotation' value='OnArg_NonFirst'/>
        </arg>
      </signal>
      <property type='s' name='FluxCapicitorName' access='read'>
        <annotation name='org.gtk.GDBus.Annotation' value='OnProperty'>
          <annotation name='org.gtk.GDBus.Annotation' value='OnAnnotation_YesThisIsCrazy'/>
        </annotation>
      </property>
      <property type='s' name='Title' access='readwrite'/>
      <property type='s' name='ReadingAlwaysThrowsError' access='read'/>
      <property type='s' name='WritingAlwaysThrowsError' access='readwrite'/>
      <property type='s' name='OnlyWritable' access='write'/>
      <property type='s' name='Foo' access='read'/>
      <property type='s' name='Bar' access='read'/>
    </interface>
</node>"""

NodeInfo = Gio.DBusNodeInfo.new_for_xml(node_xml)

def on_bus_acquired(conn, name):
    conn.register_object(conn,
                         "/org/Gtk/GDBus/TestInterface",
                         NodeInfo,
                         print,
                         print,
                         print)

def on_name_acquired(conn, name):
    print("Name acquired")

def on_name_lost(conn, name):
    print("Name lost")

Gio.bus_own_name(Gio.BusType.SESSION,
                 "org.gtk.GDBus.TestInterface",
                 Gio.BusNameOwnerFlags.NONE,
                 None,
                 on_name_acquired,
                 on_name_lost)
GLib.MainLoop().run()

Running it, I got Name acquired.

client.py

import sys
from gi.repository import GLib, Gio

if len(sys.argv) < 2:
sys.stderr.write(“Tell something\n”)
sys.exit(1)

if len(sys.argv) > 2:
sys.stderr.write(“Too many arguments\n”)
sys.exit(1)

connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
proxy = Gio.DBusProxy.new_sync(connection,
Gio.DBusProxyFlags.NONE,
None,
“org.gtk.GDBus.TestInterface”,
“/org/Gtk/GDBus/TestInterface”,
“org.gtk.GDBus.TestInterface”,
None)
try:
proxy.call_sync(“Configure”,
GLib.Variant("(u)", (int(sys.argv[1]),)),
Gio.DBusCallFlags.NONE,
-1,
None)
except Exception as e:
sys.stderr.write(“Error: %s\n” % str(e))
else:
print(“Done”)

Running it with service.py already running, I got this:

$ python3 client.py 13
Error: g-dbus-error-quark: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface “org.gtk.GDBus.TestInterface” on object at path /org/Gtk/GDBus/TestInterface (19)

What’s next?

I find the tool d-feet very useful. Try using that to test your D-Bus service.