_dbus_bindings: Expose name-validation functions to Python code.
[dbus-python-phuang.git] / dbus / _dbus.py
blob9004fb73f7a37db67bc82b3f0662a96a4fd49738
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 (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'
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(_dbus_bindings._Bus):
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 = _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.__class__._signal_func)
122 if use_default_mainloop:
123 func = getattr(dbus, "_dbus_mainloop_setup_function", None)
124 if func:
125 func(bus)
127 if not private:
128 cls._shared_instances[bus_type] = bus
130 return 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
135 pass
137 def get_connection(self):
138 return self
140 _connection = property(get_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 """Return a local proxy for the given remote object.
180 Method calls on the proxy are translated into method calls on the
181 remote object.
183 :Parameters:
184 `named_service` : str
185 A bus name (either the unique name or a well-known name)
186 of the application owning the object
187 `object_path` : str
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):
194 args_dict = None
195 for (key, value) in keywords.iteritems():
196 if key.startswith('arg'):
197 try:
198 snum = key[3:]
199 num = int(snum)
201 if not args_dict:
202 args_dict = {}
204 args_dict[num] = value
205 except ValueError:
206 raise TypeError("Invalid arg index %s"%snum)
207 elif key in ("sender_keyword", "path_keyword"):
208 pass
209 else:
210 raise TypeError("Unknown keyword %s"%(key))
212 return args_dict
214 def add_signal_receiver(self, handler_function,
215 signal_name=None,
216 dbus_interface=None,
217 named_service=None,
218 path=None,
219 **keywords):
220 """Arrange for the given function to be called when a signal matching
221 the parameters is emitted.
223 :Parameters:
224 `handler_function` : callable
225 The function to be called.
226 `signal_name` : str
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
234 any sender
235 `path` : str
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
242 `path_keyword` : str
243 If not None (the default), the handler function will receive
244 the object-path of the sending object as a keyword argument
245 with this name
246 `keywords`
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"):
261 if kw in keywords:
262 setattr(match_rule, kw, keywords[kw])
263 else:
264 setattr(match_rule, kw, None)
266 if args_dict:
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,
276 signal_name=None,
277 dbus_interface=None,
278 named_service=None,
279 path=None,
280 **keywords):
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)
290 if (args_dict):
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, 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)
317 def __repr__(self):
318 if self._bus_type == self.TYPE_SESSION:
319 name = 'SESSION'
320 elif self._bus_type == self.TYPE_SYSTEM:
321 name = 'SYSTEM'
322 elif self._bus_type == self.TYPE_STARTER:
323 name = 'STARTER'
324 else:
325 assert False, 'Unable to represent unknown bus type.'
327 return '<dbus.Bus on %s at %#x>' % (name, id(self))
328 __str__ = __repr__
331 # FIXME: Drop the subclasses here? I can't think why we'd ever want
332 # polymorphism
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.
338 :Parameters:
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
342 `private` : bool
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.
353 :Parameters:
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
357 `private` : bool
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.
370 :Parameters:
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
374 `private` : bool
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)
380 class Interface:
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
385 dbus interface
388 def __init__(self, object, dbus_interface):
389 """Construct a proxy for the given interface on the given object.
391 :Parameters:
392 `object` : `dbus.proxies.ProxyObject`
393 The remote object
394 `dbus_interface` : str
395 An interface the `object` implements
397 self._obj = object
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
402 emitted.
404 :Parameters:
405 `signal_name` : str
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
417 `path_keyword` : str
418 If not None (the default), the handler function will receive
419 the object-path of the sending object as a keyword argument
420 with this name
421 `keywords`
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']
435 else:
436 _dbus_interface = self._dbus_interface
438 if member == '__call__':
439 return object.__call__
440 else:
441 ret = self._obj.__getattr__(member, dbus_interface=_dbus_interface)
442 return ret
444 def __repr__(self):
445 return '<Interface %r implementing %r at %#x>'%(
446 self._obj, self._dbus_interface, id(self))
447 __str__ = __repr__