dbus/__init__.py: Remove pseudo-tutorial from docstring, we have a tutorial now
[dbus-python-phuang.git] / dbus / _dbus.py
blob6b1ac1649e91e3f793c65dba22a0cf063f39f39f
1 """Implementation for dbus.Bus. Not to be imported directly."""
3 # Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
4 # Copyright (C) 2003 David Zeuthen
5 # Copyright (C) 2004 Rob Taylor
6 # Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
8 # Licensed under the Academic Free License version 2.1
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 from __future__ import generators
26 __all__ = ('Bus', 'SystemBus', 'SessionBus', 'StarterBus', 'Interface')
27 __docformat__ = 'reStructuredText'
29 import _dbus_bindings
30 UTF8String = _dbus_bindings.UTF8String
31 DBusException = _dbus_bindings.DBusException
32 BusImplementation = _dbus_bindings.BusImplementation
34 import os
35 import logging
36 import sys
37 import weakref
38 from traceback import print_exc
40 from dbus.proxies import ProxyObject, BUS_DAEMON_NAME, BUS_DAEMON_PATH, \
41 BUS_DAEMON_IFACE
43 try:
44 import thread
45 except ImportError:
46 import dummy_thread as thread
48 logger = logging.getLogger('dbus._dbus')
49 BUS_DAEMON_NAME = 'org.freedesktop.DBus'
50 BUS_DAEMON_PATH = '/org/freedesktop/DBus'
51 BUS_DAEMON_IFACE = BUS_DAEMON_NAME
53 _NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s',"
54 "interface='%s',member='NameOwnerChanged',"
55 "path='%s',arg0='%%s'"
56 % (BUS_DAEMON_NAME, BUS_DAEMON_IFACE,
57 BUS_DAEMON_PATH))
58 """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
59 messages"""
62 class SignalMatch(object):
63 __slots__ = ('sender_unique', '_member', '_interface', '_sender',
64 '_path', '_handler', '_args_match', '_rule',
65 '_utf8_strings', '_byte_arrays', '_conn_weakref',
66 '_destination_keyword', '_interface_keyword',
67 '_message_keyword', '_member_keyword',
68 '_sender_keyword', '_path_keyword', '_int_args_match')
70 def __init__(self, conn, sender, object_path, dbus_interface,
71 member, handler, utf8_strings=False, byte_arrays=False,
72 sender_keyword=None, path_keyword=None,
73 interface_keyword=None, member_keyword=None,
74 message_keyword=None, destination_keyword=None,
75 **kwargs):
76 if member is not None:
77 _dbus_bindings.validate_member_name(member)
78 if dbus_interface is not None:
79 _dbus_bindings.validate_interface_name(dbus_interface)
80 if sender is not None:
81 _dbus_bindings.validate_bus_name(sender)
82 if object_path is not None:
83 _dbus_bindings.validate_object_path(object_path)
85 self._conn_weakref = weakref.ref(conn)
86 self._sender = sender
87 self._interface = dbus_interface
88 self._member = member
89 self._path = object_path
90 self._handler = handler
91 if (sender is not None and sender[:1] != ':'
92 and sender != BUS_DAEMON_NAME):
93 self.sender_unique = conn.get_object(BUS_DAEMON_NAME, BUS_DAEMON_PATH).GetNameOwner(sender, dbus_interface=BUS_DAEMON_IFACE)
94 else:
95 self.sender_unique = sender
96 self._utf8_strings = utf8_strings
97 self._byte_arrays = byte_arrays
98 self._sender_keyword = sender_keyword
99 self._path_keyword = path_keyword
100 self._member_keyword = member_keyword
101 self._interface_keyword = interface_keyword
102 self._message_keyword = message_keyword
103 self._destination_keyword = destination_keyword
105 self._args_match = kwargs
106 if not kwargs:
107 self._int_args_match = None
108 else:
109 self._int_args_match = {}
110 for kwarg in kwargs:
111 if not kwarg.startswith('arg'):
112 raise TypeError('SignalMatch: unknown keyword argument %s'
113 % kwarg)
114 try:
115 index = int(kwarg[3:])
116 except ValueError:
117 raise TypeError('SignalMatch: unknown keyword argument %s'
118 % kwarg)
119 if index < 0 or index > 63:
120 raise TypeError('SignalMatch: arg match index must be in '
121 'range(64), not %d' % index)
122 self._int_args_match[index] = kwargs[kwarg]
124 # we're always going to have to calculate the match rule for
125 # the Bus's benefit, so this constructor might as well do the work
126 rule = ["type='signal'"]
127 if self._sender is not None:
128 rule.append("sender='%s'" % self._sender)
129 if self._path is not None:
130 rule.append("path='%s'" % self._path)
131 if self._interface is not None:
132 rule.append("interface='%s'" % self._interface)
133 if self._member is not None:
134 rule.append("member='%s'" % self._member)
135 for kwarg, value in kwargs.iteritems():
136 rule.append("%s='%s'" % (kwarg, value))
138 self._rule = ','.join(rule)
140 def __str__(self):
141 return self._rule
143 def __repr__(self):
144 return ('<%s at %x "%s" on conn %r>'
145 % (self.__class__, id(self), self._rule, self._conn_weakref()))
147 def matches_removal_spec(self, sender, object_path,
148 dbus_interface, member, handler, **kwargs):
149 if handler not in (None, self._handler):
150 return False
151 if sender != self._sender:
152 return False
153 if object_path != self._path:
154 return False
155 if dbus_interface != self._interface:
156 return False
157 if member != self._member:
158 return False
159 if kwargs != self._args_match:
160 return False
161 return True
163 def maybe_handle_message(self, message):
164 args = None
166 # these haven't been checked yet by the match tree
167 if self.sender_unique not in (None, message.get_sender()):
168 return False
169 if self._int_args_match is not None:
170 # extracting args with utf8_strings and byte_arrays is less work
171 args = message.get_args_list(utf8_strings=True, byte_arrays=True)
172 for index, value in self._int_args_match.iteritems():
173 if (index >= len(args)
174 or not isinstance(args[index], UTF8String)
175 or args[index] != value):
176 return False
178 # these have likely already been checked by the match tree
179 if self._member not in (None, message.get_member()):
180 return False
181 if self._interface not in (None, message.get_interface()):
182 return False
183 if self._path not in (None, message.get_path()):
184 return False
186 try:
187 # minor optimization: if we already extracted the args with the
188 # right calling convention to do the args match, don't bother
189 # doing so again
190 if args is None or not self._utf8_strings or not self._byte_arrays:
191 args = message.get_args_list(utf8_strings=self._utf8_strings,
192 byte_arrays=self._byte_arrays)
193 kwargs = {}
194 if self._sender_keyword is not None:
195 kwargs[self._sender_keyword] = message.get_sender()
196 if self._destination_keyword is not None:
197 kwargs[self._destination_keyword] = message.get_destination()
198 if self._path_keyword is not None:
199 kwargs[self._path_keyword] = message.get_path()
200 if self._member_keyword is not None:
201 kwargs[self._member_keyword] = message.get_member()
202 if self._interface_keyword is not None:
203 kwargs[self._interface_keyword] = message.get_interface()
204 if self._message_keyword is not None:
205 kwargs[self._message_keyword] = message
206 self._handler(*args, **kwargs)
207 except:
208 # FIXME: need to decide whether dbus-python uses logging, or
209 # stderr, or what, and make it consistent
210 sys.stderr.write('Exception in handler for D-Bus signal:\n')
211 print_exc()
213 return True
215 def remove(self):
216 #logger.debug('%r: removing', self)
217 conn = self._conn_weakref()
218 # do nothing if the connection has already vanished
219 if conn is not None:
220 #logger.debug('%r: removing from connection %r', self, conn)
221 conn.remove_signal_receiver(self, self._member,
222 self._interface, self._sender,
223 self._path,
224 **self._args_match)
227 class Bus(BusImplementation):
228 """A connection to a DBus daemon.
230 One of three possible standard buses, the SESSION, SYSTEM,
231 or STARTER bus
234 TYPE_SESSION = _dbus_bindings.BUS_SESSION
235 """Represents a session bus (same as the global dbus.BUS_SESSION)"""
237 TYPE_SYSTEM = _dbus_bindings.BUS_SYSTEM
238 """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
240 TYPE_STARTER = _dbus_bindings.BUS_STARTER
241 """Represents the bus that started this service by activation (same as
242 the global dbus.BUS_STARTER)"""
244 ProxyObjectClass = ProxyObject
246 START_REPLY_SUCCESS = _dbus_bindings.DBUS_START_REPLY_SUCCESS
247 START_REPLY_ALREADY_RUNNING = _dbus_bindings.DBUS_START_REPLY_ALREADY_RUNNING
249 _shared_instances = {}
251 def __new__(cls, bus_type=TYPE_SESSION, private=False, mainloop=None):
252 """Constructor, returning an existing instance where appropriate.
254 The returned instance is actually always an instance of `SessionBus`,
255 `SystemBus` or `StarterBus`.
257 :Parameters:
258 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
259 Connect to the appropriate bus
260 `private` : bool
261 If true, never return an existing shared instance, but instead
262 return a private connection
263 `mainloop` : dbus.mainloop.NativeMainLoop
264 The main loop to use. The default is to use the default
265 main loop if one has been set up, or raise an exception
266 if none has been.
267 :ToDo:
268 - There is currently no way to connect this class to a custom
269 address.
270 - Some of this functionality should be available on
271 peer-to-peer D-Bus connections too.
272 :Changed: in dbus-python 0.80:
273 converted from a wrapper around a Connection to a Connection
274 subclass.
276 if (not private and bus_type in cls._shared_instances):
277 return cls._shared_instances[bus_type]
279 # this is a bit odd, but we create instances of the subtypes
280 # so we can return the shared instances if someone tries to
281 # construct one of them (otherwise we'd eg try and return an
282 # instance of Bus from __new__ in SessionBus). why are there
283 # three ways to construct this class? we just don't know.
284 if bus_type == cls.TYPE_SESSION:
285 subclass = SessionBus
286 elif bus_type == cls.TYPE_SYSTEM:
287 subclass = SystemBus
288 elif bus_type == cls.TYPE_STARTER:
289 subclass = StarterBus
290 else:
291 raise ValueError('invalid bus_type %s' % bus_type)
293 bus = _dbus_bindings.BusImplementation.__new__(subclass, bus_type,
294 mainloop=mainloop)
296 bus._bus_type = bus_type
297 # _bus_names is used by dbus.service.BusName!
298 bus._bus_names = weakref.WeakValueDictionary()
300 bus._signal_recipients_by_object_path = {}
301 """Map from object path to dict mapping dbus_interface to dict
302 mapping member to list of SignalMatch objects."""
304 bus._signal_sender_matches = {}
305 """Map from sender well-known name to list of match rules for all
306 signal handlers that match on sender well-known name."""
308 bus._signals_lock = thread.allocate_lock()
309 """Lock used to protect signal data structures if doing two
310 removals at the same time (everything else is atomic, thanks to
311 the GIL)"""
313 bus.add_message_filter(bus.__class__._signal_func)
315 if not private:
316 cls._shared_instances[bus_type] = bus
318 return bus
320 def close(self):
321 t = self._bus_type
322 if self.__class__._shared_instances[t] is self:
323 del self.__class__._shared_instances[t]
324 BusImplementation.close(self)
326 def get_connection(self):
327 """(Deprecated - in new code, just use self)
329 Return self, for backwards compatibility with earlier dbus-python
330 versions where Bus was not a subclass of Connection.
332 return self
333 _connection = property(get_connection, None, None,
334 """self._connection == self, for backwards
335 compatibility with earlier dbus-python versions
336 where Bus was not a subclass of Connection.""")
338 def get_session(private=False):
339 """Static method that returns a connection to the session bus.
341 :Parameters:
342 `private` : bool
343 If true, do not return a shared connection.
345 return SessionBus(private=private)
347 get_session = staticmethod(get_session)
349 def get_system(private=False):
350 """Static method that returns a connection to the system bus.
352 :Parameters:
353 `private` : bool
354 If true, do not return a shared connection.
356 return SystemBus(private=private)
358 get_system = staticmethod(get_system)
361 def get_starter(private=False):
362 """Static method that returns a connection to the starter bus.
364 :Parameters:
365 `private` : bool
366 If true, do not return a shared connection.
368 return StarterBus(private=private)
370 get_starter = staticmethod(get_starter)
372 def get_object(self, named_service, object_path, introspect=True,
373 follow_name_owner_changes=False):
374 """Return a local proxy for the given remote object.
376 Method calls on the proxy are translated into method calls on the
377 remote object.
379 :Parameters:
380 `named_service` : str
381 A bus name (either the unique name or a well-known name)
382 of the application owning the object
383 `object_path` : str
384 The object path of the desired object
385 `introspect` : bool
386 If true (default), attempt to introspect the remote
387 object to find out supported methods and their signatures
388 `follow_name_owner_changes` : bool
389 If the object path is a well-known name and this parameter
390 is false (default), resolve the well-known name to the unique
391 name of its current owner and bind to that instead; if the
392 ownership of the well-known name changes in future,
393 keep communicating with the original owner.
394 This is necessary if the D-Bus API used is stateful.
396 If the object path is a well-known name and this parameter
397 is true, whenever the well-known name changes ownership in
398 future, bind to the new owner, if any.
400 If the given object path is a unique name, this parameter
401 has no effect.
403 :Returns: a `dbus.proxies.ProxyObject`
404 :Raises `DBusException`: if resolving the well-known name to a
405 unique name fails
407 if follow_name_owner_changes:
408 self._require_main_loop() # we don't get the signals otherwise
409 return self.ProxyObjectClass(self, named_service, object_path,
410 introspect=introspect,
411 follow_name_owner_changes=follow_name_owner_changes)
413 def add_signal_receiver(self, handler_function,
414 signal_name=None,
415 dbus_interface=None,
416 named_service=None,
417 path=None,
418 **keywords):
419 """Arrange for the given function to be called when a signal matching
420 the parameters is received.
422 :Parameters:
423 `handler_function` : callable
424 The function to be called. Its positional arguments will
425 be the arguments of the signal. By default it will receive
426 no keyword arguments, but see the description of
427 the optional keyword arguments below.
428 `signal_name` : str
429 The signal name; None (the default) matches all names
430 `dbus_interface` : str
431 The D-Bus interface name with which to qualify the signal;
432 None (the default) matches all interface names
433 `named_service` : str
434 A bus name for the sender, which will be resolved to a
435 unique name if it is not already; None (the default) matches
436 any sender
437 `path` : str
438 The object path of the object which must have emitted the
439 signal; None (the default) matches any object path
440 :Keywords:
441 `utf8_strings` : bool
442 If True, the handler function will receive any string
443 arguments as dbus.UTF8String objects (a subclass of str
444 guaranteed to be UTF-8). If False (default) it will receive
445 any string arguments as dbus.String objects (a subclass of
446 unicode).
447 `byte_arrays` : bool
448 If True, the handler function will receive any byte-array
449 arguments as dbus.ByteArray objects (a subclass of str).
450 If False (default) it will receive any byte-array
451 arguments as a dbus.Array of dbus.Byte (subclasses of:
452 a list of ints).
453 `sender_keyword` : str
454 If not None (the default), the handler function will receive
455 the unique name of the sending endpoint as a keyword
456 argument with this name.
457 `destination_keyword` : str
458 If not None (the default), the handler function will receive
459 the bus name of the destination (or None if the signal is a
460 broadcast, as is usual) as a keyword argument with this name.
461 `interface_keyword` : str
462 If not None (the default), the handler function will receive
463 the signal interface as a keyword argument with this name.
464 `member_keyword` : str
465 If not None (the default), the handler function will receive
466 the signal name as a keyword argument with this name.
467 `path_keyword` : str
468 If not None (the default), the handler function will receive
469 the object-path of the sending object as a keyword argument
470 with this name.
471 `message_keyword` : str
472 If not None (the default), the handler function will receive
473 the `dbus.lowlevel.SignalMessage` as a keyword argument with
474 this name.
475 `arg...` : unicode or UTF-8 str
476 If there are additional keyword parameters of the form
477 ``arg``\ *n*, match only signals where the *n*\ th argument
478 is the value given for that keyword parameter. As of this
479 time only string arguments can be matched (in particular,
480 object paths and signatures can't).
482 self._require_main_loop()
484 match = SignalMatch(self, named_service, path, dbus_interface,
485 signal_name, handler_function, **keywords)
486 by_interface = self._signal_recipients_by_object_path.setdefault(path,
488 by_member = by_interface.setdefault(dbus_interface, {})
489 matches = by_member.setdefault(signal_name, [])
490 # The bus daemon is special - its unique-name is org.freedesktop.DBus
491 # rather than starting with :
492 if (named_service is not None and named_service[:1] != ':'
493 and named_service != BUS_DAEMON_NAME):
494 notification = self._signal_sender_matches.setdefault(named_service,
496 if not notification:
497 self.add_match_string(_NAME_OWNER_CHANGE_MATCH % named_service)
498 notification.append(match)
499 # make sure nobody is currently manipulating the list
500 self._signals_lock.acquire()
501 try:
502 matches.append(match)
503 finally:
504 self._signals_lock.release()
505 self.add_match_string(str(match))
506 return match
508 def _iter_easy_matches(self, path, dbus_interface, member):
509 if path is not None:
510 path_keys = (None, path)
511 else:
512 path_keys = (None,)
513 if dbus_interface is not None:
514 interface_keys = (None, dbus_interface)
515 else:
516 interface_keys = (None,)
517 if member is not None:
518 member_keys = (None, member)
519 else:
520 member_keys = (None,)
522 for path in path_keys:
523 by_interface = self._signal_recipients_by_object_path.get(path,
524 None)
525 if by_interface is None:
526 continue
527 for dbus_interface in interface_keys:
528 by_member = by_interface.get(dbus_interface, None)
529 if by_member is None:
530 continue
531 for member in member_keys:
532 matches = by_member.get(member, None)
533 if matches is None:
534 continue
535 for m in matches:
536 yield m
538 def _remove_name_owner_changed_for_match(self, named_service, match):
539 notification = self._signal_sender_matches.get(named_service, False)
540 if notification:
541 try:
542 notification.remove(match)
543 except LookupError:
544 pass
545 if not notification:
546 self.remove_match_string(_NAME_OWNER_CHANGE_MATCH
547 % named_service)
549 def remove_signal_receiver(self, handler_or_match,
550 signal_name=None,
551 dbus_interface=None,
552 named_service=None,
553 path=None,
554 **keywords):
555 #logger.debug('%r: removing signal receiver %r: member=%s, '
556 #'iface=%s, sender=%s, path=%s, kwargs=%r',
557 #self, handler_or_match, signal_name,
558 #dbus_interface, named_service, path, keywords)
559 #logger.debug('%r', self._signal_recipients_by_object_path)
560 by_interface = self._signal_recipients_by_object_path.get(path, None)
561 if by_interface is None:
562 return
563 by_member = by_interface.get(dbus_interface, None)
564 if by_member is None:
565 return
566 matches = by_member.get(signal_name, None)
567 if matches is None:
568 return
569 self._signals_lock.acquire()
570 #logger.debug(matches)
571 try:
572 new = []
573 for match in matches:
574 if (handler_or_match is match
575 or match.matches_removal_spec(named_service,
576 path,
577 dbus_interface,
578 signal_name,
579 handler_or_match,
580 **keywords)):
581 #logger.debug('Removing match string: %s', match)
582 self.remove_match_string(str(match))
583 self._remove_name_owner_changed_for_match(named_service,
584 match)
585 else:
586 new.append(match)
587 by_member[signal_name] = new
588 finally:
589 self._signals_lock.release()
591 def _signal_func(self, message):
592 """D-Bus filter function. Handle signals by dispatching to Python
593 callbacks kept in the match-rule tree.
596 #logger.debug('Incoming message %r with args %r', message,
597 #message.get_args_list())
599 if (message.get_type() != _dbus_bindings.MESSAGE_TYPE_SIGNAL):
600 return _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
602 # If it's NameOwnerChanged, we'll need to update our
603 # sender well-known name -> sender unique name mappings
604 if (message.is_signal(BUS_DAEMON_NAME, 'NameOwnerChanged')
605 and message.has_path(BUS_DAEMON_PATH)):
606 name, unused, new = message.get_args_list()
607 for match in self._signal_sender_matches.get(name, (None,))[1:]:
608 match.sender_unique = new
610 # See if anyone else wants to know
611 dbus_interface = message.get_interface()
612 path = message.get_path()
613 signal_name = message.get_member()
615 ret = _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
616 for match in self._iter_easy_matches(path, dbus_interface,
617 signal_name):
618 if match.maybe_handle_message(message):
619 ret = _dbus_bindings.HANDLER_RESULT_HANDLED
620 return ret
622 def __repr__(self):
623 if self._bus_type == self.TYPE_SESSION:
624 name = 'SESSION'
625 elif self._bus_type == self.TYPE_SYSTEM:
626 name = 'SYSTEM'
627 elif self._bus_type == self.TYPE_STARTER:
628 name = 'STARTER'
629 else:
630 raise AssertionError('Unable to represent unknown bus type.')
632 return '<dbus.Bus on %s at %#x>' % (name, id(self))
633 __str__ = __repr__
636 # FIXME: Drop the subclasses here? I can't think why we'd ever want
637 # polymorphism
638 class SystemBus(Bus):
639 """The system-wide message bus."""
640 def __new__(cls, private=False, mainloop=None):
641 """Return a connection to the system bus.
643 :Parameters:
644 `private` : bool
645 If true, never return an existing shared instance, but instead
646 return a private connection.
647 `mainloop` : dbus.mainloop.NativeMainLoop
648 The main loop to use. The default is to use the default
649 main loop if one has been set up, or raise an exception
650 if none has been.
652 return Bus.__new__(cls, Bus.TYPE_SYSTEM, mainloop=mainloop,
653 private=private)
655 class SessionBus(Bus):
656 """The session (current login) message bus."""
657 def __new__(cls, private=False, mainloop=None):
658 """Return a connection to the session bus.
660 :Parameters:
661 `private` : bool
662 If true, never return an existing shared instance, but instead
663 return a private connection.
664 `mainloop` : dbus.mainloop.NativeMainLoop
665 The main loop to use. The default is to use the default
666 main loop if one has been set up, or raise an exception
667 if none has been.
669 return Bus.__new__(cls, Bus.TYPE_SESSION, private=private,
670 mainloop=mainloop)
672 class StarterBus(Bus):
673 """The bus that activated this process (only valid if
674 this process was launched by DBus activation).
676 def __new__(cls, private=False, mainloop=None):
677 """Return a connection to the bus that activated this process.
679 :Parameters:
680 `private` : bool
681 If true, never return an existing shared instance, but instead
682 return a private connection.
683 `mainloop` : dbus.mainloop.NativeMainLoop
684 The main loop to use. The default is to use the default
685 main loop if one has been set up, or raise an exception
686 if none has been.
688 return Bus.__new__(cls, Bus.TYPE_STARTER, private=private,
689 mainloop=mainloop)
691 class Interface:
692 """An interface into a remote object.
694 An Interface can be used to wrap ProxyObjects
695 so that calls can be routed to their correct
696 dbus interface
699 def __init__(self, object, dbus_interface):
700 """Construct a proxy for the given interface on the given object.
702 :Parameters:
703 `object` : `dbus.proxies.ProxyObject`
704 The remote object
705 `dbus_interface` : str
706 An interface the `object` implements
708 self._obj = object
709 self._dbus_interface = dbus_interface
711 __dbus_object_path__ = property (lambda self: self._obj.__dbus_object_path__,
712 None, None,
713 "The D-Bus object path of the "
714 "underlying object")
716 def connect_to_signal(self, signal_name, handler_function, dbus_interface = None, **keywords):
717 """Arrange for a function to be called when the given signal is
718 emitted.
720 :Parameters:
721 `signal_name` : str
722 The name of the signal
723 `handler_function` : callable
724 A function to be called when the signal is emitted by the
725 remote object. It will receive the signal's arguments
726 as its positional arguments.
727 `dbus_interface` : str
728 Optional interface with which to qualify the signal name.
729 The default is to use the interface this Interface represents.
730 (FIXME: deprecate this? Violates least astonishment)
731 :Keywords:
732 `utf8_strings` : bool
733 If True, the handler function will receive any string
734 arguments as dbus.UTF8String objects (a subclass of str
735 guaranteed to be UTF-8). If False (default) it will receive
736 any string arguments as dbus.String objects (a subclass of
737 unicode).
738 `byte_arrays` : bool
739 If True, the handler function will receive any byte-array
740 arguments as dbus.ByteArray objects (a subclass of str).
741 If False (default) it will receive any byte-array
742 arguments as a dbus.Array of dbus.Byte (subclasses of:
743 a list of ints).
744 `sender_keyword` : str
745 If not None (the default), the handler function will receive
746 the unique name of the sending endpoint as a keyword
747 argument with this name
748 `destination_keyword` : str
749 If not None (the default), the handler function will receive
750 the bus name of the destination (or None if the signal is a
751 broadcast, as is usual) as a keyword argument with this name.
752 `interface_keyword` : str
753 If not None (the default), the handler function will receive
754 the signal interface as a keyword argument with this name.
755 `member_keyword` : str
756 If not None (the default), the handler function will receive
757 the signal name as a keyword argument with this name.
758 `path_keyword` : str
759 If not None (the default), the handler function will receive
760 the object-path of the sending object as a keyword argument
761 with this name
762 `message_keyword` : str
763 If not None (the default), the handler function will receive
764 the `dbus.lowlevel.SignalMessage` as a keyword argument with
765 this name.
766 `arg...` : unicode or UTF-8 str
767 If there are additional keyword parameters of the form
768 ``arg``\ *n*, only call the handler_function for signals
769 where the *n*\ th argument is the value given for that
770 keyword parameter. As of this time only string arguments
771 can be matched.
773 if not dbus_interface:
774 dbus_interface = self._dbus_interface
776 return self._obj.connect_to_signal(signal_name, handler_function, dbus_interface, **keywords)
778 def __getattr__(self, member, **keywords):
779 # FIXME: this syntax is bizarre.
780 if (keywords.has_key('dbus_interface')):
781 _dbus_interface = keywords['dbus_interface']
782 else:
783 _dbus_interface = self._dbus_interface
785 # I have no idea what's going on here. -smcv
786 if member == '__call__':
787 return object.__call__
788 else:
789 ret = self._obj.__getattr__(member, dbus_interface=_dbus_interface)
790 return ret
792 def get_dbus_method(self, member, dbus_interface=None):
793 """Return a proxy method representing the given D-Bus method. The
794 returned proxy method can be called in the usual way. For instance, ::
796 iface.get_dbus_method("Foo")(123)
798 is equivalent to::
800 iface.Foo(123)
802 or even::
804 getattr(iface, "Foo")(123)
806 However, using `get_dbus_method` is the only way to call D-Bus
807 methods with certain awkward names - if the author of a service
808 implements a method called ``connect_to_signal`` or even
809 ``__getattr__``, you'll need to use `get_dbus_method` to call them.
811 For services which follow the D-Bus convention of CamelCaseMethodNames
812 this won't be a problem.
814 if dbus_interface is None:
815 dbus_interface = self._dbus_interface
816 return self._obj.get_dbus_method(member, dbus_interface=dbus_interface)
818 def __repr__(self):
819 return '<Interface %r implementing %r at %#x>'%(
820 self._obj, self._dbus_interface, id(self))
821 __str__ = __repr__
824 _dbus_bindings_warning = DeprecationWarning("""\
825 The dbus_bindings module is deprecated and will go away soon.
827 dbus-python 0.80 provides only a partial emulation of the old
828 dbus_bindings, which was never meant to be public API.
830 Most uses of dbus_bindings are applications catching the exception
831 dbus.dbus_bindings.DBusException. You should use dbus.DBusException
832 instead (this is compatible with all dbus-python versions since 0.40.2).
834 If you need additional public API, please contact the maintainers via
835 <dbus@lists.freedesktop.org>.
836 """)
838 if 'DBUS_PYTHON_NO_DEPRECATED' not in os.environ:
840 class _DBusBindingsEmulation:
841 """A partial emulation of the dbus_bindings module."""
842 _module = None
843 def __str__(self):
844 return '_DBusBindingsEmulation()'
845 def __repr__(self):
846 return '_DBusBindingsEmulation()'
847 def __getattr__(self, attr):
848 if self._module is None:
849 from warnings import warn as _warn
850 _warn(_dbus_bindings_warning, DeprecationWarning, stacklevel=2)
852 import dbus.dbus_bindings as m
853 self._module = m
854 return getattr(self._module, attr)
856 dbus_bindings = _DBusBindingsEmulation()
857 """Deprecated, don't use."""