Here's another way using Python and GDBus.
The python-dbus module is I believe being deprecated (very slowly), and its not the best API. There are a few other promising projects for Python which can be found by googling.
For the Gtk/GLib/GObject world, the simplest is to use GDBus which is built in to Gio library (which comes with GLib). This is the recommended way for new Gtk-based code (rather than dbus module). If you are writing a Gtk app or trying to script a Gtk-based desktop environment (Gnome, Xfce, Cinnamon, Mate, Pantheon etc.), these libraries are probably already available. You can use in Python through Gobject Introspection (python gi module). Python gi API Docs here.
Here is an example introspect function which returns a single DBusNodeInfo.
from gi.repository import Gio, GLib
def introspect(bus, name, object_path):
res = bus.call_sync(
name, # bus_name
object_path, # object_path
'org.freedesktop.DBus.Introspectable', # interface_name
'Introspect', # method_name
None, # parameters
GLib.VariantType("(s)"), # reply_type
Gio.DBusCallFlags.NONE, # flags
-1, # timeout_msecs
None # cancellable
)
if not res:
return None
return Gio.DBusNodeInfo.new_for_xml(res[0])
(API docs link: Gio.DBusConnection.call_sync.)
Note that you will need to get the bus doing something like
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM)
or
bus = Gio.bus_get_sync(Gio.BusType.SESSION)
(See Gio.bus_get_sync docs)
DBusNodeInfo has nodes, interfaces and path properties. The path property contains just the last segment of the actual path (e.g. "bar", not "/foo/bar"). nodes is a list of DBusNodeInfo, but note that these are not recursively introspected, you must iterate over them, build the absolute path by joining to the parent path with a slash, and call introspect again.
As you can see, the library includes XML parser and parse tree API, unlike python-dbus, so there is no need to use Python's xml.etree etc.
Recursive introspection
Building on the above introspect function you could do something like (Python 3 code):
def introspect_tree(bus, name, object_path):
node = introspect(bus, name, object_path)
if node:
yield object_path, node
if object_path == '/':
object_path = ''
for child in node.nodes:
yield from introspect_tree(bus, name, f"{object_path}/{child.path}")
This is a generator of (object_path, node) pairs, where object_path is the absolute path and node is the DBusNodeInfo object.
If you just the want paths:
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM)
for path, node in introspect_tree(bus, 'org.freedesktop.UPower', '/org/freedesktop/UPower'):
print(path)
Prints:
/org/freedesktop/UPower
/org/freedesktop/UPower/Wakeups
/org/freedesktop/UPower/devices
/org/freedesktop/UPower/devices/DisplayDevice
/org/freedesktop/UPower/devices/battery_BAT0
/org/freedesktop/UPower/devices/line_power_ADP0
Introspecting interfaces
The interfaces property of DBusNodeInfo objects contains a list of Gio.DBusInterfaceInfo objects of the interfaces the object has. From there, you have name, methods, properties and signals properties.
Asynchronous API
Note that the above code is all synchronous, which is fine for command line apps and simple tools. However, there is also an asynchronous API which you will definitely want to use for GUI apps. All the functions that end with _sync have async versions e.g. call_sync has call and call_finish. There's also support for timeouts and cancellation (see timeout_msec and cancellable parameters of call/call_sync for example). Pretty easy to use once you figure it out, can be hard to find the docs though. Reading existing source code is a good way, especially the d-feet app source code which demonstrates asynchronous use of the APIs mentioned here.
gdbus CLI tool
Another fantastic part of GDBus is the gdbus command line tool which comes with the library (should be available in your Gtk-based desktop environments). Like qdbus (mentioned above), it has tab completion and excellent introspection functionality built in and is very useful for quick exploration and discovering names and paths.
Try:
gdbus introspect -r --system --dest org.freedesktop.UPower --object-path /org/freedesktop/UPower