Fix a leak of references to Py_None in slightly pathological cases (should never...
[dbus-python-phuang.git] / dbus / _dbus.py
blob21ed122c0898f49c3df7535c10a8cefa9f019a7d
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 weakref
38 from dbus.proxies import ProxyObject, BUS_DAEMON_NAME, BUS_DAEMON_PATH, \
39 BUS_DAEMON_IFACE
41 try:
42 import thread
43 except ImportError:
44 import dummy_thread as thread
46 logger = logging.getLogger('dbus._dbus')
47 BUS_DAEMON_NAME = 'org.freedesktop.DBus'
48 BUS_DAEMON_PATH = '/org/freedesktop/DBus'
49 BUS_DAEMON_IFACE = BUS_DAEMON_NAME
51 _NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s',"
52 "interface='%s',member='NameOwnerChanged',"
53 "path='%s',arg0='%%s'"
54 % (BUS_DAEMON_NAME, BUS_DAEMON_IFACE,
55 BUS_DAEMON_PATH))
56 """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
57 messages"""
60 class SignalMatch(object):
61 __slots__ = ('sender_unique', '_member', '_interface', '_sender',
62 '_path', '_handler', '_args_match', '_rule',
63 '_utf8_strings', '_byte_arrays', '_conn_weakref',
64 '_destination_keyword', '_interface_keyword',
65 '_message_keyword', '_member_keyword',
66 '_sender_keyword', '_path_keyword', '_int_args_match')
68 def __init__(self, conn, sender, object_path, dbus_interface,
69 member, handler, utf8_strings=False, byte_arrays=False,
70 sender_keyword=None, path_keyword=None,
71 interface_keyword=None, member_keyword=None,
72 message_keyword=None, destination_keyword=None,
73 **kwargs):
74 self._conn_weakref = weakref.ref(conn)
75 self._sender = sender
76 self._interface = dbus_interface
77 self._member = member
78 self._path = object_path
79 self._handler = handler
80 if (sender is not None and sender[:1] != ':'
81 and sender != BUS_DAEMON_NAME):
82 self.sender_unique = conn.get_object(BUS_DAEMON_NAME, BUS_DAEMON_PATH).GetNameOwner(sender, dbus_interface=BUS_DAEMON_IFACE)
83 else:
84 self.sender_unique = sender
85 self._utf8_strings = utf8_strings
86 self._byte_arrays = byte_arrays
87 self._sender_keyword = sender_keyword
88 self._path_keyword = path_keyword
89 self._member_keyword = member_keyword
90 self._interface_keyword = interface_keyword
91 self._message_keyword = message_keyword
92 self._destination_keyword = destination_keyword
94 self._args_match = kwargs
95 if not kwargs:
96 self._int_args_match = None
97 else:
98 self._int_args_match = {}
99 for kwarg in kwargs:
100 if not kwarg.startswith('arg'):
101 raise TypeError('SignalMatch: unknown keyword argument %s'
102 % kwarg)
103 try:
104 index = int(kwarg[3:])
105 except ValueError:
106 raise TypeError('SignalMatch: unknown keyword argument %s'
107 % kwarg)
108 if index < 0 or index > 63:
109 raise TypeError('SignalMatch: arg match index must be in '
110 'range(64), not %d' % index)
111 self._int_args_match[index] = kwargs[kwarg]
113 # we're always going to have to calculate the match rule for
114 # the Bus's benefit, so this constructor might as well do the work
115 rule = ["type='signal'"]
116 if self._sender is not None:
117 rule.append("sender='%s'" % self._sender)
118 if self._path is not None:
119 rule.append("path='%s'" % self._path)
120 if self._interface is not None:
121 rule.append("interface='%s'" % self._interface)
122 if self._member is not None:
123 rule.append("member='%s'" % self._member)
124 for kwarg, value in kwargs.iteritems():
125 rule.append("%s='%s'" % (kwarg, value))
127 self._rule = ','.join(rule)
129 def __str__(self):
130 return self._rule
132 def __repr__(self):
133 return ('<%s at %x "%s" on conn %r>'
134 % (self.__class__, id(self), self._rule, self._conn_weakref()))
136 def matches_removal_spec(self, sender, object_path,
137 dbus_interface, member, handler, **kwargs):
138 if handler not in (None, self._handler):
139 #logger.debug('No match: handler %r is not %r', handler, self._handler)
140 return False
141 if sender != self._sender:
142 #logger.debug('No match: sender %r is not %r', sender, self._sender)
143 return False
144 if object_path != self._path:
145 #logger.debug('No match: path %r is not %r', object_path, self._path)
146 return False
147 if dbus_interface != self._interface:
148 #logger.debug('No match: interface %r is not %r', dbus_interface, self._interface)
149 return False
150 if member != self._member:
151 #logger.debug('No match: member %r is not %r', member, self._member)
152 return False
153 if kwargs != self._args_match:
154 #logger.debug('No match: args %r are not %r', kwargs, self._args_match)
155 return False
156 return True
158 def maybe_handle_message(self, message):
159 #logger.debug('%r: Considering whether I match %r %r', self, message,
160 #message.get_args_list())
161 args = None
163 # these haven't been checked yet by the match tree
164 if self.sender_unique not in (None, message.get_sender()):
165 #logger.debug('%r: not the desired sender, it was %r and I want '
166 #'%r', self, message.get_sender(), self.sender_unique)
167 return False
168 if self._int_args_match is not None:
169 # extracting args with utf8_strings and byte_arrays is less work
170 args = message.get_args_list(utf8_strings=True, byte_arrays=True)
171 for index, value in self._int_args_match.iteritems():
172 if (index >= len(args)
173 or not isinstance(args[index], UTF8String)
174 or args[index] != value):
175 #logger.debug('%r: not the desired args', self)
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 #logger.debug('%r: not the desired member', self)
181 return False
182 if self._interface not in (None, message.get_interface()):
183 #logger.debug('%r: not the desired interface', self)
184 return False
185 if self._path not in (None, message.get_path()):
186 #logger.debug('%r: not the desired path', self)
187 return False
189 #logger.debug('%r: yes, I want to handle that signal', self)
190 # minor optimization: if we already extracted the args with the right
191 # calling convention to do the args match, don't bother doing so again
192 if args is None or not self._utf8_strings or not self._byte_arrays:
193 args = message.get_args_list(utf8_strings=self._utf8_strings,
194 byte_arrays=self._byte_arrays)
195 #logger.debug('%r: extracted signal arguments', self)
196 kwargs = {}
197 if self._sender_keyword is not None:
198 kwargs[self._sender_keyword] = message.get_sender()
199 if self._destination_keyword is not None:
200 kwargs[self._destination_keyword] = message.get_destination()
201 if self._path_keyword is not None:
202 kwargs[self._path_keyword] = message.get_path()
203 if self._member_keyword is not None:
204 kwargs[self._member_keyword] = message.get_member()
205 if self._interface_keyword is not None:
206 kwargs[self._interface_keyword] = message.get_interface()
207 if self._message_keyword is not None:
208 kwargs[self._message_keyword] = message
209 #logger.debug('%r: calling handler with %r and %r', self, args, kwargs)
210 self._handler(*args, **kwargs)
211 #logger.debug('%r: signal handled', self)
212 return True
214 def remove(self):
215 #logger.debug('%r: removing', self)
216 conn = self._conn_weakref()
217 # do nothing if the connection has already vanished
218 if conn is not None:
219 #logger.debug('%r: removing from connection %r', self, conn)
220 conn.remove_signal_receiver(self, self._member,
221 self._interface, self._sender,
222 self._path,
223 **self._args_match)
226 class Bus(BusImplementation):
227 """A connection to a DBus daemon.
229 One of three possible standard buses, the SESSION, SYSTEM,
230 or STARTER bus
233 TYPE_SESSION = _dbus_bindings.BUS_SESSION
234 """Represents a session bus (same as the global dbus.BUS_SESSION)"""
236 TYPE_SYSTEM = _dbus_bindings.BUS_SYSTEM
237 """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
239 TYPE_STARTER = _dbus_bindings.BUS_STARTER
240 """Represents the bus that started this service by activation (same as
241 the global dbus.BUS_STARTER)"""
243 ProxyObjectClass = ProxyObject
245 START_REPLY_SUCCESS = _dbus_bindings.DBUS_START_REPLY_SUCCESS
246 START_REPLY_ALREADY_RUNNING = _dbus_bindings.DBUS_START_REPLY_ALREADY_RUNNING
248 _shared_instances = {}
250 def __new__(cls, bus_type=TYPE_SESSION, private=False, mainloop=None):
251 """Constructor, returning an existing instance where appropriate.
253 The returned instance is actually always an instance of `SessionBus`,
254 `SystemBus` or `StarterBus`.
256 :Parameters:
257 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
258 Connect to the appropriate bus
259 `private` : bool
260 If true, never return an existing shared instance, but instead
261 return a private connection
262 `mainloop` : dbus.mainloop.NativeMainLoop
263 The main loop to use. The default is to use the default
264 main loop if one has been set up, or raise an exception
265 if none has been.
266 :ToDo:
267 - There is currently no way to connect this class to a custom
268 address.
269 - Some of this functionality should be available on
270 peer-to-peer D-Bus connections too.
271 :Changed: in dbus-python 0.80:
272 converted from a wrapper around a Connection to a Connection
273 subclass.
275 if (not private and bus_type in cls._shared_instances):
276 return cls._shared_instances[bus_type]
278 # this is a bit odd, but we create instances of the subtypes
279 # so we can return the shared instances if someone tries to
280 # construct one of them (otherwise we'd eg try and return an
281 # instance of Bus from __new__ in SessionBus). why are there
282 # three ways to construct this class? we just don't know.
283 if bus_type == cls.TYPE_SESSION:
284 subclass = SessionBus
285 elif bus_type == cls.TYPE_SYSTEM:
286 subclass = SystemBus
287 elif bus_type == cls.TYPE_STARTER:
288 subclass = StarterBus
289 else:
290 raise ValueError('invalid bus_type %s' % bus_type)
292 bus = _dbus_bindings.BusImplementation.__new__(subclass, bus_type,
293 mainloop=mainloop)
295 bus._bus_type = bus_type
296 # _bus_names is used by dbus.service.BusName!
297 bus._bus_names = weakref.WeakValueDictionary()
299 bus._signal_recipients_by_object_path = {}
300 """Map from object path to dict mapping dbus_interface to dict
301 mapping member to list of SignalMatch objects."""
303 bus._signal_sender_matches = {}
304 """Map from sender well-known name to list of match rules for all
305 signal handlers that match on sender well-known name."""
307 bus._signals_lock = thread.allocate_lock()
308 """Lock used to protect signal data structures if doing two
309 removals at the same time (everything else is atomic, thanks to
310 the GIL)"""
312 bus.add_message_filter(bus.__class__._signal_func)
314 if not private:
315 cls._shared_instances[bus_type] = bus
317 return bus
319 def close(self):
320 t = self._bus_type
321 if self.__class__._shared_instances[t] is self:
322 del self.__class__._shared_instances[t]
323 BusImplementation.close(self)
325 def get_connection(self):
326 """(Deprecated - in new code, just use self)
328 Return self, for backwards compatibility with earlier dbus-python
329 versions where Bus was not a subclass of Connection.
331 return self
332 _connection = property(get_connection, None, None,
333 """self._connection == self, for backwards
334 compatibility with earlier dbus-python versions
335 where Bus was not a subclass of Connection.""")
337 def get_session(private=False):
338 """Static method that returns a connection to the session bus.
340 :Parameters:
341 `private` : bool
342 If true, do not return a shared connection.
344 return SessionBus(private=private)
346 get_session = staticmethod(get_session)
348 def get_system(private=False):
349 """Static method that returns a connection to the system bus.
351 :Parameters:
352 `private` : bool
353 If true, do not return a shared connection.
355 return SystemBus(private=private)
357 get_system = staticmethod(get_system)
360 def get_starter(private=False):
361 """Static method that returns a connection to the starter bus.
363 :Parameters:
364 `private` : bool
365 If true, do not return a shared connection.
367 return StarterBus(private=private)
369 get_starter = staticmethod(get_starter)
371 def get_object(self, named_service, object_path, introspect=True,
372 follow_name_owner_changes=False):
373 """Return a local proxy for the given remote object.
375 Method calls on the proxy are translated into method calls on the
376 remote object.
378 :Parameters:
379 `named_service` : str
380 A bus name (either the unique name or a well-known name)
381 of the application owning the object
382 `object_path` : str
383 The object path of the desired object
384 `introspect` : bool
385 If true (default), attempt to introspect the remote
386 object to find out supported methods and their signatures
387 `follow_name_owner_changes` : bool
388 If the object path is a well-known name and this parameter
389 is false (default), resolve the well-known name to the unique
390 name of its current owner and bind to that instead; if the
391 ownership of the well-known name changes in future,
392 keep communicating with the original owner.
393 This is necessary if the D-Bus API used is stateful.
395 If the object path is a well-known name and this parameter
396 is true, whenever the well-known name changes ownership in
397 future, bind to the new owner, if any.
399 If the given object path is a unique name, this parameter
400 has no effect.
402 :Returns: a `dbus.proxies.ProxyObject`
403 :Raises `DBusException`: if resolving the well-known name to a
404 unique name fails
406 if follow_name_owner_changes:
407 self._require_main_loop() # we don't get the signals otherwise
408 return self.ProxyObjectClass(self, named_service, object_path,
409 introspect=introspect,
410 follow_name_owner_changes=follow_name_owner_changes)
412 def add_signal_receiver(self, handler_function,
413 signal_name=None,
414 dbus_interface=None,
415 named_service=None,
416 path=None,
417 **keywords):
418 """Arrange for the given function to be called when a signal matching
419 the parameters is received.
421 :Parameters:
422 `handler_function` : callable
423 The function to be called. Its positional arguments will
424 be the arguments of the signal. By default it will receive
425 no keyword arguments, but see the description of
426 the optional keyword arguments below.
427 `signal_name` : str
428 The signal name; None (the default) matches all names
429 `dbus_interface` : str
430 The D-Bus interface name with which to qualify the signal;
431 None (the default) matches all interface names
432 `named_service` : str
433 A bus name for the sender, which will be resolved to a
434 unique name if it is not already; None (the default) matches
435 any sender
436 `path` : str
437 The object path of the object which must have emitted the
438 signal; None (the default) matches any object path
439 :Keywords:
440 `utf8_strings` : bool
441 If True, the handler function will receive any string
442 arguments as dbus.UTF8String objects (a subclass of str
443 guaranteed to be UTF-8). If False (default) it will receive
444 any string arguments as dbus.String objects (a subclass of
445 unicode).
446 `byte_arrays` : bool
447 If True, the handler function will receive any byte-array
448 arguments as dbus.ByteArray objects (a subclass of str).
449 If False (default) it will receive any byte-array
450 arguments as a dbus.Array of dbus.Byte (subclasses of:
451 a list of ints).
452 `sender_keyword` : str
453 If not None (the default), the handler function will receive
454 the unique name of the sending endpoint as a keyword
455 argument with this name.
456 `destination_keyword` : str
457 If not None (the default), the handler function will receive
458 the bus name of the destination (or None if the signal is a
459 broadcast, as is usual) as a keyword argument with this name.
460 `interface_keyword` : str
461 If not None (the default), the handler function will receive
462 the signal interface as a keyword argument with this name.
463 `member_keyword` : str
464 If not None (the default), the handler function will receive
465 the signal name as a keyword argument with this name.
466 `path_keyword` : str
467 If not None (the default), the handler function will receive
468 the object-path of the sending object as a keyword argument
469 with this name.
470 `message_keyword` : str
471 If not None (the default), the handler function will receive
472 the `dbus.lowlevel.SignalMessage` as a keyword argument with
473 this name.
474 `arg...` : unicode or UTF-8 str
475 If there are additional keyword parameters of the form
476 ``arg``\ *n*, match only signals where the *n*\ th argument
477 is the value given for that keyword parameter. As of this
478 time only string arguments can be matched (in particular,
479 object paths and signatures can't).
481 self._require_main_loop()
483 match = SignalMatch(self, named_service, path, dbus_interface,
484 signal_name, handler_function, **keywords)
485 by_interface = self._signal_recipients_by_object_path.setdefault(path,
487 by_member = by_interface.setdefault(dbus_interface, {})
488 matches = by_member.setdefault(signal_name, [])
489 # The bus daemon is special - its unique-name is org.freedesktop.DBus
490 # rather than starting with :
491 if (named_service is not None and named_service[:1] != ':'
492 and named_service != BUS_DAEMON_NAME):
493 notification = self._signal_sender_matches.setdefault(named_service,
495 if not notification:
496 self.add_match_string(_NAME_OWNER_CHANGE_MATCH % named_service)
497 notification.append(match)
498 # make sure nobody is currently manipulating the list
499 self._signals_lock.acquire()
500 try:
501 matches.append(match)
502 finally:
503 self._signals_lock.release()
504 self.add_match_string(str(match))
505 return match
507 def _iter_easy_matches(self, path, dbus_interface, member):
508 if path is not None:
509 path_keys = (None, path)
510 else:
511 path_keys = (None,)
512 if dbus_interface is not None:
513 interface_keys = (None, dbus_interface)
514 else:
515 interface_keys = (None,)
516 if member is not None:
517 member_keys = (None, member)
518 else:
519 member_keys = (None,)
521 for path in path_keys:
522 by_interface = self._signal_recipients_by_object_path.get(path,
523 None)
524 if by_interface is None:
525 continue
526 for dbus_interface in interface_keys:
527 by_member = by_interface.get(dbus_interface, None)
528 if by_member is None:
529 continue
530 for member in member_keys:
531 matches = by_member.get(member, None)
532 if matches is None:
533 continue
534 for m in matches:
535 yield m
537 def _remove_name_owner_changed_for_match(self, named_service, match):
538 notification = self._signal_sender_matches.get(named_service, False)
539 if notification:
540 try:
541 notification.remove(match)
542 except LookupError:
543 pass
544 if not notification:
545 self.remove_match_string(_NAME_OWNER_CHANGE_MATCH
546 % named_service)
548 def remove_signal_receiver(self, handler_or_match,
549 signal_name=None,
550 dbus_interface=None,
551 named_service=None,
552 path=None,
553 **keywords):
554 #logger.debug('%r: removing signal receiver %r: member=%s, '
555 #'iface=%s, sender=%s, path=%s, kwargs=%r',
556 #self, handler_or_match, signal_name,
557 #dbus_interface, named_service, path, keywords)
558 #logger.debug('%r', self._signal_recipients_by_object_path)
559 by_interface = self._signal_recipients_by_object_path.get(path, None)
560 if by_interface is None:
561 return
562 by_member = by_interface.get(dbus_interface, None)
563 if by_member is None:
564 return
565 matches = by_member.get(signal_name, None)
566 if matches is None:
567 return
568 self._signals_lock.acquire()
569 #logger.debug(matches)
570 try:
571 new = []
572 for match in matches:
573 if (handler_or_match is match
574 or match.matches_removal_spec(named_service,
575 path,
576 dbus_interface,
577 signal_name,
578 handler_or_match,
579 **keywords)):
580 #logger.debug('Removing match string: %s', match)
581 self.remove_match_string(str(match))
582 self._remove_name_owner_changed_for_match(named_service,
583 match)
584 else:
585 new.append(match)
586 by_member[signal_name] = new
587 finally:
588 self._signals_lock.release()
590 def _signal_func(self, message):
591 """D-Bus filter function. Handle signals by dispatching to Python
592 callbacks kept in the match-rule tree.
595 #logger.debug('Incoming message %r with args %r', message,
596 #message.get_args_list())
598 if (message.get_type() != _dbus_bindings.MESSAGE_TYPE_SIGNAL):
599 return _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
601 # If it's NameOwnerChanged, we'll need to update our
602 # sender well-known name -> sender unique name mappings
603 if (message.is_signal(BUS_DAEMON_NAME, 'NameOwnerChanged')
604 and message.has_path(BUS_DAEMON_PATH)):
605 name, unused, new = message.get_args_list()
606 for match in self._signal_sender_matches.get(name, (None,))[1:]:
607 match.sender_unique = new
609 # See if anyone else wants to know
610 dbus_interface = message.get_interface()
611 path = message.get_path()
612 signal_name = message.get_member()
614 ret = _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
615 for match in self._iter_easy_matches(path, dbus_interface,
616 signal_name):
617 if match.maybe_handle_message(message):
618 ret = _dbus_bindings.HANDLER_RESULT_HANDLED
619 return ret
621 def __repr__(self):
622 if self._bus_type == self.TYPE_SESSION:
623 name = 'SESSION'
624 elif self._bus_type == self.TYPE_SYSTEM:
625 name = 'SYSTEM'
626 elif self._bus_type == self.TYPE_STARTER:
627 name = 'STARTER'
628 else:
629 raise AssertionError('Unable to represent unknown bus type.')
631 return '<dbus.Bus on %s at %#x>' % (name, id(self))
632 __str__ = __repr__
635 # FIXME: Drop the subclasses here? I can't think why we'd ever want
636 # polymorphism
637 class SystemBus(Bus):
638 """The system-wide message bus."""
639 def __new__(cls, private=False, mainloop=None):
640 """Return a connection to the system bus.
642 :Parameters:
643 `private` : bool
644 If true, never return an existing shared instance, but instead
645 return a private connection.
646 `mainloop` : dbus.mainloop.NativeMainLoop
647 The main loop to use. The default is to use the default
648 main loop if one has been set up, or raise an exception
649 if none has been.
651 return Bus.__new__(cls, Bus.TYPE_SYSTEM, mainloop=mainloop,
652 private=private)
654 class SessionBus(Bus):
655 """The session (current login) message bus."""
656 def __new__(cls, private=False, mainloop=None):
657 """Return a connection to the session bus.
659 :Parameters:
660 `private` : bool
661 If true, never return an existing shared instance, but instead
662 return a private connection.
663 `mainloop` : dbus.mainloop.NativeMainLoop
664 The main loop to use. The default is to use the default
665 main loop if one has been set up, or raise an exception
666 if none has been.
668 return Bus.__new__(cls, Bus.TYPE_SESSION, private=private,
669 mainloop=mainloop)
671 class StarterBus(Bus):
672 """The bus that activated this process (only valid if
673 this process was launched by DBus activation).
675 def __new__(cls, private=False, mainloop=None):
676 """Return a connection to the bus that activated this process.
678 :Parameters:
679 `private` : bool
680 If true, never return an existing shared instance, but instead
681 return a private connection.
682 `mainloop` : dbus.mainloop.NativeMainLoop
683 The main loop to use. The default is to use the default
684 main loop if one has been set up, or raise an exception
685 if none has been.
687 return Bus.__new__(cls, Bus.TYPE_STARTER, private=private,
688 mainloop=mainloop)
690 class Interface:
691 """An interface into a remote object.
693 An Interface can be used to wrap ProxyObjects
694 so that calls can be routed to their correct
695 dbus interface
698 def __init__(self, object, dbus_interface):
699 """Construct a proxy for the given interface on the given object.
701 :Parameters:
702 `object` : `dbus.proxies.ProxyObject`
703 The remote object
704 `dbus_interface` : str
705 An interface the `object` implements
707 self._obj = object
708 self._dbus_interface = dbus_interface
710 __dbus_object_path__ = property (lambda self: self._obj.__dbus_object_path__,
711 None, None,
712 "The D-Bus object path of the "
713 "underlying object")
715 def connect_to_signal(self, signal_name, handler_function, dbus_interface = None, **keywords):
716 """Arrange for a function to be called when the given signal is
717 emitted.
719 :Parameters:
720 `signal_name` : str
721 The name of the signal
722 `handler_function` : callable
723 A function to be called when the signal is emitted by the
724 remote object. It will receive the signal's arguments
725 as its positional arguments.
726 `dbus_interface` : str
727 Optional interface with which to qualify the signal name.
728 The default is to use the interface this Interface represents.
729 (FIXME: deprecate this? Violates least astonishment)
730 :Keywords:
731 `utf8_strings` : bool
732 If True, the handler function will receive any string
733 arguments as dbus.UTF8String objects (a subclass of str
734 guaranteed to be UTF-8). If False (default) it will receive
735 any string arguments as dbus.String objects (a subclass of
736 unicode).
737 `byte_arrays` : bool
738 If True, the handler function will receive any byte-array
739 arguments as dbus.ByteArray objects (a subclass of str).
740 If False (default) it will receive any byte-array
741 arguments as a dbus.Array of dbus.Byte (subclasses of:
742 a list of ints).
743 `sender_keyword` : str
744 If not None (the default), the handler function will receive
745 the unique name of the sending endpoint as a keyword
746 argument with this name
747 `destination_keyword` : str
748 If not None (the default), the handler function will receive
749 the bus name of the destination (or None if the signal is a
750 broadcast, as is usual) as a keyword argument with this name.
751 `interface_keyword` : str
752 If not None (the default), the handler function will receive
753 the signal interface as a keyword argument with this name.
754 `member_keyword` : str
755 If not None (the default), the handler function will receive
756 the signal name as a keyword argument with this name.
757 `path_keyword` : str
758 If not None (the default), the handler function will receive
759 the object-path of the sending object as a keyword argument
760 with this name
761 `message_keyword` : str
762 If not None (the default), the handler function will receive
763 the `dbus.lowlevel.SignalMessage` as a keyword argument with
764 this name.
765 `arg...` : unicode or UTF-8 str
766 If there are additional keyword parameters of the form
767 ``arg``\ *n*, only call the handler_function for signals
768 where the *n*\ th argument is the value given for that
769 keyword parameter. As of this time only string arguments
770 can be matched.
772 if not dbus_interface:
773 dbus_interface = self._dbus_interface
775 return self._obj.connect_to_signal(signal_name, handler_function, dbus_interface, **keywords)
777 def __getattr__(self, member, **keywords):
778 # FIXME: this syntax is bizarre.
779 if (keywords.has_key('dbus_interface')):
780 _dbus_interface = keywords['dbus_interface']
781 else:
782 _dbus_interface = self._dbus_interface
784 # I have no idea what's going on here. -smcv
785 if member == '__call__':
786 return object.__call__
787 else:
788 ret = self._obj.__getattr__(member, dbus_interface=_dbus_interface)
789 return ret
791 def get_dbus_method(self, member, dbus_interface=None):
792 """Return a proxy method representing the given D-Bus method. The
793 returned proxy method can be called in the usual way. For instance, ::
795 iface.get_dbus_method("Foo")(123)
797 is equivalent to::
799 iface.Foo(123)
801 or even::
803 getattr(iface, "Foo")(123)
805 However, using `get_dbus_method` is the only way to call D-Bus
806 methods with certain awkward names - if the author of a service
807 implements a method called ``connect_to_signal`` or even
808 ``__getattr__``, you'll need to use `get_dbus_method` to call them.
810 For services which follow the D-Bus convention of CamelCaseMethodNames
811 this won't be a problem.
813 if dbus_interface is None:
814 dbus_interface = self._dbus_interface
815 return self._obj.get_dbus_method(member, dbus_interface=dbus_interface)
817 def __repr__(self):
818 return '<Interface %r implementing %r at %#x>'%(
819 self._obj, self._dbus_interface, id(self))
820 __str__ = __repr__
823 _dbus_bindings_warning = DeprecationWarning("""\
824 The dbus_bindings module is deprecated and will go away soon.
826 dbus-python 0.80 provides only a partial emulation of the old
827 dbus_bindings, which was never meant to be public API.
829 Most uses of dbus_bindings are applications catching the exception
830 dbus.dbus_bindings.DBusException. You should use dbus.DBusException
831 instead (this is compatible with all dbus-python versions since 0.40.2).
833 If you need additional public API, please contact the maintainers via
834 <dbus@lists.freedesktop.org>.
835 """)
837 if 'DBUS_PYTHON_NO_DEPRECATED' not in os.environ:
839 class _DBusBindingsEmulation:
840 """A partial emulation of the dbus_bindings module."""
841 _module = None
842 def __str__(self):
843 return '_DBusBindingsEmulation()'
844 def __repr__(self):
845 return '_DBusBindingsEmulation()'
846 def __getattr__(self, attr):
847 if self._module is None:
848 from warnings import warn as _warn
849 _warn(_dbus_bindings_warning, DeprecationWarning, stacklevel=2)
851 import dbus.dbus_bindings as m
852 self._module = m
853 return getattr(self._module, attr)
855 dbus_bindings = _DBusBindingsEmulation()
856 """Deprecated, don't use."""