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')
27 __docformat__
= 'reStructuredText'
30 UTF8String
= _dbus_bindings
.UTF8String
31 DBusException
= _dbus_bindings
.DBusException
32 BusImplementation
= _dbus_bindings
.BusImplementation
38 from traceback
import print_exc
40 from dbus
.proxies
import ProxyObject
, BUS_DAEMON_NAME
, BUS_DAEMON_PATH
, \
46 import dummy_thread
as thread
48 logger
= logging
.getLogger('dbus._dbus')
50 _NAME_OWNER_CHANGE_MATCH
= ("type='signal',sender='%s',"
51 "interface='%s',member='NameOwnerChanged',"
52 "path='%s',arg0='%%s'"
53 % (BUS_DAEMON_NAME
, BUS_DAEMON_IFACE
,
55 """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
59 class SignalMatch(object):
60 __slots__
= ('sender_unique', '_member', '_interface', '_sender',
61 '_path', '_handler', '_args_match', '_rule',
62 '_utf8_strings', '_byte_arrays', '_conn_weakref',
63 '_destination_keyword', '_interface_keyword',
64 '_message_keyword', '_member_keyword',
65 '_sender_keyword', '_path_keyword', '_int_args_match')
67 def __init__(self
, conn
, sender
, object_path
, dbus_interface
,
68 member
, handler
, utf8_strings
=False, byte_arrays
=False,
69 sender_keyword
=None, path_keyword
=None,
70 interface_keyword
=None, member_keyword
=None,
71 message_keyword
=None, destination_keyword
=None,
73 if member
is not None:
74 _dbus_bindings
.validate_member_name(member
)
75 if dbus_interface
is not None:
76 _dbus_bindings
.validate_interface_name(dbus_interface
)
77 if sender
is not None:
78 _dbus_bindings
.validate_bus_name(sender
)
79 if object_path
is not None:
80 _dbus_bindings
.validate_object_path(object_path
)
82 self
._conn
_weakref
= weakref
.ref(conn
)
84 self
._interface
= dbus_interface
86 self
._path
= object_path
87 self
._handler
= handler
88 if (sender
is not None and sender
[:1] != ':'
89 and sender
!= BUS_DAEMON_NAME
):
90 self
.sender_unique
= conn
.get_object(BUS_DAEMON_NAME
, BUS_DAEMON_PATH
).GetNameOwner(sender
, dbus_interface
=BUS_DAEMON_IFACE
)
92 self
.sender_unique
= sender
93 self
._utf
8_strings
= utf8_strings
94 self
._byte
_arrays
= byte_arrays
95 self
._sender
_keyword
= sender_keyword
96 self
._path
_keyword
= path_keyword
97 self
._member
_keyword
= member_keyword
98 self
._interface
_keyword
= interface_keyword
99 self
._message
_keyword
= message_keyword
100 self
._destination
_keyword
= destination_keyword
102 self
._args
_match
= kwargs
104 self
._int
_args
_match
= None
106 self
._int
_args
_match
= {}
108 if not kwarg
.startswith('arg'):
109 raise TypeError('SignalMatch: unknown keyword argument %s'
112 index
= int(kwarg
[3:])
114 raise TypeError('SignalMatch: unknown keyword argument %s'
116 if index
< 0 or index
> 63:
117 raise TypeError('SignalMatch: arg match index must be in '
118 'range(64), not %d' % index
)
119 self
._int
_args
_match
[index
] = kwargs
[kwarg
]
121 # we're always going to have to calculate the match rule for
122 # the Bus's benefit, so this constructor might as well do the work
123 rule
= ["type='signal'"]
124 if self
._sender
is not None:
125 rule
.append("sender='%s'" % self
._sender
)
126 if self
._path
is not None:
127 rule
.append("path='%s'" % self
._path
)
128 if self
._interface
is not None:
129 rule
.append("interface='%s'" % self
._interface
)
130 if self
._member
is not None:
131 rule
.append("member='%s'" % self
._member
)
132 for kwarg
, value
in kwargs
.iteritems():
133 rule
.append("%s='%s'" % (kwarg
, value
))
135 self
._rule
= ','.join(rule
)
141 return ('<%s at %x "%s" on conn %r>'
142 % (self
.__class
__, id(self
), self
._rule
, self
._conn
_weakref
()))
144 def matches_removal_spec(self
, sender
, object_path
,
145 dbus_interface
, member
, handler
, **kwargs
):
146 if handler
not in (None, self
._handler
):
148 if sender
!= self
._sender
:
150 if object_path
!= self
._path
:
152 if dbus_interface
!= self
._interface
:
154 if member
!= self
._member
:
156 if kwargs
!= self
._args
_match
:
160 def maybe_handle_message(self
, message
):
163 # these haven't been checked yet by the match tree
164 if self
.sender_unique
not in (None, message
.get_sender()):
166 if self
._int
_args
_match
is not None:
167 # extracting args with utf8_strings and byte_arrays is less work
168 args
= message
.get_args_list(utf8_strings
=True, byte_arrays
=True)
169 for index
, value
in self
._int
_args
_match
.iteritems():
170 if (index
>= len(args
)
171 or not isinstance(args
[index
], UTF8String
)
172 or args
[index
] != value
):
175 # these have likely already been checked by the match tree
176 if self
._member
not in (None, message
.get_member()):
178 if self
._interface
not in (None, message
.get_interface()):
180 if self
._path
not in (None, message
.get_path()):
184 # minor optimization: if we already extracted the args with the
185 # right calling convention to do the args match, don't bother
187 if args
is None or not self
._utf
8_strings
or not self
._byte
_arrays
:
188 args
= message
.get_args_list(utf8_strings
=self
._utf
8_strings
,
189 byte_arrays
=self
._byte
_arrays
)
191 if self
._sender
_keyword
is not None:
192 kwargs
[self
._sender
_keyword
] = message
.get_sender()
193 if self
._destination
_keyword
is not None:
194 kwargs
[self
._destination
_keyword
] = message
.get_destination()
195 if self
._path
_keyword
is not None:
196 kwargs
[self
._path
_keyword
] = message
.get_path()
197 if self
._member
_keyword
is not None:
198 kwargs
[self
._member
_keyword
] = message
.get_member()
199 if self
._interface
_keyword
is not None:
200 kwargs
[self
._interface
_keyword
] = message
.get_interface()
201 if self
._message
_keyword
is not None:
202 kwargs
[self
._message
_keyword
] = message
203 self
._handler
(*args
, **kwargs
)
205 # FIXME: need to decide whether dbus-python uses logging, or
206 # stderr, or what, and make it consistent
207 sys
.stderr
.write('Exception in handler for D-Bus signal:\n')
213 #logger.debug('%r: removing', self)
214 conn
= self
._conn
_weakref
()
215 # do nothing if the connection has already vanished
217 #logger.debug('%r: removing from connection %r', self, conn)
218 conn
.remove_signal_receiver(self
, self
._member
,
219 self
._interface
, self
._sender
,
224 class Bus(BusImplementation
):
225 """A connection to a DBus daemon.
227 One of three possible standard buses, the SESSION, SYSTEM,
231 TYPE_SESSION
= _dbus_bindings
.BUS_SESSION
232 """Represents a session bus (same as the global dbus.BUS_SESSION)"""
234 TYPE_SYSTEM
= _dbus_bindings
.BUS_SYSTEM
235 """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
237 TYPE_STARTER
= _dbus_bindings
.BUS_STARTER
238 """Represents the bus that started this service by activation (same as
239 the global dbus.BUS_STARTER)"""
241 ProxyObjectClass
= ProxyObject
243 START_REPLY_SUCCESS
= _dbus_bindings
.DBUS_START_REPLY_SUCCESS
244 START_REPLY_ALREADY_RUNNING
= _dbus_bindings
.DBUS_START_REPLY_ALREADY_RUNNING
246 _shared_instances
= {}
248 def __new__(cls
, bus_type
=TYPE_SESSION
, private
=False, mainloop
=None):
249 """Constructor, returning an existing instance where appropriate.
251 The returned instance is actually always an instance of `SessionBus`,
252 `SystemBus` or `StarterBus`.
255 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
256 Connect to the appropriate bus
258 If true, never return an existing shared instance, but instead
259 return a private connection
260 `mainloop` : dbus.mainloop.NativeMainLoop
261 The main loop to use. The default is to use the default
262 main loop if one has been set up, or raise an exception
265 - There is currently no way to connect this class to a custom
267 - Some of this functionality should be available on
268 peer-to-peer D-Bus connections too.
269 :Changed: in dbus-python 0.80:
270 converted from a wrapper around a Connection to a Connection
273 if (not private
and bus_type
in cls
._shared
_instances
):
274 return cls
._shared
_instances
[bus_type
]
276 # this is a bit odd, but we create instances of the subtypes
277 # so we can return the shared instances if someone tries to
278 # construct one of them (otherwise we'd eg try and return an
279 # instance of Bus from __new__ in SessionBus). why are there
280 # three ways to construct this class? we just don't know.
281 if bus_type
== cls
.TYPE_SESSION
:
282 subclass
= SessionBus
283 elif bus_type
== cls
.TYPE_SYSTEM
:
285 elif bus_type
== cls
.TYPE_STARTER
:
286 subclass
= StarterBus
288 raise ValueError('invalid bus_type %s' % bus_type
)
290 bus
= _dbus_bindings
.BusImplementation
.__new
__(subclass
, bus_type
,
293 bus
._bus
_type
= bus_type
294 # _bus_names is used by dbus.service.BusName!
295 bus
._bus
_names
= weakref
.WeakValueDictionary()
297 bus
._signal
_recipients
_by
_object
_path
= {}
298 """Map from object path to dict mapping dbus_interface to dict
299 mapping member to list of SignalMatch objects."""
301 bus
._signal
_sender
_matches
= {}
302 """Map from sender well-known name to list of match rules for all
303 signal handlers that match on sender well-known name."""
305 bus
._signals
_lock
= thread
.allocate_lock()
306 """Lock used to protect signal data structures if doing two
307 removals at the same time (everything else is atomic, thanks to
310 bus
.add_message_filter(bus
.__class
__._signal
_func
)
313 cls
._shared
_instances
[bus_type
] = bus
319 if self
.__class
__._shared
_instances
[t
] is self
:
320 del self
.__class
__._shared
_instances
[t
]
321 BusImplementation
.close(self
)
323 def get_connection(self
):
324 """(Deprecated - in new code, just use self)
326 Return self, for backwards compatibility with earlier dbus-python
327 versions where Bus was not a subclass of Connection.
330 _connection
= property(get_connection
, None, None,
331 """self._connection == self, for backwards
332 compatibility with earlier dbus-python versions
333 where Bus was not a subclass of Connection.""")
335 def get_session(private
=False):
336 """Static method that returns a connection to the session bus.
340 If true, do not return a shared connection.
342 return SessionBus(private
=private
)
344 get_session
= staticmethod(get_session
)
346 def get_system(private
=False):
347 """Static method that returns a connection to the system bus.
351 If true, do not return a shared connection.
353 return SystemBus(private
=private
)
355 get_system
= staticmethod(get_system
)
358 def get_starter(private
=False):
359 """Static method that returns a connection to the starter bus.
363 If true, do not return a shared connection.
365 return StarterBus(private
=private
)
367 get_starter
= staticmethod(get_starter
)
369 def get_object(self
, named_service
, object_path
, introspect
=True,
370 follow_name_owner_changes
=False):
371 """Return a local proxy for the given remote object.
373 Method calls on the proxy are translated into method calls on the
377 `named_service` : str
378 A bus name (either the unique name or a well-known name)
379 of the application owning the object
381 The object path of the desired object
383 If true (default), attempt to introspect the remote
384 object to find out supported methods and their signatures
385 `follow_name_owner_changes` : bool
386 If the object path is a well-known name and this parameter
387 is false (default), resolve the well-known name to the unique
388 name of its current owner and bind to that instead; if the
389 ownership of the well-known name changes in future,
390 keep communicating with the original owner.
391 This is necessary if the D-Bus API used is stateful.
393 If the object path is a well-known name and this parameter
394 is true, whenever the well-known name changes ownership in
395 future, bind to the new owner, if any.
397 If the given object path is a unique name, this parameter
400 :Returns: a `dbus.proxies.ProxyObject`
401 :Raises `DBusException`: if resolving the well-known name to a
404 if follow_name_owner_changes
:
405 self
._require
_main
_loop
() # we don't get the signals otherwise
406 return self
.ProxyObjectClass(self
, named_service
, object_path
,
407 introspect
=introspect
,
408 follow_name_owner_changes
=follow_name_owner_changes
)
410 def add_signal_receiver(self
, handler_function
,
416 """Arrange for the given function to be called when a signal matching
417 the parameters is received.
420 `handler_function` : callable
421 The function to be called. Its positional arguments will
422 be the arguments of the signal. By default it will receive
423 no keyword arguments, but see the description of
424 the optional keyword arguments below.
426 The signal name; None (the default) matches all names
427 `dbus_interface` : str
428 The D-Bus interface name with which to qualify the signal;
429 None (the default) matches all interface names
430 `named_service` : str
431 A bus name for the sender, which will be resolved to a
432 unique name if it is not already; None (the default) matches
435 The object path of the object which must have emitted the
436 signal; None (the default) matches any object path
438 `utf8_strings` : bool
439 If True, the handler function will receive any string
440 arguments as dbus.UTF8String objects (a subclass of str
441 guaranteed to be UTF-8). If False (default) it will receive
442 any string arguments as dbus.String objects (a subclass of
445 If True, the handler function will receive any byte-array
446 arguments as dbus.ByteArray objects (a subclass of str).
447 If False (default) it will receive any byte-array
448 arguments as a dbus.Array of dbus.Byte (subclasses of:
450 `sender_keyword` : str
451 If not None (the default), the handler function will receive
452 the unique name of the sending endpoint as a keyword
453 argument with this name.
454 `destination_keyword` : str
455 If not None (the default), the handler function will receive
456 the bus name of the destination (or None if the signal is a
457 broadcast, as is usual) as a keyword argument with this name.
458 `interface_keyword` : str
459 If not None (the default), the handler function will receive
460 the signal interface as a keyword argument with this name.
461 `member_keyword` : str
462 If not None (the default), the handler function will receive
463 the signal name as a keyword argument with this name.
465 If not None (the default), the handler function will receive
466 the object-path of the sending object as a keyword argument
468 `message_keyword` : str
469 If not None (the default), the handler function will receive
470 the `dbus.lowlevel.SignalMessage` as a keyword argument with
472 `arg...` : unicode or UTF-8 str
473 If there are additional keyword parameters of the form
474 ``arg``\ *n*, match only signals where the *n*\ th argument
475 is the value given for that keyword parameter. As of this
476 time only string arguments can be matched (in particular,
477 object paths and signatures can't).
479 self
._require
_main
_loop
()
481 match
= SignalMatch(self
, named_service
, path
, dbus_interface
,
482 signal_name
, handler_function
, **keywords
)
483 by_interface
= self
._signal
_recipients
_by
_object
_path
.setdefault(path
,
485 by_member
= by_interface
.setdefault(dbus_interface
, {})
486 matches
= by_member
.setdefault(signal_name
, [])
487 # The bus daemon is special - its unique-name is org.freedesktop.DBus
488 # rather than starting with :
489 if (named_service
is not None and named_service
[:1] != ':'
490 and named_service
!= BUS_DAEMON_NAME
):
491 notification
= self
._signal
_sender
_matches
.setdefault(named_service
,
494 self
.add_match_string(_NAME_OWNER_CHANGE_MATCH
% named_service
)
495 notification
.append(match
)
496 # make sure nobody is currently manipulating the list
497 self
._signals
_lock
.acquire()
499 matches
.append(match
)
501 self
._signals
_lock
.release()
502 self
.add_match_string(str(match
))
505 def _iter_easy_matches(self
, path
, dbus_interface
, member
):
507 path_keys
= (None, path
)
510 if dbus_interface
is not None:
511 interface_keys
= (None, dbus_interface
)
513 interface_keys
= (None,)
514 if member
is not None:
515 member_keys
= (None, member
)
517 member_keys
= (None,)
519 for path
in path_keys
:
520 by_interface
= self
._signal
_recipients
_by
_object
_path
.get(path
,
522 if by_interface
is None:
524 for dbus_interface
in interface_keys
:
525 by_member
= by_interface
.get(dbus_interface
, None)
526 if by_member
is None:
528 for member
in member_keys
:
529 matches
= by_member
.get(member
, None)
535 def _remove_name_owner_changed_for_match(self
, named_service
, match
):
536 notification
= self
._signal
_sender
_matches
.get(named_service
, False)
539 notification
.remove(match
)
543 self
.remove_match_string(_NAME_OWNER_CHANGE_MATCH
546 def remove_signal_receiver(self
, handler_or_match
,
552 #logger.debug('%r: removing signal receiver %r: member=%s, '
553 #'iface=%s, sender=%s, path=%s, kwargs=%r',
554 #self, handler_or_match, signal_name,
555 #dbus_interface, named_service, path, keywords)
556 #logger.debug('%r', self._signal_recipients_by_object_path)
557 by_interface
= self
._signal
_recipients
_by
_object
_path
.get(path
, None)
558 if by_interface
is None:
560 by_member
= by_interface
.get(dbus_interface
, None)
561 if by_member
is None:
563 matches
= by_member
.get(signal_name
, None)
566 self
._signals
_lock
.acquire()
567 #logger.debug(matches)
570 for match
in matches
:
571 if (handler_or_match
is match
572 or match
.matches_removal_spec(named_service
,
578 #logger.debug('Removing match string: %s', match)
579 self
.remove_match_string(str(match
))
580 self
._remove
_name
_owner
_changed
_for
_match
(named_service
,
584 by_member
[signal_name
] = new
586 self
._signals
_lock
.release()
588 def _signal_func(self
, message
):
589 """D-Bus filter function. Handle signals by dispatching to Python
590 callbacks kept in the match-rule tree.
593 #logger.debug('Incoming message %r with args %r', message,
594 #message.get_args_list())
596 if (message
.get_type() != _dbus_bindings
.MESSAGE_TYPE_SIGNAL
):
597 return _dbus_bindings
.HANDLER_RESULT_NOT_YET_HANDLED
599 # If it's NameOwnerChanged, we'll need to update our
600 # sender well-known name -> sender unique name mappings
601 if (message
.is_signal(BUS_DAEMON_IFACE
, 'NameOwnerChanged')
602 and message
.has_sender(BUS_DAEMON_NAME
)
603 and message
.has_path(BUS_DAEMON_PATH
)):
604 name
, unused
, new
= message
.get_args_list()
605 for match
in self
._signal
_sender
_matches
.get(name
, (None,))[1:]:
606 match
.sender_unique
= new
608 # See if anyone else wants to know
609 dbus_interface
= message
.get_interface()
610 path
= message
.get_path()
611 signal_name
= message
.get_member()
613 ret
= _dbus_bindings
.HANDLER_RESULT_NOT_YET_HANDLED
614 for match
in self
._iter
_easy
_matches
(path
, dbus_interface
,
616 if match
.maybe_handle_message(message
):
617 ret
= _dbus_bindings
.HANDLER_RESULT_HANDLED
621 if self
._bus
_type
== self
.TYPE_SESSION
:
623 elif self
._bus
_type
== self
.TYPE_SYSTEM
:
625 elif self
._bus
_type
== self
.TYPE_STARTER
:
628 raise AssertionError('Unable to represent unknown bus type.')
630 return '<dbus.Bus on %s at %#x>' % (name
, id(self
))
634 # FIXME: Drop the subclasses here? I can't think why we'd ever want
636 class SystemBus(Bus
):
637 """The system-wide message bus."""
638 def __new__(cls
, private
=False, mainloop
=None):
639 """Return a connection to the system bus.
643 If true, never return an existing shared instance, but instead
644 return a private connection.
645 `mainloop` : dbus.mainloop.NativeMainLoop
646 The main loop to use. The default is to use the default
647 main loop if one has been set up, or raise an exception
650 return Bus
.__new
__(cls
, Bus
.TYPE_SYSTEM
, mainloop
=mainloop
,
653 class SessionBus(Bus
):
654 """The session (current login) message bus."""
655 def __new__(cls
, private
=False, mainloop
=None):
656 """Return a connection to the session bus.
660 If true, never return an existing shared instance, but instead
661 return a private connection.
662 `mainloop` : dbus.mainloop.NativeMainLoop
663 The main loop to use. The default is to use the default
664 main loop if one has been set up, or raise an exception
667 return Bus
.__new
__(cls
, Bus
.TYPE_SESSION
, private
=private
,
670 class StarterBus(Bus
):
671 """The bus that activated this process (only valid if
672 this process was launched by DBus activation).
674 def __new__(cls
, private
=False, mainloop
=None):
675 """Return a connection to the bus that activated this process.
679 If true, never return an existing shared instance, but instead
680 return a private connection.
681 `mainloop` : dbus.mainloop.NativeMainLoop
682 The main loop to use. The default is to use the default
683 main loop if one has been set up, or raise an exception
686 return Bus
.__new
__(cls
, Bus
.TYPE_STARTER
, private
=private
,
690 _dbus_bindings_warning
= DeprecationWarning("""\
691 The dbus_bindings module is deprecated and will go away soon.
693 dbus-python 0.80 provides only a partial emulation of the old
694 dbus_bindings, which was never meant to be public API.
696 Most uses of dbus_bindings are applications catching the exception
697 dbus.dbus_bindings.DBusException. You should use dbus.DBusException
698 instead (this is compatible with all dbus-python versions since 0.40.2).
700 If you need additional public API, please contact the maintainers via
701 <dbus@lists.freedesktop.org>.
704 if 'DBUS_PYTHON_NO_DEPRECATED' not in os
.environ
:
706 class _DBusBindingsEmulation
:
707 """A partial emulation of the dbus_bindings module."""
710 return '_DBusBindingsEmulation()'
712 return '_DBusBindingsEmulation()'
713 def __getattr__(self
, attr
):
714 if self
._module
is None:
715 from warnings
import warn
as _warn
716 _warn(_dbus_bindings_warning
, DeprecationWarning, stacklevel
=2)
718 import dbus
.dbus_bindings
as m
720 return getattr(self
._module
, attr
)
722 dbus_bindings
= _DBusBindingsEmulation()
723 """Deprecated, don't use."""