Remove BusImplementation, removing its remaining functionality to Connection.
[dbus-python-phuang.git] / dbus / _dbus.py
blob68121275e99d32190aa1f75b50683012c2e6e45e
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'
29 import _dbus_bindings
30 UTF8String = _dbus_bindings.UTF8String
31 DBusException = _dbus_bindings.DBusException
33 import os
34 import logging
35 import sys
36 import weakref
37 from traceback import print_exc
39 from dbus.bus import _BusDaemonMixin
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')
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,
54 BUS_DAEMON_PATH))
55 """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
56 messages"""
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,
72 **kwargs):
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)
83 self._sender = sender
84 self._interface = dbus_interface
85 self._member = member
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)
91 else:
92 self.sender_unique = sender
93 self._utf8_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
103 if not kwargs:
104 self._int_args_match = None
105 else:
106 self._int_args_match = {}
107 for kwarg in kwargs:
108 if not kwarg.startswith('arg'):
109 raise TypeError('SignalMatch: unknown keyword argument %s'
110 % kwarg)
111 try:
112 index = int(kwarg[3:])
113 except ValueError:
114 raise TypeError('SignalMatch: unknown keyword argument %s'
115 % kwarg)
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)
137 def __str__(self):
138 return self._rule
140 def __repr__(self):
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):
147 return False
148 if sender != self._sender:
149 return False
150 if object_path != self._path:
151 return False
152 if dbus_interface != self._interface:
153 return False
154 if member != self._member:
155 return False
156 if kwargs != self._args_match:
157 return False
158 return True
160 def maybe_handle_message(self, message):
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 return False
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):
173 return False
175 # these have likely already been checked by the match tree
176 if self._member not in (None, message.get_member()):
177 return False
178 if self._interface not in (None, message.get_interface()):
179 return False
180 if self._path not in (None, message.get_path()):
181 return False
183 try:
184 # minor optimization: if we already extracted the args with the
185 # right calling convention to do the args match, don't bother
186 # doing so again
187 if args is None or not self._utf8_strings or not self._byte_arrays:
188 args = message.get_args_list(utf8_strings=self._utf8_strings,
189 byte_arrays=self._byte_arrays)
190 kwargs = {}
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)
204 except:
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')
208 print_exc()
210 return True
212 def remove(self):
213 #logger.debug('%r: removing', self)
214 conn = self._conn_weakref()
215 # do nothing if the connection has already vanished
216 if conn is not None:
217 #logger.debug('%r: removing from connection %r', self, conn)
218 conn.remove_signal_receiver(self, self._member,
219 self._interface, self._sender,
220 self._path,
221 **self._args_match)
224 class Bus(_dbus_bindings.Connection, _BusDaemonMixin):
225 """A connection to a DBus daemon.
227 One of three possible standard buses, the SESSION, SYSTEM,
228 or STARTER bus
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`.
254 :Parameters:
255 `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
256 Connect to the appropriate bus
257 `private` : bool
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
263 if none has been.
264 :ToDo:
265 - There is currently no way to connect this class to a custom
266 address.
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
271 subclass.
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:
284 subclass = SystemBus
285 elif bus_type == cls.TYPE_STARTER:
286 subclass = StarterBus
287 else:
288 raise ValueError('invalid bus_type %s' % bus_type)
290 bus = subclass._new_for_bus(bus_type, mainloop=mainloop)
292 bus._bus_type = bus_type
293 # _bus_names is used by dbus.service.BusName!
294 bus._bus_names = weakref.WeakValueDictionary()
296 bus._signal_recipients_by_object_path = {}
297 """Map from object path to dict mapping dbus_interface to dict
298 mapping member to list of SignalMatch objects."""
300 bus._signal_sender_matches = {}
301 """Map from sender well-known name to list of match rules for all
302 signal handlers that match on sender well-known name."""
304 bus._signals_lock = thread.allocate_lock()
305 """Lock used to protect signal data structures if doing two
306 removals at the same time (everything else is atomic, thanks to
307 the GIL)"""
309 bus.add_message_filter(bus.__class__._signal_func)
311 if not private:
312 cls._shared_instances[bus_type] = bus
314 return bus
316 def close(self):
317 t = self._bus_type
318 if self.__class__._shared_instances[t] is self:
319 del self.__class__._shared_instances[t]
320 _dbus_bindings.Connection.close(self)
322 def get_connection(self):
323 """(Deprecated - in new code, just use self)
325 Return self, for backwards compatibility with earlier dbus-python
326 versions where Bus was not a subclass of Connection.
328 return self
329 _connection = property(get_connection, None, None,
330 """self._connection == self, for backwards
331 compatibility with earlier dbus-python versions
332 where Bus was not a subclass of Connection.""")
334 def get_session(private=False):
335 """Static method that returns a connection to the session bus.
337 :Parameters:
338 `private` : bool
339 If true, do not return a shared connection.
341 return SessionBus(private=private)
343 get_session = staticmethod(get_session)
345 def get_system(private=False):
346 """Static method that returns a connection to the system bus.
348 :Parameters:
349 `private` : bool
350 If true, do not return a shared connection.
352 return SystemBus(private=private)
354 get_system = staticmethod(get_system)
357 def get_starter(private=False):
358 """Static method that returns a connection to the starter bus.
360 :Parameters:
361 `private` : bool
362 If true, do not return a shared connection.
364 return StarterBus(private=private)
366 get_starter = staticmethod(get_starter)
368 def get_object(self, named_service, object_path, introspect=True,
369 follow_name_owner_changes=False):
370 """Return a local proxy for the given remote object.
372 Method calls on the proxy are translated into method calls on the
373 remote object.
375 :Parameters:
376 `named_service` : str
377 A bus name (either the unique name or a well-known name)
378 of the application owning the object
379 `object_path` : str
380 The object path of the desired object
381 `introspect` : bool
382 If true (default), attempt to introspect the remote
383 object to find out supported methods and their signatures
384 `follow_name_owner_changes` : bool
385 If the object path is a well-known name and this parameter
386 is false (default), resolve the well-known name to the unique
387 name of its current owner and bind to that instead; if the
388 ownership of the well-known name changes in future,
389 keep communicating with the original owner.
390 This is necessary if the D-Bus API used is stateful.
392 If the object path is a well-known name and this parameter
393 is true, whenever the well-known name changes ownership in
394 future, bind to the new owner, if any.
396 If the given object path is a unique name, this parameter
397 has no effect.
399 :Returns: a `dbus.proxies.ProxyObject`
400 :Raises `DBusException`: if resolving the well-known name to a
401 unique name fails
403 if follow_name_owner_changes:
404 self._require_main_loop() # we don't get the signals otherwise
405 return self.ProxyObjectClass(self, named_service, object_path,
406 introspect=introspect,
407 follow_name_owner_changes=follow_name_owner_changes)
409 def add_signal_receiver(self, handler_function,
410 signal_name=None,
411 dbus_interface=None,
412 named_service=None,
413 path=None,
414 **keywords):
415 """Arrange for the given function to be called when a signal matching
416 the parameters is received.
418 :Parameters:
419 `handler_function` : callable
420 The function to be called. Its positional arguments will
421 be the arguments of the signal. By default it will receive
422 no keyword arguments, but see the description of
423 the optional keyword arguments below.
424 `signal_name` : str
425 The signal name; None (the default) matches all names
426 `dbus_interface` : str
427 The D-Bus interface name with which to qualify the signal;
428 None (the default) matches all interface names
429 `named_service` : str
430 A bus name for the sender, which will be resolved to a
431 unique name if it is not already; None (the default) matches
432 any sender
433 `path` : str
434 The object path of the object which must have emitted the
435 signal; None (the default) matches any object path
436 :Keywords:
437 `utf8_strings` : bool
438 If True, the handler function will receive any string
439 arguments as dbus.UTF8String objects (a subclass of str
440 guaranteed to be UTF-8). If False (default) it will receive
441 any string arguments as dbus.String objects (a subclass of
442 unicode).
443 `byte_arrays` : bool
444 If True, the handler function will receive any byte-array
445 arguments as dbus.ByteArray objects (a subclass of str).
446 If False (default) it will receive any byte-array
447 arguments as a dbus.Array of dbus.Byte (subclasses of:
448 a list of ints).
449 `sender_keyword` : str
450 If not None (the default), the handler function will receive
451 the unique name of the sending endpoint as a keyword
452 argument with this name.
453 `destination_keyword` : str
454 If not None (the default), the handler function will receive
455 the bus name of the destination (or None if the signal is a
456 broadcast, as is usual) as a keyword argument with this name.
457 `interface_keyword` : str
458 If not None (the default), the handler function will receive
459 the signal interface as a keyword argument with this name.
460 `member_keyword` : str
461 If not None (the default), the handler function will receive
462 the signal name as a keyword argument with this name.
463 `path_keyword` : str
464 If not None (the default), the handler function will receive
465 the object-path of the sending object as a keyword argument
466 with this name.
467 `message_keyword` : str
468 If not None (the default), the handler function will receive
469 the `dbus.lowlevel.SignalMessage` as a keyword argument with
470 this name.
471 `arg...` : unicode or UTF-8 str
472 If there are additional keyword parameters of the form
473 ``arg``\ *n*, match only signals where the *n*\ th argument
474 is the value given for that keyword parameter. As of this
475 time only string arguments can be matched (in particular,
476 object paths and signatures can't).
478 self._require_main_loop()
480 match = SignalMatch(self, named_service, path, dbus_interface,
481 signal_name, handler_function, **keywords)
482 by_interface = self._signal_recipients_by_object_path.setdefault(path,
484 by_member = by_interface.setdefault(dbus_interface, {})
485 matches = by_member.setdefault(signal_name, [])
486 # The bus daemon is special - its unique-name is org.freedesktop.DBus
487 # rather than starting with :
488 if (named_service is not None and named_service[:1] != ':'
489 and named_service != BUS_DAEMON_NAME):
490 notification = self._signal_sender_matches.setdefault(named_service,
492 if not notification:
493 self.add_match_string(_NAME_OWNER_CHANGE_MATCH % named_service)
494 notification.append(match)
495 # make sure nobody is currently manipulating the list
496 self._signals_lock.acquire()
497 try:
498 matches.append(match)
499 finally:
500 self._signals_lock.release()
501 self.add_match_string(str(match))
502 return match
504 def _iter_easy_matches(self, path, dbus_interface, member):
505 if path is not None:
506 path_keys = (None, path)
507 else:
508 path_keys = (None,)
509 if dbus_interface is not None:
510 interface_keys = (None, dbus_interface)
511 else:
512 interface_keys = (None,)
513 if member is not None:
514 member_keys = (None, member)
515 else:
516 member_keys = (None,)
518 for path in path_keys:
519 by_interface = self._signal_recipients_by_object_path.get(path,
520 None)
521 if by_interface is None:
522 continue
523 for dbus_interface in interface_keys:
524 by_member = by_interface.get(dbus_interface, None)
525 if by_member is None:
526 continue
527 for member in member_keys:
528 matches = by_member.get(member, None)
529 if matches is None:
530 continue
531 for m in matches:
532 yield m
534 def _remove_name_owner_changed_for_match(self, named_service, match):
535 # The signals lock must be held.
536 notification = self._signal_sender_matches.get(named_service, False)
537 if notification:
538 try:
539 notification.remove(match)
540 except LookupError:
541 pass
542 if not notification:
543 self.remove_match_string(_NAME_OWNER_CHANGE_MATCH
544 % named_service)
546 def remove_signal_receiver(self, handler_or_match,
547 signal_name=None,
548 dbus_interface=None,
549 named_service=None,
550 path=None,
551 **keywords):
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:
559 return
560 by_member = by_interface.get(dbus_interface, None)
561 if by_member is None:
562 return
563 matches = by_member.get(signal_name, None)
564 if matches is None:
565 return
566 self._signals_lock.acquire()
567 #logger.debug(matches)
568 try:
569 new = []
570 for match in matches:
571 if (handler_or_match is match
572 or match.matches_removal_spec(named_service,
573 path,
574 dbus_interface,
575 signal_name,
576 handler_or_match,
577 **keywords)):
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,
581 match)
582 else:
583 new.append(match)
584 by_member[signal_name] = new
585 finally:
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,
615 signal_name):
616 if match.maybe_handle_message(message):
617 ret = _dbus_bindings.HANDLER_RESULT_HANDLED
618 return ret
620 def __repr__(self):
621 if self._bus_type == self.TYPE_SESSION:
622 name = 'SESSION'
623 elif self._bus_type == self.TYPE_SYSTEM:
624 name = 'SYSTEM'
625 elif self._bus_type == self.TYPE_STARTER:
626 name = 'STARTER'
627 else:
628 raise AssertionError('Unable to represent unknown bus type.')
630 return '<dbus.Bus on %s at %#x>' % (name, id(self))
631 __str__ = __repr__
634 # FIXME: Drop the subclasses here? I can't think why we'd ever want
635 # polymorphism
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.
641 :Parameters:
642 `private` : bool
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
648 if none has been.
650 return Bus.__new__(cls, Bus.TYPE_SYSTEM, mainloop=mainloop,
651 private=private)
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.
658 :Parameters:
659 `private` : bool
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
665 if none has been.
667 return Bus.__new__(cls, Bus.TYPE_SESSION, private=private,
668 mainloop=mainloop)
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.
677 :Parameters:
678 `private` : bool
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
684 if none has been.
686 return Bus.__new__(cls, Bus.TYPE_STARTER, private=private,
687 mainloop=mainloop)
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>.
702 """)
704 if 'DBUS_PYTHON_NO_DEPRECATED' not in os.environ:
706 class _DBusBindingsEmulation:
707 """A partial emulation of the dbus_bindings module."""
708 _module = None
709 def __str__(self):
710 return '_DBusBindingsEmulation()'
711 def __repr__(self):
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
719 self._module = m
720 return getattr(self._module, attr)
722 dbus_bindings = _DBusBindingsEmulation()
723 """Deprecated, don't use."""