Update NEWS, README for 0.80.0
[dbus-python-phuang.git] / dbus / _dbus.py
blobd0f2f1b5b33797aa88141ce2910bb0b1868e73e5
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 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 from __future__ import generators
28 __all__ = ('Bus', 'SystemBus', 'SessionBus', 'StarterBus', 'Interface')
29 __docformat__ = 'reStructuredText'
31 import _dbus_bindings
32 UTF8String = _dbus_bindings.UTF8String
33 DBusException = _dbus_bindings.DBusException
34 BusImplementation = _dbus_bindings.BusImplementation
36 import os
37 import logging
38 import weakref
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 self._conn_weakref = weakref.ref(conn)
77 self._sender = sender
78 self._interface = dbus_interface
79 self._member = member
80 self._path = object_path
81 self._handler = handler
82 if (sender is not None and sender[:1] != ':'
83 and sender != BUS_DAEMON_NAME):
84 self.sender_unique = conn.get_object(BUS_DAEMON_NAME, BUS_DAEMON_PATH).GetNameOwner(sender, dbus_interface=BUS_DAEMON_IFACE)
85 else:
86 self.sender_unique = sender
87 self._utf8_strings = utf8_strings
88 self._byte_arrays = byte_arrays
89 self._sender_keyword = sender_keyword
90 self._path_keyword = path_keyword
91 self._member_keyword = member_keyword
92 self._interface_keyword = interface_keyword
93 self._message_keyword = message_keyword
94 self._destination_keyword = destination_keyword
96 self._args_match = kwargs
97 if not kwargs:
98 self._int_args_match = None
99 else:
100 self._int_args_match = {}
101 for kwarg in kwargs:
102 if not kwarg.startswith('arg'):
103 raise TypeError('SignalMatch: unknown keyword argument %s'
104 % kwarg)
105 try:
106 index = int(kwarg[3:])
107 except ValueError:
108 raise TypeError('SignalMatch: unknown keyword argument %s'
109 % kwarg)
110 if index < 0 or index > 63:
111 raise TypeError('SignalMatch: arg match index must be in '
112 'range(64), not %d' % index)
113 self._int_args_match[index] = kwargs[kwarg]
115 # we're always going to have to calculate the match rule for
116 # the Bus's benefit, so this constructor might as well do the work
117 rule = ["type='signal'"]
118 if self._sender is not None:
119 rule.append("sender='%s'" % self._sender)
120 if self._path is not None:
121 rule.append("path='%s'" % self._path)
122 if self._interface is not None:
123 rule.append("interface='%s'" % self._interface)
124 if self._member is not None:
125 rule.append("member='%s'" % self._member)
126 for kwarg, value in kwargs.iteritems():
127 rule.append("%s='%s'" % (kwarg, value))
129 self._rule = ','.join(rule)
131 def __str__(self):
132 return self._rule
134 def __repr__(self):
135 return ('<%s at %x "%s" on conn %r>'
136 % (self.__class__, id(self), self._rule, self._conn_weakref()))
138 def matches_removal_spec(self, sender, object_path,
139 dbus_interface, member, handler, **kwargs):
140 if handler not in (None, self._handler):
141 #logger.debug('No match: handler %r is not %r', handler, self._handler)
142 return False
143 if sender != self._sender:
144 #logger.debug('No match: sender %r is not %r', sender, self._sender)
145 return False
146 if object_path != self._path:
147 #logger.debug('No match: path %r is not %r', object_path, self._path)
148 return False
149 if dbus_interface != self._interface:
150 #logger.debug('No match: interface %r is not %r', dbus_interface, self._interface)
151 return False
152 if member != self._member:
153 #logger.debug('No match: member %r is not %r', member, self._member)
154 return False
155 if kwargs != self._args_match:
156 #logger.debug('No match: args %r are not %r', kwargs, self._args_match)
157 return False
158 return True
160 def maybe_handle_message(self, message):
161 #logger.debug('%r: Considering whether I match %r %r', self, message,
162 #message.get_args_list())
163 args = None
165 # these haven't been checked yet by the match tree
166 if self.sender_unique not in (None, message.get_sender()):
167 #logger.debug('%r: not the desired sender, it was %r and I want '
168 #'%r', self, message.get_sender(), self.sender_unique)
169 return False
170 if self._int_args_match is not None:
171 # extracting args with utf8_strings and byte_arrays is less work
172 args = message.get_args_list(utf8_strings=True, byte_arrays=True)
173 for index, value in self._int_args_match.iteritems():
174 if (index >= len(args)
175 or not isinstance(args[index], UTF8String)
176 or args[index] != value):
177 #logger.debug('%r: not the desired args', self)
178 return False
180 # these have likely already been checked by the match tree
181 if self._member not in (None, message.get_member()):
182 #logger.debug('%r: not the desired member', self)
183 return False
184 if self._interface not in (None, message.get_interface()):
185 #logger.debug('%r: not the desired interface', self)
186 return False
187 if self._path not in (None, message.get_path()):
188 #logger.debug('%r: not the desired path', self)
189 return False
191 #logger.debug('%r: yes, I want to handle that signal', self)
192 # minor optimization: if we already extracted the args with the right
193 # calling convention to do the args match, don't bother doing so again
194 if args is None or not self._utf8_strings or not self._byte_arrays:
195 args = message.get_args_list(utf8_strings=self._utf8_strings,
196 byte_arrays=self._byte_arrays)
197 #logger.debug('%r: extracted signal arguments', self)
198 kwargs = {}
199 if self._sender_keyword is not None:
200 kwargs[self._sender_keyword] = message.get_sender()
201 if self._destination_keyword is not None:
202 kwargs[self._destination_keyword] = message.get_destination()
203 if self._path_keyword is not None:
204 kwargs[self._path_keyword] = message.get_path()
205 if self._member_keyword is not None:
206 kwargs[self._member_keyword] = message.get_member()
207 if self._interface_keyword is not None:
208 kwargs[self._interface_keyword] = message.get_interface()
209 if self._message_keyword is not None:
210 kwargs[self._message_keyword] = message
211 #logger.debug('%r: calling handler with %r and %r', self, args, kwargs)
212 self._handler(*args, **kwargs)
213 #logger.debug('%r: signal handled', self)
214 return True
216 def remove(self):
217 #logger.debug('%r: removing', self)
218 conn = self._conn_weakref()
219 # do nothing if the connection has already vanished
220 if conn is not None:
221 #logger.debug('%r: removing from connection %r', self, conn)
222 conn.remove_signal_receiver(self, self._member,
223 self._interface, self._sender,
224 self._path,
225 **self._args_match)
228 class Bus(BusImplementation):
229 """A connection to a DBus daemon.
231 One of three possible standard buses, the SESSION, SYSTEM,
232 or STARTER bus
235 TYPE_SESSION = _dbus_bindings.BUS_SESSION
236 """Represents a session bus (same as the global dbus.BUS_SESSION)"""
238 TYPE_SYSTEM = _dbus_bindings.BUS_SYSTEM
239 """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
241 TYPE_STARTER = _dbus_bindings.BUS_STARTER
242 """Represents the bus that started this service by activation (same as
243 the global dbus.BUS_STARTER)"""
245 ProxyObjectClass = ProxyObject
247 START_REPLY_SUCCESS = _dbus_bindings.DBUS_START_REPLY_SUCCESS
248 START_REPLY_ALREADY_RUNNING = _dbus_bindings.DBUS_START_REPLY_ALREADY_RUNNING
250 _shared_instances = {}
252 def __new__(cls, bus_type=TYPE_SESSION, private=False, mainloop=None):
253 """Constructor, returning an existing instance where appropriate.
255 The returned instance is actually always an instance of `SessionBus`,
256 `SystemBus` or `StarterBus`.
258 :Parameters:
259 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
260 Connect to the appropriate bus
261 `private` : bool
262 If true, never return an existing shared instance, but instead
263 return a private connection
264 `mainloop` : dbus.mainloop.NativeMainLoop
265 The main loop to use. The default is to use the default
266 main loop if one has been set up, or raise an exception
267 if none has been.
268 :ToDo:
269 - There is currently no way to connect this class to a custom
270 address.
271 - Some of this functionality should be available on
272 peer-to-peer D-Bus connections too.
273 :Changed: in dbus-python 0.80:
274 converted from a wrapper around a Connection to a Connection
275 subclass.
277 if (not private and bus_type in cls._shared_instances):
278 return cls._shared_instances[bus_type]
280 # this is a bit odd, but we create instances of the subtypes
281 # so we can return the shared instances if someone tries to
282 # construct one of them (otherwise we'd eg try and return an
283 # instance of Bus from __new__ in SessionBus). why are there
284 # three ways to construct this class? we just don't know.
285 if bus_type == cls.TYPE_SESSION:
286 subclass = SessionBus
287 elif bus_type == cls.TYPE_SYSTEM:
288 subclass = SystemBus
289 elif bus_type == cls.TYPE_STARTER:
290 subclass = StarterBus
291 else:
292 raise ValueError('invalid bus_type %s' % bus_type)
294 bus = _dbus_bindings.BusImplementation.__new__(subclass, bus_type,
295 mainloop=mainloop)
297 bus._bus_type = bus_type
298 # _bus_names is used by dbus.service.BusName!
299 bus._bus_names = weakref.WeakValueDictionary()
301 bus._signal_recipients_by_object_path = {}
302 """Map from object path to dict mapping dbus_interface to dict
303 mapping member to list of SignalMatch objects."""
305 bus._signal_sender_matches = {}
306 """Map from sender well-known name to list of match rules for all
307 signal handlers that match on sender well-known name."""
309 bus._signals_lock = thread.allocate_lock()
310 """Lock used to protect signal data structures if doing two
311 removals at the same time (everything else is atomic, thanks to
312 the GIL)"""
314 bus.add_message_filter(bus.__class__._signal_func)
316 if not private:
317 cls._shared_instances[bus_type] = bus
319 return bus
321 def close(self):
322 t = self._bus_type
323 if self.__class__._shared_instances[t] is self:
324 del self.__class__._shared_instances[t]
325 BusImplementation.close(self)
327 def get_connection(self):
328 """(Deprecated - in new code, just use self)
330 Return self, for backwards compatibility with earlier dbus-python
331 versions where Bus was not a subclass of Connection.
333 return self
334 _connection = property(get_connection, None, None,
335 """self._connection == self, for backwards
336 compatibility with earlier dbus-python versions
337 where Bus was not a subclass of Connection.""")
339 def get_session(private=False):
340 """Static method that returns a connection to the session bus.
342 :Parameters:
343 `private` : bool
344 If true, do not return a shared connection.
346 return SessionBus(private=private)
348 get_session = staticmethod(get_session)
350 def get_system(private=False):
351 """Static method that returns a connection to the system bus.
353 :Parameters:
354 `private` : bool
355 If true, do not return a shared connection.
357 return SystemBus(private=private)
359 get_system = staticmethod(get_system)
362 def get_starter(private=False):
363 """Static method that returns a connection to the starter bus.
365 :Parameters:
366 `private` : bool
367 If true, do not return a shared connection.
369 return StarterBus(private=private)
371 get_starter = staticmethod(get_starter)
373 def get_object(self, named_service, object_path, introspect=True,
374 follow_name_owner_changes=False):
375 """Return a local proxy for the given remote object.
377 Method calls on the proxy are translated into method calls on the
378 remote object.
380 :Parameters:
381 `named_service` : str
382 A bus name (either the unique name or a well-known name)
383 of the application owning the object
384 `object_path` : str
385 The object path of the desired object
386 `introspect` : bool
387 If true (default), attempt to introspect the remote
388 object to find out supported methods and their signatures
389 `follow_name_owner_changes` : bool
390 If the object path is a well-known name and this parameter
391 is false (default), resolve the well-known name to the unique
392 name of its current owner and bind to that instead; if the
393 ownership of the well-known name changes in future,
394 keep communicating with the original owner.
395 This is necessary if the D-Bus API used is stateful.
397 If the object path is a well-known name and this parameter
398 is true, whenever the well-known name changes ownership in
399 future, bind to the new owner, if any.
401 If the given object path is a unique name, this parameter
402 has no effect.
404 :Returns: a `dbus.proxies.ProxyObject`
405 :Raises `DBusException`: if resolving the well-known name to a
406 unique name fails
408 if follow_name_owner_changes:
409 self._require_main_loop() # we don't get the signals otherwise
410 return self.ProxyObjectClass(self, named_service, object_path,
411 introspect=introspect,
412 follow_name_owner_changes=follow_name_owner_changes)
414 def add_signal_receiver(self, handler_function,
415 signal_name=None,
416 dbus_interface=None,
417 named_service=None,
418 path=None,
419 **keywords):
420 """Arrange for the given function to be called when a signal matching
421 the parameters is received.
423 :Parameters:
424 `handler_function` : callable
425 The function to be called. Its positional arguments will
426 be the arguments of the signal. By default it will receive
427 no keyword arguments, but see the description of
428 the optional keyword arguments below.
429 `signal_name` : str
430 The signal name; None (the default) matches all names
431 `dbus_interface` : str
432 The D-Bus interface name with which to qualify the signal;
433 None (the default) matches all interface names
434 `named_service` : str
435 A bus name for the sender, which will be resolved to a
436 unique name if it is not already; None (the default) matches
437 any sender
438 `path` : str
439 The object path of the object which must have emitted the
440 signal; None (the default) matches any object path
441 :Keywords:
442 `utf8_strings` : bool
443 If True, the handler function will receive any string
444 arguments as dbus.UTF8String objects (a subclass of str
445 guaranteed to be UTF-8). If False (default) it will receive
446 any string arguments as dbus.String objects (a subclass of
447 unicode).
448 `byte_arrays` : bool
449 If True, the handler function will receive any byte-array
450 arguments as dbus.ByteArray objects (a subclass of str).
451 If False (default) it will receive any byte-array
452 arguments as a dbus.Array of dbus.Byte (subclasses of:
453 a list of ints).
454 `sender_keyword` : str
455 If not None (the default), the handler function will receive
456 the unique name of the sending endpoint as a keyword
457 argument with this name.
458 `destination_keyword` : str
459 If not None (the default), the handler function will receive
460 the bus name of the destination (or None if the signal is a
461 broadcast, as is usual) as a keyword argument with this name.
462 `interface_keyword` : str
463 If not None (the default), the handler function will receive
464 the signal interface as a keyword argument with this name.
465 `member_keyword` : str
466 If not None (the default), the handler function will receive
467 the signal name as a keyword argument with this name.
468 `path_keyword` : str
469 If not None (the default), the handler function will receive
470 the object-path of the sending object as a keyword argument
471 with this name.
472 `message_keyword` : str
473 If not None (the default), the handler function will receive
474 the `dbus.lowlevel.SignalMessage` as a keyword argument with
475 this name.
476 `arg...` : unicode or UTF-8 str
477 If there are additional keyword parameters of the form
478 ``arg``\ *n*, match only signals where the *n*\ th argument
479 is the value given for that keyword parameter. As of this
480 time only string arguments can be matched (in particular,
481 object paths and signatures can't).
483 self._require_main_loop()
485 match = SignalMatch(self, named_service, path, dbus_interface,
486 signal_name, handler_function, **keywords)
487 by_interface = self._signal_recipients_by_object_path.setdefault(path,
489 by_member = by_interface.setdefault(dbus_interface, {})
490 matches = by_member.setdefault(signal_name, [])
491 # The bus daemon is special - its unique-name is org.freedesktop.DBus
492 # rather than starting with :
493 if (named_service is not None and named_service[:1] != ':'
494 and named_service != BUS_DAEMON_NAME):
495 notification = self._signal_sender_matches.setdefault(named_service,
497 if not notification:
498 self.add_match_string(_NAME_OWNER_CHANGE_MATCH % named_service)
499 notification.append(match)
500 # make sure nobody is currently manipulating the list
501 self._signals_lock.acquire()
502 try:
503 matches.append(match)
504 finally:
505 self._signals_lock.release()
506 self.add_match_string(str(match))
507 return match
509 def _iter_easy_matches(self, path, dbus_interface, member):
510 if path is not None:
511 path_keys = (None, path)
512 else:
513 path_keys = (None,)
514 if dbus_interface is not None:
515 interface_keys = (None, dbus_interface)
516 else:
517 interface_keys = (None,)
518 if member is not None:
519 member_keys = (None, member)
520 else:
521 member_keys = (None,)
523 for path in path_keys:
524 by_interface = self._signal_recipients_by_object_path.get(path,
525 None)
526 if by_interface is None:
527 continue
528 for dbus_interface in interface_keys:
529 by_member = by_interface.get(dbus_interface, None)
530 if by_member is None:
531 continue
532 for member in member_keys:
533 matches = by_member.get(member, None)
534 if matches is None:
535 continue
536 for m in matches:
537 yield m
539 def _remove_name_owner_changed_for_match(self, named_service, match):
540 notification = self._signal_sender_matches.get(named_service, False)
541 if notification:
542 try:
543 notification.remove(match)
544 except LookupError:
545 pass
546 if not notification:
547 self.remove_match_string(_NAME_OWNER_CHANGE_MATCH
548 % named_service)
550 def remove_signal_receiver(self, handler_or_match,
551 signal_name=None,
552 dbus_interface=None,
553 named_service=None,
554 path=None,
555 **keywords):
556 #logger.debug('%r: removing signal receiver %r: member=%s, '
557 #'iface=%s, sender=%s, path=%s, kwargs=%r',
558 #self, handler_or_match, signal_name,
559 #dbus_interface, named_service, path, keywords)
560 #logger.debug('%r', self._signal_recipients_by_object_path)
561 by_interface = self._signal_recipients_by_object_path.get(path, None)
562 if by_interface is None:
563 return
564 by_member = by_interface.get(dbus_interface, None)
565 if by_member is None:
566 return
567 matches = by_member.get(signal_name, None)
568 if matches is None:
569 return
570 self._signals_lock.acquire()
571 #logger.debug(matches)
572 try:
573 new = []
574 for match in matches:
575 if (handler_or_match is match
576 or match.matches_removal_spec(named_service,
577 path,
578 dbus_interface,
579 signal_name,
580 handler_or_match,
581 **keywords)):
582 #logger.debug('Removing match string: %s', match)
583 self.remove_match_string(str(match))
584 self._remove_name_owner_changed_for_match(named_service,
585 match)
586 else:
587 new.append(match)
588 by_member[signal_name] = new
589 finally:
590 self._signals_lock.release()
592 def _signal_func(self, message):
593 """D-Bus filter function. Handle signals by dispatching to Python
594 callbacks kept in the match-rule tree.
597 #logger.debug('Incoming message %r with args %r', message,
598 #message.get_args_list())
600 if (message.get_type() != _dbus_bindings.MESSAGE_TYPE_SIGNAL):
601 return _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
603 # If it's NameOwnerChanged, we'll need to update our
604 # sender well-known name -> sender unique name mappings
605 if (message.is_signal(BUS_DAEMON_NAME, 'NameOwnerChanged')
606 and message.has_path(BUS_DAEMON_PATH)):
607 name, unused, new = message.get_args_list()
608 for match in self._signal_sender_matches.get(name, (None,))[1:]:
609 match.sender_unique = new
611 # See if anyone else wants to know
612 dbus_interface = message.get_interface()
613 path = message.get_path()
614 signal_name = message.get_member()
616 ret = _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
617 for match in self._iter_easy_matches(path, dbus_interface,
618 signal_name):
619 if match.maybe_handle_message(message):
620 ret = _dbus_bindings.HANDLER_RESULT_HANDLED
621 return ret
623 def __repr__(self):
624 if self._bus_type == self.TYPE_SESSION:
625 name = 'SESSION'
626 elif self._bus_type == self.TYPE_SYSTEM:
627 name = 'SYSTEM'
628 elif self._bus_type == self.TYPE_STARTER:
629 name = 'STARTER'
630 else:
631 raise AssertionError('Unable to represent unknown bus type.')
633 return '<dbus.Bus on %s at %#x>' % (name, id(self))
634 __str__ = __repr__
637 # FIXME: Drop the subclasses here? I can't think why we'd ever want
638 # polymorphism
639 class SystemBus(Bus):
640 """The system-wide message bus."""
641 def __new__(cls, private=False, mainloop=None):
642 """Return a connection to the system bus.
644 :Parameters:
645 `private` : bool
646 If true, never return an existing shared instance, but instead
647 return a private connection.
648 `mainloop` : dbus.mainloop.NativeMainLoop
649 The main loop to use. The default is to use the default
650 main loop if one has been set up, or raise an exception
651 if none has been.
653 return Bus.__new__(cls, Bus.TYPE_SYSTEM, mainloop=mainloop,
654 private=private)
656 class SessionBus(Bus):
657 """The session (current login) message bus."""
658 def __new__(cls, private=False, mainloop=None):
659 """Return a connection to the session bus.
661 :Parameters:
662 `private` : bool
663 If true, never return an existing shared instance, but instead
664 return a private connection.
665 `mainloop` : dbus.mainloop.NativeMainLoop
666 The main loop to use. The default is to use the default
667 main loop if one has been set up, or raise an exception
668 if none has been.
670 return Bus.__new__(cls, Bus.TYPE_SESSION, private=private,
671 mainloop=mainloop)
673 class StarterBus(Bus):
674 """The bus that activated this process (only valid if
675 this process was launched by DBus activation).
677 def __new__(cls, private=False, mainloop=None):
678 """Return a connection to the bus that activated this process.
680 :Parameters:
681 `private` : bool
682 If true, never return an existing shared instance, but instead
683 return a private connection.
684 `mainloop` : dbus.mainloop.NativeMainLoop
685 The main loop to use. The default is to use the default
686 main loop if one has been set up, or raise an exception
687 if none has been.
689 return Bus.__new__(cls, Bus.TYPE_STARTER, private=private,
690 mainloop=mainloop)
692 class Interface:
693 """An interface into a remote object.
695 An Interface can be used to wrap ProxyObjects
696 so that calls can be routed to their correct
697 dbus interface
700 def __init__(self, object, dbus_interface):
701 """Construct a proxy for the given interface on the given object.
703 :Parameters:
704 `object` : `dbus.proxies.ProxyObject`
705 The remote object
706 `dbus_interface` : str
707 An interface the `object` implements
709 self._obj = object
710 self._dbus_interface = dbus_interface
712 __dbus_object_path__ = property (lambda self: self._obj.__dbus_object_path__,
713 None, None,
714 "The D-Bus object path of the "
715 "underlying object")
717 def connect_to_signal(self, signal_name, handler_function, dbus_interface = None, **keywords):
718 """Arrange for a function to be called when the given signal is
719 emitted.
721 :Parameters:
722 `signal_name` : str
723 The name of the signal
724 `handler_function` : callable
725 A function to be called when the signal is emitted by the
726 remote object. It will receive the signal's arguments
727 as its positional arguments.
728 `dbus_interface` : str
729 Optional interface with which to qualify the signal name.
730 The default is to use the interface this Interface represents.
731 (FIXME: deprecate this? Violates least astonishment)
732 :Keywords:
733 `utf8_strings` : bool
734 If True, the handler function will receive any string
735 arguments as dbus.UTF8String objects (a subclass of str
736 guaranteed to be UTF-8). If False (default) it will receive
737 any string arguments as dbus.String objects (a subclass of
738 unicode).
739 `byte_arrays` : bool
740 If True, the handler function will receive any byte-array
741 arguments as dbus.ByteArray objects (a subclass of str).
742 If False (default) it will receive any byte-array
743 arguments as a dbus.Array of dbus.Byte (subclasses of:
744 a list of ints).
745 `sender_keyword` : str
746 If not None (the default), the handler function will receive
747 the unique name of the sending endpoint as a keyword
748 argument with this name
749 `destination_keyword` : str
750 If not None (the default), the handler function will receive
751 the bus name of the destination (or None if the signal is a
752 broadcast, as is usual) as a keyword argument with this name.
753 `interface_keyword` : str
754 If not None (the default), the handler function will receive
755 the signal interface as a keyword argument with this name.
756 `member_keyword` : str
757 If not None (the default), the handler function will receive
758 the signal name as a keyword argument with this name.
759 `path_keyword` : str
760 If not None (the default), the handler function will receive
761 the object-path of the sending object as a keyword argument
762 with this name
763 `message_keyword` : str
764 If not None (the default), the handler function will receive
765 the `dbus.lowlevel.SignalMessage` as a keyword argument with
766 this name.
767 `arg...` : unicode or UTF-8 str
768 If there are additional keyword parameters of the form
769 ``arg``\ *n*, only call the handler_function for signals
770 where the *n*\ th argument is the value given for that
771 keyword parameter. As of this time only string arguments
772 can be matched.
774 if not dbus_interface:
775 dbus_interface = self._dbus_interface
777 return self._obj.connect_to_signal(signal_name, handler_function, dbus_interface, **keywords)
779 def __getattr__(self, member, **keywords):
780 # FIXME: this syntax is bizarre.
781 if (keywords.has_key('dbus_interface')):
782 _dbus_interface = keywords['dbus_interface']
783 else:
784 _dbus_interface = self._dbus_interface
786 # I have no idea what's going on here. -smcv
787 if member == '__call__':
788 return object.__call__
789 else:
790 ret = self._obj.__getattr__(member, dbus_interface=_dbus_interface)
791 return ret
793 def get_dbus_method(self, member, dbus_interface=None):
794 """Return a proxy method representing the given D-Bus method. The
795 returned proxy method can be called in the usual way. For instance, ::
797 iface.get_dbus_method("Foo")(123)
799 is equivalent to::
801 iface.Foo(123)
803 or even::
805 getattr(iface, "Foo")(123)
807 However, using `get_dbus_method` is the only way to call D-Bus
808 methods with certain awkward names - if the author of a service
809 implements a method called ``connect_to_signal`` or even
810 ``__getattr__``, you'll need to use `get_dbus_method` to call them.
812 For services which follow the D-Bus convention of CamelCaseMethodNames
813 this won't be a problem.
815 if dbus_interface is None:
816 dbus_interface = self._dbus_interface
817 return self._obj.get_dbus_method(member, dbus_interface=dbus_interface)
819 def __repr__(self):
820 return '<Interface %r implementing %r at %#x>'%(
821 self._obj, self._dbus_interface, id(self))
822 __str__ = __repr__
825 _dbus_bindings_warning = DeprecationWarning("""\
826 The dbus_bindings module is deprecated and will go away soon.
828 dbus-python 0.80 provides only a partial emulation of the old
829 dbus_bindings, which was never meant to be public API.
831 Most uses of dbus_bindings are applications catching the exception
832 dbus.dbus_bindings.DBusException. You should use dbus.DBusException
833 instead (this is compatible with all dbus-python versions since 0.40.2).
835 If you need additional public API, please contact the maintainers via
836 <dbus@lists.freedesktop.org>.
837 """)
839 if 'DBUS_PYTHON_NO_DEPRECATED' not in os.environ:
841 class _DBusBindingsEmulation:
842 """A partial emulation of the dbus_bindings module."""
843 _module = None
844 def __str__(self):
845 return '_DBusBindingsEmulation()'
846 def __repr__(self):
847 return '_DBusBindingsEmulation()'
848 def __getattr__(self, attr):
849 if self._module is None:
850 from warnings import warn as _warn
851 _warn(_dbus_bindings_warning, DeprecationWarning, stacklevel=2)
853 import dbus.dbus_bindings as m
854 self._module = m
855 return getattr(self._module, attr)
857 dbus_bindings = _DBusBindingsEmulation()
858 """Deprecated, don't use."""