1 """Module for high-level communication over the FreeDesktop.org Bus (DBus)
3 DBus allows you to share and access remote objects between processes
4 running on the desktop, and also to access system services (such as
7 To use DBus, first get a Bus object, which provides a connection to one
8 of a few standard dbus-daemon instances that might be running. From the
9 Bus you can get a RemoteService. A service is provided by an application or
10 process connected to the Bus, and represents a set of objects. Once you
11 have a RemoteService you can get a RemoteObject that implements a specific interface
12 (an interface is just a standard group of member functions). Then you can call
13 those member functions directly.
15 You can think of a complete method call as looking something like::
17 Bus:SESSION -> Service:org.gnome.Evolution -> Object:/org/gnome/Evolution/Inbox -> Interface: org.gnome.Evolution.MailFolder -> Method: Forward('message1', 'seth@gnome.org')
19 This communicates over the SESSION Bus to the org.gnome.Evolution process to call the
20 Forward method of the /org/gnome/Evolution/Inbox object (which provides the
21 org.gnome.Evolution.MailFolder interface) with two string arguments.
23 For example, the dbus-daemon itself provides a service and some objects::
25 # Get a connection to the desktop-wide SESSION bus
26 bus = dbus.Bus(dbus.Bus.TYPE_SESSION)
28 # Get the service provided by the dbus-daemon named org.freedesktop.DBus
29 dbus_service = bus.get_service('org.freedesktop.DBus')
31 # Get a reference to the desktop bus' standard object, denoted
32 # by the path /org/freedesktop/DBus. The object /org/freedesktop/DBus
33 # implements the 'org.freedesktop.DBus' interface
34 dbus_object = dbus_service.get_object('/org/freedesktop/DBus',
35 'org.freedesktop.DBus')
37 # One of the member functions in the org.freedesktop.DBus interface
38 # is ListServices(), which provides a list of all the other services
39 # registered on this bus. Call it, and print the list.
40 print(dbus_object.ListServices())
43 __all__
= ('Bus', 'SystemBus', 'SessionBus', 'StarterBus', 'Interface',
44 # From exceptions (DBusException originally from _dbus_bindings)
45 'DBusException', 'MissingErrorHandlerException',
46 'MissingReplyHandlerException', 'ValidationException',
47 'IntrospectionParserException', 'UnknownMethodException',
48 'NameExistsException',
49 # proxies, matchrules are not public API, so exclude them
51 __docformat__
= 'reStructuredText'
58 from exceptions
import *
59 from matchrules
import *
61 class Bus(_dbus_bindings
._Bus
):
62 """A connection to a DBus daemon.
64 One of three possible standard buses, the SESSION, SYSTEM,
67 TYPE_SESSION
= _dbus_bindings
.BUS_SESSION
68 TYPE_SYSTEM
= _dbus_bindings
.BUS_SYSTEM
69 TYPE_STARTER
= _dbus_bindings
.BUS_STARTER
71 """bus_type=[Bus.TYPE_SESSION | Bus.TYPE_SYSTEM | Bus.TYPE_STARTER]
74 ProxyObjectClass
= ProxyObject
76 START_REPLY_SUCCESS
= _dbus_bindings
.DBUS_START_REPLY_SUCCESS
77 START_REPLY_ALREADY_RUNNING
= _dbus_bindings
.DBUS_START_REPLY_ALREADY_RUNNING
79 _shared_instances
= weakref
.WeakValueDictionary()
81 def __new__(cls
, bus_type
=TYPE_SESSION
, use_default_mainloop
=True, private
=False):
82 """Constructor, returning an existing instance where appropriate.
84 The returned instance is actually always an instance of `SessionBus`,
85 `SystemBus` or `StarterBus`.
88 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
89 Connect to the appropriate bus
90 `use_default_mainloop` : bool
91 If true (default), automatically register the new connection
92 to be polled by the default main loop, if any
94 If true, never return an existing shared instance, but instead
95 return a private connection
97 if (not private
and bus_type
in cls
._shared
_instances
):
98 return cls
._shared
_instances
[bus_type
]
100 # this is a bit odd, but we create instances of the subtypes
101 # so we can return the shared instances if someone tries to
102 # construct one of them (otherwise we'd eg try and return an
103 # instance of Bus from __new__ in SessionBus). why are there
104 # three ways to construct this class? we just don't know.
105 if bus_type
== cls
.TYPE_SESSION
:
106 subclass
= SessionBus
107 elif bus_type
== cls
.TYPE_SYSTEM
:
109 elif bus_type
== cls
.TYPE_STARTER
:
110 subclass
= StarterBus
112 raise ValueError('invalid bus_type %s' % bus_type
)
114 bus
= _dbus_bindings
._Bus
.__new
__(subclass
, bus_type
)
116 bus
._bus
_type
= bus_type
117 bus
._bus
_names
= weakref
.WeakValueDictionary()
118 bus
._match
_rule
_tree
= SignalMatchTree()
120 bus
._add
_filter
(bus
._signal
_func
)
122 if use_default_mainloop
:
123 func
= getattr(dbus
, "_dbus_mainloop_setup_function", None)
128 cls
._shared
_instances
[bus_type
] = bus
132 def __init__(self
, *args
, **keywords
):
133 # do nothing here because this can get called multiple times on the
134 # same object if __new__ returns a shared instance
137 def get_connection(self
):
140 _connection
= property(get_connection
)
142 def get_session(private
=False):
143 """Static method that returns a connection to the session bus.
147 If true, do not return a shared connection.
149 return SessionBus(private
=private
)
151 get_session
= staticmethod(get_session
)
153 def get_system(private
=False):
154 """Static method that returns a connection to the system bus.
158 If true, do not return a shared connection.
160 return SystemBus(private
=private
)
162 get_system
= staticmethod(get_system
)
165 def get_starter(private
=False):
166 """Static method that returns a connection to the starter bus.
170 If true, do not return a shared connection.
172 return StarterBus(private
=private
)
174 get_starter
= staticmethod(get_starter
)
177 def get_object(self
, named_service
, object_path
):
178 """Return a local proxy for the given remote object.
180 Method calls on the proxy are translated into method calls on the
184 `named_service` : str
185 A bus name (either the unique name or a well-known name)
186 of the application owning the object
188 The object path of the desired object
189 :Returns: a `dbus.proxies.ProxyObject`
191 return self
.ProxyObjectClass(self
, named_service
, object_path
)
193 def _create_args_dict(self
, keywords
):
195 for (key
, value
) in keywords
.iteritems():
196 if key
.startswith('arg'):
204 args_dict
[num
] = value
206 raise TypeError("Invalid arg index %s"%snum
)
207 elif key
in ("sender_keyword", "path_keyword"):
210 raise TypeError("Unknown keyword %s"%(key))
214 def add_signal_receiver(self
, handler_function
,
220 """Arrange for the given function to be called when a signal matching
221 the parameters is emitted.
224 `handler_function` : callable
225 The function to be called.
227 The signal name; None (the default) matches all names
228 `dbus_interface` : str
229 The D-Bus interface name with which to qualify the signal;
230 None (the default) matches all interface names
231 `named_service` : str
232 A bus name for the sender, which will be resolved to a
233 unique name if it is not already; None (the default) matches
236 The object path of the object which must have emitted the
237 signal; None (the default) matches any object path
238 `sender_keyword` : str
239 If not None (the default), the handler function will receive
240 the unique name of the sending endpoint as a keyword
241 argument with this name
243 If not None (the default), the handler function will receive
244 the object-path of the sending object as a keyword argument
247 If there are additional keyword parameters of the form
248 ``arg``\ *n*, match only signals where the *n*\ th argument
249 is the value given for that keyword parameter
252 args_dict
= self
._create
_args
_dict
(keywords
)
254 if (named_service
and named_service
[0] != ':'):
255 bus_object
= self
.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
256 named_service
= bus_object
.GetNameOwner(named_service
, dbus_interface
='org.freedesktop.DBus')
258 match_rule
= SignalMatchRule(signal_name
, dbus_interface
, named_service
, path
)
260 for kw
in ("sender_keyword", "path_keyword"):
262 setattr(match_rule
, kw
, keywords
[kw
])
264 setattr(match_rule
, kw
, None)
267 match_rule
.add_args_match(args_dict
)
269 match_rule
.add_handler(handler_function
)
271 self
._match
_rule
_tree
.add(match_rule
)
273 self
.add_match_string(repr(match_rule
))
275 def remove_signal_receiver(self
, handler_function
,
282 args_dict
= self
._create
_args
_dict
(keywords
)
284 if (named_service
and named_service
[0] != ':'):
285 bus_object
= self
.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
286 named_service
= bus_object
.GetNameOwner(named_service
, dbus_interface
='org.freedesktop.DBus')
288 match_rule
= SignalMatchRule(signal_name
, dbus_interface
, named_service
, path
)
291 match_rule
.add_args_match(args_dict
)
293 if (handler_function
):
294 match_rule
.add_handler(handler_function
)
296 self
._match
_rule
_tree
.remove(match_rule
)
298 # TODO: remove the match string at the libdbus level
300 def _signal_func(self
, connection
, message
):
301 """D-Bus filter function. Handle signals by dispatching to Python
302 callbacks kept in the match-rule tree.
305 if (message
.get_type() != _dbus_bindings
.MESSAGE_TYPE_SIGNAL
):
306 return _dbus_bindings
.HANDLER_RESULT_NOT_YET_HANDLED
308 dbus_interface
= message
.get_interface()
309 named_service
= message
.get_sender()
310 path
= message
.get_path()
311 signal_name
= message
.get_member()
313 match_rule
= SignalMatchRule(signal_name
, dbus_interface
, named_service
, path
)
315 self
._match
_rule
_tree
.exec_matches(match_rule
, message
)
318 if self
._bus
_type
== self
.TYPE_SESSION
:
320 elif self
._bus
_type
== self
.TYPE_SYSTEM
:
322 elif self
._bus
_type
== self
.TYPE_STARTER
:
325 assert False, 'Unable to represent unknown bus type.'
327 return '<dbus.Bus on %s at %#x>' % (name
, id(self
))
331 # FIXME: Drop the subclasses here? I can't think why we'd ever want
333 class SystemBus(Bus
):
334 """The system-wide message bus."""
335 def __new__(cls
, use_default_mainloop
=True, private
=False):
336 """Return a connection to the system bus.
339 `use_default_mainloop` : bool
340 If true (default), automatically register the new connection
341 to be polled by the default main loop, if any
343 If true, never return an existing shared instance, but instead
344 return a private connection.
346 return Bus
.__new
__(cls
, Bus
.TYPE_SYSTEM
, use_default_mainloop
, private
)
348 class SessionBus(Bus
):
349 """The session (current login) message bus."""
350 def __new__(cls
, use_default_mainloop
=True, private
=False):
351 """Return a connection to the session bus.
354 `use_default_mainloop` : bool
355 If true (default), automatically register the new connection
356 to be polled by the default main loop, if any
358 If true, never return an existing shared instance, but instead
359 return a private connection.
361 return Bus
.__new
__(cls
, Bus
.TYPE_SESSION
, use_default_mainloop
, private
)
363 class StarterBus(Bus
):
364 """The bus that activated this process (only valid if
365 this process was launched by DBus activation).
367 def __new__(cls
, use_default_mainloop
=True, private
=False):
368 """Return a connection to the bus that activated this process.
371 `use_default_mainloop` : bool
372 If true (default), automatically register the new connection
373 to be polled by the default main loop, if any
375 If true, never return an existing shared instance, but instead
376 return a private connection.
378 return Bus
.__new
__(cls
, Bus
.TYPE_STARTER
, use_default_mainloop
, private
)
381 """An interface into a remote object.
383 An Interface can be used to wrap ProxyObjects
384 so that calls can be routed to their correct
388 def __init__(self
, object, dbus_interface
):
389 """Construct a proxy for the given interface on the given object.
392 `object` : `dbus.proxies.ProxyObject`
394 `dbus_interface` : str
395 An interface the `object` implements
398 self
._dbus
_interface
= dbus_interface
400 def connect_to_signal(self
, signal_name
, handler_function
, dbus_interface
= None, **keywords
):
401 """Arrange for a function to be called when the given signal is
406 The name of the signal
407 `handler_function` : callable
408 A function to be called (FIXME arguments?) when the signal
409 is emitted by the remote object.
410 `dbus_interface` : str
411 Optional interface with which to qualify the signal name.
412 The default is to use the interface this Interface represents.
413 `sender_keyword` : str
414 If not None (the default), the handler function will receive
415 the unique name of the sending endpoint as a keyword
416 argument with this name
418 If not None (the default), the handler function will receive
419 the object-path of the sending object as a keyword argument
422 If there are additional keyword parameters of the form
423 ``arg``\ *n*, match only signals where the *n*\ th argument
424 is the value given for that keyword parameter
426 if not dbus_interface
:
427 dbus_interface
= self
._dbus
_interface
429 self
._obj
.connect_to_signal(signal_name
, handler_function
, dbus_interface
, **keywords
)
431 def __getattr__(self
, member
, **keywords
):
432 # FIXME: this syntax is bizarre.
433 if (keywords
.has_key('dbus_interface')):
434 _dbus_interface
= keywords
['dbus_interface']
436 _dbus_interface
= self
._dbus
_interface
438 if member
== '__call__':
439 return object.__call
__
441 ret
= self
._obj
.__getattr
__(member
, dbus_interface
=_dbus_interface
)
445 return '<Interface %r implementing %r at %#x>'%(
446 self
._obj
, self
._dbus
_interface
, id(self
))