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'
32 UTF8String
= _dbus_bindings
.UTF8String
33 DBusException
= _dbus_bindings
.DBusException
34 BusImplementation
= _dbus_bindings
.BusImplementation
40 from dbus
.proxies
import ProxyObject
, BUS_DAEMON_NAME
, BUS_DAEMON_PATH
, \
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
,
58 """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
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,
76 self
._conn
_weakref
= weakref
.ref(conn
)
78 self
._interface
= dbus_interface
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
)
86 self
.sender_unique
= sender
87 self
._utf
8_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
98 self
._int
_args
_match
= None
100 self
._int
_args
_match
= {}
102 if not kwarg
.startswith('arg'):
103 raise TypeError('SignalMatch: unknown keyword argument %s'
106 index
= int(kwarg
[3:])
108 raise TypeError('SignalMatch: unknown keyword argument %s'
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
)
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)
143 if sender
!= self
._sender
:
144 #logger.debug('No match: sender %r is not %r', sender, self._sender)
146 if object_path
!= self
._path
:
147 #logger.debug('No match: path %r is not %r', object_path, self._path)
149 if dbus_interface
!= self
._interface
:
150 #logger.debug('No match: interface %r is not %r', dbus_interface, self._interface)
152 if member
!= self
._member
:
153 #logger.debug('No match: member %r is not %r', member, self._member)
155 if kwargs
!= self
._args
_match
:
156 #logger.debug('No match: args %r are not %r', kwargs, self._args_match)
160 def maybe_handle_message(self
, message
):
161 #logger.debug('%r: Considering whether I match %r %r', self, message,
162 #message.get_args_list())
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)
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)
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)
184 if self
._interface
not in (None, message
.get_interface()):
185 #logger.debug('%r: not the desired interface', self)
187 if self
._path
not in (None, message
.get_path()):
188 #logger.debug('%r: not the desired path', self)
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
._utf
8_strings
or not self
._byte
_arrays
:
195 args
= message
.get_args_list(utf8_strings
=self
._utf
8_strings
,
196 byte_arrays
=self
._byte
_arrays
)
197 #logger.debug('%r: extracted signal arguments', self)
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)
217 #logger.debug('%r: removing', self)
218 conn
= self
._conn
_weakref
()
219 # do nothing if the connection has already vanished
221 #logger.debug('%r: removing from connection %r', self, conn)
222 conn
.remove_signal_receiver(self
, self
._member
,
223 self
._interface
, self
._sender
,
228 class Bus(BusImplementation
):
229 """A connection to a DBus daemon.
231 One of three possible standard buses, the SESSION, SYSTEM,
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`.
259 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
260 Connect to the appropriate bus
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
269 - There is currently no way to connect this class to a custom
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
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
:
289 elif bus_type
== cls
.TYPE_STARTER
:
290 subclass
= StarterBus
292 raise ValueError('invalid bus_type %s' % bus_type
)
294 bus
= _dbus_bindings
.BusImplementation
.__new
__(subclass
, bus_type
,
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
314 bus
.add_message_filter(bus
.__class
__._signal
_func
)
317 cls
._shared
_instances
[bus_type
] = bus
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.
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.
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.
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.
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
381 `named_service` : str
382 A bus name (either the unique name or a well-known name)
383 of the application owning the object
385 The object path of the desired object
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
404 :Returns: a `dbus.proxies.ProxyObject`
405 :Raises `DBusException`: if resolving the well-known name to a
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
,
420 """Arrange for the given function to be called when a signal matching
421 the parameters is received.
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.
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
439 The object path of the object which must have emitted the
440 signal; None (the default) matches any object path
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
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:
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.
469 If not None (the default), the handler function will receive
470 the object-path of the sending object as a keyword argument
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
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
,
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()
503 matches
.append(match
)
505 self
._signals
_lock
.release()
506 self
.add_match_string(str(match
))
509 def _iter_easy_matches(self
, path
, dbus_interface
, member
):
511 path_keys
= (None, path
)
514 if dbus_interface
is not None:
515 interface_keys
= (None, dbus_interface
)
517 interface_keys
= (None,)
518 if member
is not None:
519 member_keys
= (None, member
)
521 member_keys
= (None,)
523 for path
in path_keys
:
524 by_interface
= self
._signal
_recipients
_by
_object
_path
.get(path
,
526 if by_interface
is None:
528 for dbus_interface
in interface_keys
:
529 by_member
= by_interface
.get(dbus_interface
, None)
530 if by_member
is None:
532 for member
in member_keys
:
533 matches
= by_member
.get(member
, None)
539 def _remove_name_owner_changed_for_match(self
, named_service
, match
):
540 notification
= self
._signal
_sender
_matches
.get(named_service
, False)
543 notification
.remove(match
)
547 self
.remove_match_string(_NAME_OWNER_CHANGE_MATCH
550 def remove_signal_receiver(self
, handler_or_match
,
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:
564 by_member
= by_interface
.get(dbus_interface
, None)
565 if by_member
is None:
567 matches
= by_member
.get(signal_name
, None)
570 self
._signals
_lock
.acquire()
571 #logger.debug(matches)
574 for match
in matches
:
575 if (handler_or_match
is match
576 or match
.matches_removal_spec(named_service
,
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
,
588 by_member
[signal_name
] = new
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
,
619 if match
.maybe_handle_message(message
):
620 ret
= _dbus_bindings
.HANDLER_RESULT_HANDLED
624 if self
._bus
_type
== self
.TYPE_SESSION
:
626 elif self
._bus
_type
== self
.TYPE_SYSTEM
:
628 elif self
._bus
_type
== self
.TYPE_STARTER
:
631 raise AssertionError('Unable to represent unknown bus type.')
633 return '<dbus.Bus on %s at %#x>' % (name
, id(self
))
637 # FIXME: Drop the subclasses here? I can't think why we'd ever want
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.
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
653 return Bus
.__new
__(cls
, Bus
.TYPE_SYSTEM
, mainloop
=mainloop
,
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.
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
670 return Bus
.__new
__(cls
, Bus
.TYPE_SESSION
, private
=private
,
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.
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
689 return Bus
.__new
__(cls
, Bus
.TYPE_STARTER
, private
=private
,
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
700 def __init__(self
, object, dbus_interface
):
701 """Construct a proxy for the given interface on the given object.
704 `object` : `dbus.proxies.ProxyObject`
706 `dbus_interface` : str
707 An interface the `object` implements
710 self
._dbus
_interface
= dbus_interface
712 __dbus_object_path__
= property (lambda self
: self
._obj
.__dbus
_object
_path
__,
714 "The D-Bus object path of the "
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
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)
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
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:
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.
760 If not None (the default), the handler function will receive
761 the object-path of the sending object as a keyword argument
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
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
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']
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
__
790 ret
= self
._obj
.__getattr
__(member
, dbus_interface
=_dbus_interface
)
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)
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
)
820 return '<Interface %r implementing %r at %#x>'%(
821 self
._obj
, self
._dbus
_interface
, id(self
))
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>.
839 if 'DBUS_PYTHON_NO_DEPRECATED' not in os
.environ
:
841 class _DBusBindingsEmulation
:
842 """A partial emulation of the dbus_bindings module."""
845 return '_DBusBindingsEmulation()'
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
855 return getattr(self
._module
, attr
)
857 dbus_bindings
= _DBusBindingsEmulation()
858 """Deprecated, don't use."""