Draft: `Gio.DBusTemplate`: A convenient abstraction to turn Python objects into D-Bus objects
This adds a new override Gio.DBusTemplate
. Classes can be decorated with this to turn Python classes into D-Bus objects with a syntax that is very familiar to users already using Gtk.Template
and GObject.Object.Property
(those were the inspirations for this). The usage is also very similiar to what I've seen in Vala and users coming from dbus-python
will also find this familiar but much more powerful.
The interface supports defining method, signal and property handlers for D-Bus objects. The Properties
and Introspectable
interfaces are also automatically implemented (thanks to Gio.DBusConnection.register_object
) and PropertiesChanged
is automatically emitted but can also be manually emitted if needed.
Example Usage
from gi.repository import Gio, GLib
from gdbus_ext import DBusTemplate
EXAMPLE_XML = """
<node>
<interface name='org.example.Example'>
<method name='MethodNoArgs'/>
<method name='MethodWithArg'>
<arg direction='in' name='Argument' type='s'/>
<arg direction='out' name='ReturnValue' type='b'/>
</method>
<property name='ExamplePropertyReadOnly' type='b' access='read' />
<property name='ExamplePropertyReadWrite' type='b' access='readwrite' />
</interface>
<interface name='org.example.AnotherExample'>
<property name='ExamplePropertyReadWrite' type='b' access='readwrite' />
<signal name='SomethingHappened'>
<arg name='Events' type='as'/>
</signal>
</interface>
</node>
"""
@DBusTemplate(string=EXAMPLE_XML)
class Example:
def connect_with_dbus(self, connection: Gio.DBusConnection):
DBusTemplate.register_object(
connection,
"org.example.Example",
"/org/example/Example",
self,
)
@DBusTemplate.Method()
def method_no_args(self):
...
@DBusTemplate.Method()
def method_with_arg(self, argument: str):
return argument == "example"
@DBusTemplate.Property()
def example_property_read_only(self):
return True
# In this example, this property is defined in multiple interfaces, so its interface name must be provided.
@DBusTemplate.Property(interface="org.example.Example")
def example_property_read_write(self):
return self._prop
@example_property_read_write.setter
def example_property_read_write(self, value):
self._prop = value
@DBusTemplate.Property(
interface="org.example.AnotherExample", name="ExamplePropertyReadWrite"
)
def example_property_read_write2(self):
return not self._prop
@example_property_read_write2.setter
def example_property_read_write2(self, value):
self._prop = not value
# Calling this method will emit the signal.
@DBusTemplate.Signal()
def something_happened(self, events: list[str]):
...
def some_external_event(self):
# Let's say some external event changed the internal read-only property
# org.example.Example.ExamplePropertyReadOnly.
# A property change signal can then be manually emitted. Note that this does not need to be done, if using
# property setters.
DBusTemplate.properties_changed(
self, "org.example.Example", ("ExamplePropertyReadOnly",)
)
if __name__ == "__main__":
loop = GLib.MainLoop()
connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
obj = Example()
obj.connect_with_dbus(connection)
loop.run()
Proof of Concept
I created an example implementation here: https://github.com/theCapypara/gio-dbustemplate-demo
This is a demo Gtk "music player" that implements MPRIS2 and uses Gio.DBusTemplate
.
Disclaimer
I have personally not used D-Bus much. I have read into this topic to write these abstractions, since I'm currently contributing to Workbench and saw that writing D-Bus objects in Python is currently pretty tedious, so I wrote this. I used Gtk.Template
and GNOME Music's abstraction as a base and built upon that and used the "Proof of Concept" to make sure the implementation and API are actually sound.
Type Hints
Since PyGObject supports Python 3.8 and all versions support the typing
module, I have kept the type hints in that I used during development. I recognize these are quite unusual for this project, so if you want I can remove or tone them down a bit.
Draft Status
This is still a draft because tests and docs are pending. I would like some feedback on the implementation and API first before diving into those. But I definitely plan to add both tests & docs.