dbus/service.py, dbus/_dbus_bindings-types.pxi: Move VariantSignature
[dbus-python-phuang.git] / dbus / _dbus.py
blobf82d643ca263a071c31ec99ee038e64afd53bba7
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 # From exceptions (some originally from _dbus_bindings)
45 'DBusException', 'ConnectionError', 'MissingErrorHandlerException',
46 'MissingReplyHandlerException', 'ValidationException',
47 'IntrospectionParserException', 'UnknownMethodException',
48 'NameExistsException',
49 # proxies, matchrules are not public API, so exclude them
51 __docformat__ = 'reStructuredText'
53 import dbus
54 import _dbus_bindings
55 import weakref
57 from proxies import *
58 from exceptions import *
59 from matchrules import *
61 class Bus(object):
62 """A connection to a DBus daemon.
64 One of three possible standard buses, the SESSION, SYSTEM,
65 or STARTER bus
66 """
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]
72 """
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`.
87 :Parameters:
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
93 `private` : bool
94 If true, never return an existing shared instance, but instead
95 return a private connection
96 """
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:
108 subclass = SystemBus
109 elif bus_type == cls.TYPE_STARTER:
110 subclass = StarterBus
111 else:
112 raise ValueError('invalid bus_type %s' % bus_type)
114 bus = object.__new__(subclass)
116 bus._bus_type = bus_type
117 bus._bus_names = weakref.WeakValueDictionary()
118 bus._match_rule_tree = SignalMatchTree()
120 # FIXME: if you get a starter and a system/session bus connection
121 # in the same process, it's the same underlying connection that
122 # is returned by bus_get, but we initialise it twice
123 bus._connection = _dbus_bindings.bus_get(bus_type, private)
124 bus._connection.add_filter(bus._signal_func)
126 if use_default_mainloop:
127 func = getattr(dbus, "_dbus_mainloop_setup_function", None)
128 if func:
129 func(bus)
131 if not private:
132 cls._shared_instances[bus_type] = bus
134 return bus
136 def __init__(self, *args, **keywords):
137 # do nothing here because this can get called multiple times on the
138 # same object if __new__ returns a shared instance
139 pass
141 def close(self):
142 """Close the connection."""
143 self._connection.close()
145 def get_connection(self):
146 """Return the underlying `_dbus_bindings.Connection`."""
147 return self._connection
149 def get_session(private=False):
150 """Static method that returns a connection to the session bus.
152 :Parameters:
153 `private` : bool
154 If true, do not return a shared connection.
156 return SessionBus(private=private)
158 get_session = staticmethod(get_session)
160 def get_system(private=False):
161 """Static method that returns a connection to the system bus.
163 :Parameters:
164 `private` : bool
165 If true, do not return a shared connection.
167 return SystemBus(private=private)
169 get_system = staticmethod(get_system)
172 def get_starter(private=False):
173 """Static method that returns a connection to the starter bus.
175 :Parameters:
176 `private` : bool
177 If true, do not return a shared connection.
179 return StarterBus(private=private)
181 get_starter = staticmethod(get_starter)
184 def get_object(self, named_service, object_path):
185 """Return a local proxy for the given remote object.
187 Method calls on the proxy are translated into method calls on the
188 remote object.
190 :Parameters:
191 `named_service` : str
192 A bus name (either the unique name or a well-known name)
193 of the application owning the object
194 `object_path` : str
195 The object path of the desired object
196 :Returns: a `dbus.proxies.ProxyObject`
198 return self.ProxyObjectClass(self, named_service, object_path)
200 def _create_args_dict(self, keywords):
201 args_dict = None
202 for (key, value) in keywords.iteritems():
203 if key.startswith('arg'):
204 try:
205 snum = key[3:]
206 num = int(snum)
208 if not args_dict:
209 args_dict = {}
211 args_dict[num] = value
212 except ValueError:
213 raise TypeError("Invalid arg index %s"%snum)
214 elif key in ("sender_keyword", "path_keyword"):
215 pass
216 else:
217 raise TypeError("Unknown keyword %s"%(key))
219 return args_dict
221 def add_signal_receiver(self, handler_function,
222 signal_name=None,
223 dbus_interface=None,
224 named_service=None,
225 path=None,
226 **keywords):
227 """Arrange for the given function to be called when a signal matching
228 the parameters is emitted.
230 :Parameters:
231 `handler_function` : callable
232 The function to be called.
233 `signal_name` : str
234 The signal name; None (the default) matches all names
235 `dbus_interface` : str
236 The D-Bus interface name with which to qualify the signal;
237 None (the default) matches all interface names
238 `named_service` : str
239 A bus name for the sender, which will be resolved to a
240 unique name if it is not already; None (the default) matches
241 any sender
242 `path` : str
243 The object path of the object which must have emitted the
244 signal; None (the default) matches any object path
245 `sender_keyword` : str
246 If not None (the default), the handler function will receive
247 the unique name of the sending endpoint as a keyword
248 argument with this name
249 `path_keyword` : str
250 If not None (the default), the handler function will receive
251 the object-path of the sending object as a keyword argument
252 with this name
253 `keywords`
254 If there are additional keyword parameters of the form
255 ``arg``\ *n*, match only signals where the *n*\ th argument
256 is the value given for that keyword parameter
259 args_dict = self._create_args_dict(keywords)
261 if (named_service and named_service[0] != ':'):
262 bus_object = self.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
263 named_service = bus_object.GetNameOwner(named_service, dbus_interface='org.freedesktop.DBus')
265 match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
267 for kw in ("sender_keyword", "path_keyword"):
268 if kw in keywords:
269 setattr(match_rule, kw, keywords[kw])
270 else:
271 setattr(match_rule, kw, None)
273 if args_dict:
274 match_rule.add_args_match(args_dict)
276 match_rule.add_handler(handler_function)
278 self._match_rule_tree.add(match_rule)
280 _dbus_bindings.bus_add_match(self._connection, repr(match_rule))
282 def remove_signal_receiver(self, handler_function,
283 signal_name=None,
284 dbus_interface=None,
285 named_service=None,
286 path=None,
287 **keywords):
289 args_dict = self._create_args_dict(keywords)
291 if (named_service and named_service[0] != ':'):
292 bus_object = self.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
293 named_service = bus_object.GetNameOwner(named_service, dbus_interface='org.freedesktop.DBus')
295 match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
297 if (args_dict):
298 match_rule.add_args_match(args_dict)
300 if (handler_function):
301 match_rule.add_handler(handler_function)
303 self._match_rule_tree.remove(match_rule)
305 #TODO we leak match rules in the lower level bindings. We need to ref count them
307 def get_unix_user(self, named_service):
308 """Get the numeric uid of the process which owns the given bus name
309 on the connected bus daemon.
311 :Parameters:
312 `named_service` : str
313 A bus name (may be either a unique name or a well-known name)
315 return _dbus_bindings.bus_get_unix_user(self._connection, named_service)
317 def _signal_func(self, connection, message):
318 if (message.get_type() != _dbus_bindings.MESSAGE_TYPE_SIGNAL):
319 return _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
321 dbus_interface = message.get_interface()
322 named_service = message.get_sender()
323 path = message.get_path()
324 signal_name = message.get_member()
326 match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
328 self._match_rule_tree.exec_matches(match_rule, message)
330 def start_service_by_name(self, named_service):
331 """Start a service which will implement the given bus name on this
332 Bus.
334 :Parameters:
335 `named_service` : str
336 The well-known bus name for which an implementation is required
338 :Returns: A tuple of 2 elements. The first is always True, the second is
339 either START_REPLY_SUCCESS or START_REPLY_ALREADY_RUNNING.
341 :Raises DBusException: if the service could not be started.
343 return _dbus_bindings.bus_start_service_by_name(self._connection, named_service)
345 def __repr__(self):
346 if self._bus_type == self.TYPE_SESSION:
347 name = 'SESSION'
348 elif self._bus_type == self.TYPE_SYSTEM:
349 name = 'SYSTEM'
350 elif self._bus_type == self.TYPE_STARTER:
351 name = 'STARTER'
352 else:
353 assert False, 'Unable to represent unknown bus type.'
355 return '<dbus.Bus on %s at %#x>' % (name, id(self))
356 __str__ = __repr__
359 # FIXME: Drop the subclasses here? I can't think why we'd ever want
360 # polymorphism
361 class SystemBus(Bus):
362 """The system-wide message bus."""
363 def __new__(cls, use_default_mainloop=True, private=False):
364 """Return a connection to the system bus.
366 :Parameters:
367 `use_default_mainloop` : bool
368 If true (default), automatically register the new connection
369 to be polled by the default main loop, if any
370 `private` : bool
371 If true, never return an existing shared instance, but instead
372 return a private connection.
374 return Bus.__new__(cls, Bus.TYPE_SYSTEM, use_default_mainloop, private)
376 class SessionBus(Bus):
377 """The session (current login) message bus."""
378 def __new__(cls, use_default_mainloop=True, private=False):
379 """Return a connection to the session bus.
381 :Parameters:
382 `use_default_mainloop` : bool
383 If true (default), automatically register the new connection
384 to be polled by the default main loop, if any
385 `private` : bool
386 If true, never return an existing shared instance, but instead
387 return a private connection.
389 return Bus.__new__(cls, Bus.TYPE_SESSION, use_default_mainloop, private)
391 class StarterBus(Bus):
392 """The bus that activated this process (only valid if
393 this process was launched by DBus activation).
395 def __new__(cls, use_default_mainloop=True, private=False):
396 """Return a connection to the bus that activated this process.
398 :Parameters:
399 `use_default_mainloop` : bool
400 If true (default), automatically register the new connection
401 to be polled by the default main loop, if any
402 `private` : bool
403 If true, never return an existing shared instance, but instead
404 return a private connection.
406 return Bus.__new__(cls, Bus.TYPE_STARTER, use_default_mainloop, private)
408 class Interface:
409 """An interface into a remote object.
411 An Interface can be used to wrap ProxyObjects
412 so that calls can be routed to their correct
413 dbus interface
416 def __init__(self, object, dbus_interface):
417 """Construct a proxy for the given interface on the given object.
419 :Parameters:
420 `object` : `dbus.proxies.ProxyObject`
421 The remote object
422 `dbus_interface` : str
423 An interface the `object` implements
425 self._obj = object
426 self._dbus_interface = dbus_interface
428 def connect_to_signal(self, signal_name, handler_function, dbus_interface = None, **keywords):
429 """Arrange for a function to be called when the given signal is
430 emitted.
432 :Parameters:
433 `signal_name` : str
434 The name of the signal
435 `handler_function` : callable
436 A function to be called (FIXME arguments?) when the signal
437 is emitted by the remote object.
438 `dbus_interface` : str
439 Optional interface with which to qualify the signal name.
440 The default is to use the interface this Interface represents.
441 `sender_keyword` : str
442 If not None (the default), the handler function will receive
443 the unique name of the sending endpoint as a keyword
444 argument with this name
445 `path_keyword` : str
446 If not None (the default), the handler function will receive
447 the object-path of the sending object as a keyword argument
448 with this name
449 `keywords`
450 If there are additional keyword parameters of the form
451 ``arg``\ *n*, match only signals where the *n*\ th argument
452 is the value given for that keyword parameter
454 if not dbus_interface:
455 dbus_interface = self._dbus_interface
457 self._obj.connect_to_signal(signal_name, handler_function, dbus_interface, **keywords)
459 def __getattr__(self, member, **keywords):
460 # FIXME: this syntax is bizarre.
461 if (keywords.has_key('dbus_interface')):
462 _dbus_interface = keywords['dbus_interface']
463 else:
464 _dbus_interface = self._dbus_interface
466 if member == '__call__':
467 return object.__call__
468 else:
469 ret = self._obj.__getattr__(member, dbus_interface=_dbus_interface)
470 return ret
472 def __repr__(self):
473 return '<Interface %r implementing %r at %#x>'%(
474 self._obj, self._dbus_interface, id(self))
475 __str__ = __repr__