dbus._dbus, _dbus_bindings, dbus.proxies: Add docstrings
[dbus-python-phuang.git] / dbus / _dbus.py
bloba7cd2c53f899c8b368d5298627ee75aee167017c
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
5 the print spool).
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())
41 """
43 __all__ = ('Bus', 'SystemBus', 'SessionBus', 'StarterBus', 'Interface')
44 __docformat__ = 'reStructuredText'
46 import dbus
47 import _dbus_bindings
48 import weakref
50 from proxies import *
51 from exceptions import *
52 from matchrules import *
54 class Bus(object):
55 """A connection to a DBus daemon.
57 One of three possible standard buses, the SESSION, SYSTEM,
58 or STARTER bus
59 """
60 TYPE_SESSION = _dbus_bindings.BUS_SESSION
61 TYPE_SYSTEM = _dbus_bindings.BUS_SYSTEM
62 TYPE_STARTER = _dbus_bindings.BUS_STARTER
64 """bus_type=[Bus.TYPE_SESSION | Bus.TYPE_SYSTEM | Bus.TYPE_STARTER]
65 """
67 ProxyObjectClass = ProxyObject
69 START_REPLY_SUCCESS = _dbus_bindings.DBUS_START_REPLY_SUCCESS
70 START_REPLY_ALREADY_RUNNING = _dbus_bindings.DBUS_START_REPLY_ALREADY_RUNNING
72 _shared_instances = weakref.WeakValueDictionary()
74 def __new__(cls, bus_type=TYPE_SESSION, use_default_mainloop=True, private=False):
75 """Constructor, returning an existing instance where appropriate.
77 The returned instance is actually always an instance of `SessionBus`,
78 `SystemBus` or `StarterBus`.
80 :Parameters:
81 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
82 Connect to the appropriate bus
83 `use_default_mainloop` : bool
84 If true (default), automatically register the new connection
85 to be polled by the default main loop, if any
86 `private` : bool
87 If true, never return an existing shared instance, but instead
88 return a private connection
89 """
90 if (not private and bus_type in cls._shared_instances):
91 return cls._shared_instances[bus_type]
93 # this is a bit odd, but we create instances of the subtypes
94 # so we can return the shared instances if someone tries to
95 # construct one of them (otherwise we'd eg try and return an
96 # instance of Bus from __new__ in SessionBus). why are there
97 # three ways to construct this class? we just don't know.
98 if bus_type == cls.TYPE_SESSION:
99 subclass = SessionBus
100 elif bus_type == cls.TYPE_SYSTEM:
101 subclass = SystemBus
102 elif bus_type == cls.TYPE_STARTER:
103 subclass = StarterBus
104 else:
105 raise ValueError('invalid bus_type %s' % bus_type)
107 bus = object.__new__(subclass)
109 bus._bus_type = bus_type
110 bus._bus_names = weakref.WeakValueDictionary()
111 bus._match_rule_tree = SignalMatchTree()
113 # FIXME: if you get a starter and a system/session bus connection
114 # in the same process, it's the same underlying connection that
115 # is returned by bus_get, but we initialise it twice
116 bus._connection = _dbus_bindings.bus_get(bus_type, private)
117 bus._connection.add_filter(bus._signal_func)
119 if use_default_mainloop:
120 func = getattr(dbus, "_dbus_mainloop_setup_function", None)
121 if func:
122 func(bus)
124 if not private:
125 cls._shared_instances[bus_type] = bus
127 return bus
129 def __init__(self, *args, **keywords):
130 # do nothing here because this can get called multiple times on the
131 # same object if __new__ returns a shared instance
132 pass
134 def close(self):
135 """Close the connection."""
136 self._connection.close()
138 def get_connection(self):
139 """Return the underlying `_dbus_bindings.Connection`."""
140 return self._connection
142 def get_session(private=False):
143 """Static method that returns a connection to the session bus.
145 :Parameters:
146 `private` : bool
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.
156 :Parameters:
157 `private` : bool
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.
168 :Parameters:
169 `private` : bool
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 """Get a proxy object to call over the bus"""
179 return self.ProxyObjectClass(self, named_service, object_path)
181 def _create_args_dict(self, keywords):
182 args_dict = None
183 for (key, value) in keywords.iteritems():
184 if key.startswith('arg'):
185 try:
186 snum = key[3:]
187 num = int(snum)
189 if not args_dict:
190 args_dict = {}
192 args_dict[num] = value
193 except ValueError:
194 raise TypeError("Invalid arg index %s"%snum)
195 elif key in ("sender_keyword", "path_keyword"):
196 pass
197 else:
198 raise TypeError("Unknown keyword %s"%(key))
200 return args_dict
202 def add_signal_receiver(self, handler_function,
203 signal_name=None,
204 dbus_interface=None,
205 named_service=None,
206 path=None,
207 **keywords):
208 """Arrange for the given function to be called when a signal matching
209 the parameters is emitted.
211 :Parameters:
212 `handler_function` : callable
213 The function to be called.
214 `signal_name` : str
215 The signal name; None (the default) matches all names
216 `dbus_interface` : str
217 The D-Bus interface name with which to qualify the signal;
218 None (the default) matches all interface names
219 `named_service` : str
220 A bus name for the sender, which will be resolved to a
221 unique name if it is not already; None (the default) matches
222 any sender
223 `path` : str
224 The object path of the object which must have emitted the
225 signal; None (the default) matches any object path
226 `sender_keyword` : str
227 If not None (the default), the handler function will receive
228 the unique name of the sending endpoint as a keyword
229 argument with this name
230 `path_keyword` : str
231 If not None (the default), the handler function will receive
232 the object-path of the sending object as a keyword argument
233 with this name
234 `keywords`
235 If there are additional keyword parameters of the form
236 ``arg``\ *n*, match only signals where the *n*\ th argument
237 is the value given for that keyword parameter
240 args_dict = self._create_args_dict(keywords)
242 if (named_service and named_service[0] != ':'):
243 bus_object = self.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
244 named_service = bus_object.GetNameOwner(named_service, dbus_interface='org.freedesktop.DBus')
246 match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
248 for kw in ("sender_keyword", "path_keyword"):
249 if kw in keywords:
250 setattr(match_rule, kw, keywords[kw])
251 else:
252 setattr(match_rule, kw, None)
254 if args_dict:
255 match_rule.add_args_match(args_dict)
257 match_rule.add_handler(handler_function)
259 self._match_rule_tree.add(match_rule)
261 _dbus_bindings.bus_add_match(self._connection, repr(match_rule))
263 def remove_signal_receiver(self, handler_function,
264 signal_name=None,
265 dbus_interface=None,
266 named_service=None,
267 path=None,
268 **keywords):
270 args_dict = self._create_args_dict(keywords)
272 if (named_service and named_service[0] != ':'):
273 bus_object = self.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
274 named_service = bus_object.GetNameOwner(named_service, dbus_interface='org.freedesktop.DBus')
276 match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
278 if (args_dict):
279 match_rule.add_args_match(args_dict)
281 if (handler_function):
282 match_rule.add_handler(handler_function)
284 self._match_rule_tree.remove(match_rule)
286 #TODO we leak match rules in the lower level bindings. We need to ref count them
288 def get_unix_user(self, named_service):
289 """Get the unix user for the given named_service on this Bus"""
290 return _dbus_bindings.bus_get_unix_user(self._connection, named_service)
292 def _signal_func(self, connection, message):
293 if (message.get_type() != _dbus_bindings.MESSAGE_TYPE_SIGNAL):
294 return _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
296 dbus_interface = message.get_interface()
297 named_service = message.get_sender()
298 path = message.get_path()
299 signal_name = message.get_member()
301 match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
303 self._match_rule_tree.exec_matches(match_rule, message)
305 def start_service_by_name(self, named_service):
306 """Start a service which will implement the given bus name on this
307 Bus.
309 :Parameters:
310 `named_service` : str
311 The well-known bus name for which an implementation is required
313 return _dbus_bindings.bus_start_service_by_name(self._connection, named_service)
315 def __repr__(self):
316 if self._bus_type == self.TYPE_SESSION:
317 name = 'SESSION'
318 elif self._bus_type == self.TYPE_SYSTEM:
319 name = 'SYSTEM'
320 elif self._bus_type == self.TYPE_STARTER:
321 name = 'STARTER'
322 else:
323 assert False, 'Unable to represent unknown bus type.'
325 return '<dbus.Bus on %s at %#x>' % (name, id(self))
326 __str__ = __repr__
328 class SystemBus(Bus):
329 """The system-wide message bus."""
330 def __new__(cls, use_default_mainloop=True, private=False):
331 """Return a connection to the system bus.
333 :Parameters:
334 `use_default_mainloop` : bool
335 If true (default), automatically register the new connection
336 to be polled by the default main loop, if any
337 `private` : bool
338 If true, never return an existing shared instance, but instead
339 return a private connection.
341 return Bus.__new__(cls, Bus.TYPE_SYSTEM, use_default_mainloop, private)
343 class SessionBus(Bus):
344 """The session (current login) message bus."""
345 def __new__(cls, use_default_mainloop=True, private=False):
346 """Return a connection to the session bus.
348 :Parameters:
349 `use_default_mainloop` : bool
350 If true (default), automatically register the new connection
351 to be polled by the default main loop, if any
352 `private` : bool
353 If true, never return an existing shared instance, but instead
354 return a private connection.
356 return Bus.__new__(cls, Bus.TYPE_SESSION, use_default_mainloop, private)
358 class StarterBus(Bus):
359 """The bus that activated this process (only valid if
360 this process was launched by DBus activation).
362 def __new__(cls, use_default_mainloop=True, private=False):
363 """Return a connection to the bus that activated this process.
365 :Parameters:
366 `use_default_mainloop` : bool
367 If true (default), automatically register the new connection
368 to be polled by the default main loop, if any
369 `private` : bool
370 If true, never return an existing shared instance, but instead
371 return a private connection.
373 return Bus.__new__(cls, Bus.TYPE_STARTER, use_default_mainloop, private)
375 class Interface:
376 """An interface into a remote object.
378 An Interface can be used to wrap ProxyObjects
379 so that calls can be routed to their correct
380 dbus interface
383 def __init__(self, object, dbus_interface):
384 """Construct a proxy for the given interface on the given object.
386 :Parameters:
387 `object` : `dbus.proxies.ProxyObject`
388 The remote object
389 `dbus_interface` : str
390 An interface the `object` implements
392 self._obj = object
393 self._dbus_interface = dbus_interface
395 def connect_to_signal(self, signal_name, handler_function, dbus_interface = None, **keywords):
396 """Arrange for a function to be called when the given signal is
397 emitted.
399 :Parameters:
400 `signal_name` : str
401 The name of the signal
402 `handler_function` : callable
403 A function to be called (FIXME arguments?) when the signal
404 is emitted by the remote object.
405 `dbus_interface` : str
406 Optional interface with which to qualify the signal name.
407 The default is to use the interface this Interface represents.
408 `sender_keyword` : str
409 If not None (the default), the handler function will receive
410 the unique name of the sending endpoint as a keyword
411 argument with this name
412 `path_keyword` : str
413 If not None (the default), the handler function will receive
414 the object-path of the sending object as a keyword argument
415 with this name
416 `keywords`
417 If there are additional keyword parameters of the form
418 ``arg``\ *n*, match only signals where the *n*\ th argument
419 is the value given for that keyword parameter
421 if not dbus_interface:
422 dbus_interface = self._dbus_interface
424 self._obj.connect_to_signal(signal_name, handler_function, dbus_interface, **keywords)
426 def __getattr__(self, member, **keywords):
427 # FIXME: this syntax is bizarre.
428 if (keywords.has_key('dbus_interface')):
429 _dbus_interface = keywords['dbus_interface']
430 else:
431 _dbus_interface = self._dbus_interface
433 if member == '__call__':
434 return object.__call__
435 else:
436 ret = self._obj.__getattr__(member, dbus_interface=_dbus_interface)
437 return ret
439 def __repr__(self):
440 return '<Interface %r implementing %r at %#x>'%(
441 self._obj, self._dbus_interface, id(self))
442 __str__ = __repr__