1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 __all__ = ('Connection', 'SignalMatch')
24 __docformat__ = 'reStructuredText'
25
26 import logging
27 try:
28 import thread
29 except ImportError:
30 import dummy_thread as thread
31 import weakref
32
33 from _dbus_bindings import Connection as _Connection, \
34 LOCAL_PATH, LOCAL_IFACE, \
35 validate_interface_name, validate_member_name,\
36 validate_bus_name, validate_object_path,\
37 validate_error_name, \
38 UTF8String
39 from dbus.exceptions import DBusException
40 from dbus.lowlevel import ErrorMessage, MethodCallMessage, SignalMessage, \
41 MethodReturnMessage, HANDLER_RESULT_NOT_YET_HANDLED
42 from dbus.proxies import ProxyObject
43
44
45 _logger = logging.getLogger('dbus.connection')
46
47
48 -def _noop(*args, **kwargs):
50
51
53 __slots__ = ('_sender_name_owner', '_member', '_interface', '_sender',
54 '_path', '_handler', '_args_match', '_rule',
55 '_utf8_strings', '_byte_arrays', '_conn_weakref',
56 '_destination_keyword', '_interface_keyword',
57 '_message_keyword', '_member_keyword',
58 '_sender_keyword', '_path_keyword', '_int_args_match')
59
60 - def __init__(self, conn, sender, object_path, dbus_interface,
61 member, handler, utf8_strings=False, byte_arrays=False,
62 sender_keyword=None, path_keyword=None,
63 interface_keyword=None, member_keyword=None,
64 message_keyword=None, destination_keyword=None,
65 **kwargs):
114
116 """SignalMatch objects are compared by identity."""
117 return hash(id(self))
118
120 """SignalMatch objects are compared by identity."""
121 return self is other
122
124 """SignalMatch objects are compared by identity."""
125 return self is not other
126
127 sender = property(lambda self: self._sender)
128
147
149 return ('<%s at %x "%s" on conn %r>'
150 % (self.__class__, id(self), self._rule, self._conn_weakref()))
151
154
155 - def matches_removal_spec(self, sender, object_path,
156 dbus_interface, member, handler, **kwargs):
170
172 args = None
173
174
175 if self._sender_name_owner not in (None, message.get_sender()):
176 return False
177 if self._int_args_match is not None:
178
179 args = message.get_args_list(utf8_strings=True, byte_arrays=True)
180 for index, value in self._int_args_match.iteritems():
181 if (index >= len(args)
182 or not isinstance(args[index], UTF8String)
183 or args[index] != value):
184 return False
185
186
187 if self._member not in (None, message.get_member()):
188 return False
189 if self._interface not in (None, message.get_interface()):
190 return False
191 if self._path not in (None, message.get_path()):
192 return False
193
194 try:
195
196
197
198 if args is None or not self._utf8_strings or not self._byte_arrays:
199 args = message.get_args_list(utf8_strings=self._utf8_strings,
200 byte_arrays=self._byte_arrays)
201 kwargs = {}
202 if self._sender_keyword is not None:
203 kwargs[self._sender_keyword] = message.get_sender()
204 if self._destination_keyword is not None:
205 kwargs[self._destination_keyword] = message.get_destination()
206 if self._path_keyword is not None:
207 kwargs[self._path_keyword] = message.get_path()
208 if self._member_keyword is not None:
209 kwargs[self._member_keyword] = message.get_member()
210 if self._interface_keyword is not None:
211 kwargs[self._interface_keyword] = message.get_interface()
212 if self._message_keyword is not None:
213 kwargs[self._message_keyword] = message
214 self._handler(*args, **kwargs)
215 except:
216
217 logging.basicConfig()
218 _logger.error('Exception in handler for D-Bus signal:', exc_info=1)
219
220 return True
221
230
231
233 """A connection to another application. In this base class there is
234 assumed to be no bus daemon.
235
236 :Since: 0.81.0
237 """
238
239 ProxyObjectClass = ProxyObject
240
242
243
244 if not hasattr(self, '_dbus_Connection_initialized'):
245 self._dbus_Connection_initialized = 1
246
247 self.__call_on_disconnection = []
248
249 self._signal_recipients_by_object_path = {}
250 """Map from object path to dict mapping dbus_interface to dict
251 mapping member to list of SignalMatch objects."""
252
253 self._signals_lock = thread.allocate_lock()
254 """Lock used to protect signal data structures"""
255
256 self.add_message_filter(self.__class__._signal_func)
257
259 """Return the unique name for the given bus name, activating it
260 if necessary and possible.
261
262 If the name is already unique or this connection is not to a
263 bus daemon, just return it.
264
265 :Returns: a bus name. If the given `bus_name` exists, the returned
266 name identifies its current owner; otherwise the returned name
267 does not exist.
268 :Raises DBusException: if the implementation has failed
269 to activate the given bus name.
270 :Since: 0.81.0
271 """
272 return bus_name
273
274 - def get_object(self, bus_name=None, object_path=None, introspect=True,
275 **kwargs):
276 """Return a local proxy for the given remote object.
277
278 Method calls on the proxy are translated into method calls on the
279 remote object.
280
281 :Parameters:
282 `bus_name` : str
283 A bus name (either the unique name or a well-known name)
284 of the application owning the object. The keyword argument
285 named_service is a deprecated alias for this.
286 `object_path` : str
287 The object path of the desired object
288 `introspect` : bool
289 If true (default), attempt to introspect the remote
290 object to find out supported methods and their signatures
291
292 :Returns: a `dbus.proxies.ProxyObject`
293 """
294 named_service = kwargs.pop('named_service', None)
295 if named_service is not None:
296 if bus_name is not None:
297 raise TypeError('bus_name and named_service cannot both '
298 'be specified')
299 from warnings import warn
300 warn('Passing the named_service parameter to get_object by name '
301 'is deprecated: please use positional parameters',
302 DeprecationWarning, stacklevel=2)
303 bus_name = named_service
304 if kwargs:
305 raise TypeError('get_object does not take these keyword '
306 'arguments: %s' % ', '.join(kwargs.iterkeys()))
307
308 return self.ProxyObjectClass(self, bus_name, object_path,
309 introspect=introspect)
310
311 - def add_signal_receiver(self, handler_function,
312 signal_name=None,
313 dbus_interface=None,
314 bus_name=None,
315 path=None,
316 **keywords):
317 """Arrange for the given function to be called when a signal matching
318 the parameters is received.
319
320 :Parameters:
321 `handler_function` : callable
322 The function to be called. Its positional arguments will
323 be the arguments of the signal. By default it will receive
324 no keyword arguments, but see the description of
325 the optional keyword arguments below.
326 `signal_name` : str
327 The signal name; None (the default) matches all names
328 `dbus_interface` : str
329 The D-Bus interface name with which to qualify the signal;
330 None (the default) matches all interface names
331 `bus_name` : str
332 A bus name for the sender, which will be resolved to a
333 unique name if it is not already; None (the default) matches
334 any sender.
335 `path` : str
336 The object path of the object which must have emitted the
337 signal; None (the default) matches any object path
338 :Keywords:
339 `utf8_strings` : bool
340 If True, the handler function will receive any string
341 arguments as dbus.UTF8String objects (a subclass of str
342 guaranteed to be UTF-8). If False (default) it will receive
343 any string arguments as dbus.String objects (a subclass of
344 unicode).
345 `byte_arrays` : bool
346 If True, the handler function will receive any byte-array
347 arguments as dbus.ByteArray objects (a subclass of str).
348 If False (default) it will receive any byte-array
349 arguments as a dbus.Array of dbus.Byte (subclasses of:
350 a list of ints).
351 `sender_keyword` : str
352 If not None (the default), the handler function will receive
353 the unique name of the sending endpoint as a keyword
354 argument with this name.
355 `destination_keyword` : str
356 If not None (the default), the handler function will receive
357 the bus name of the destination (or None if the signal is a
358 broadcast, as is usual) as a keyword argument with this name.
359 `interface_keyword` : str
360 If not None (the default), the handler function will receive
361 the signal interface as a keyword argument with this name.
362 `member_keyword` : str
363 If not None (the default), the handler function will receive
364 the signal name as a keyword argument with this name.
365 `path_keyword` : str
366 If not None (the default), the handler function will receive
367 the object-path of the sending object as a keyword argument
368 with this name.
369 `message_keyword` : str
370 If not None (the default), the handler function will receive
371 the `dbus.lowlevel.SignalMessage` as a keyword argument with
372 this name.
373 `arg...` : unicode or UTF-8 str
374 If there are additional keyword parameters of the form
375 ``arg``\ *n*, match only signals where the *n*\ th argument
376 is the value given for that keyword parameter. As of this
377 time only string arguments can be matched (in particular,
378 object paths and signatures can't).
379 `named_service` : str
380 A deprecated alias for `bus_name`.
381 """
382 self._require_main_loop()
383
384 named_service = keywords.pop('named_service', None)
385 if named_service is not None:
386 if bus_name is not None:
387 raise TypeError('bus_name and named_service cannot both be '
388 'specified')
389 bus_name = named_service
390 from warnings import warn
391 warn('Passing the named_service parameter to add_signal_receiver '
392 'by name is deprecated: please use positional parameters',
393 DeprecationWarning, stacklevel=2)
394
395 match = SignalMatch(self, bus_name, path, dbus_interface,
396 signal_name, handler_function, **keywords)
397
398 self._signals_lock.acquire()
399 try:
400 by_interface = self._signal_recipients_by_object_path.setdefault(
401 path, {})
402 by_member = by_interface.setdefault(dbus_interface, {})
403 matches = by_member.setdefault(signal_name, [])
404
405 matches.append(match)
406 finally:
407 self._signals_lock.release()
408
409 return match
410
412 if path is not None:
413 path_keys = (None, path)
414 else:
415 path_keys = (None,)
416 if dbus_interface is not None:
417 interface_keys = (None, dbus_interface)
418 else:
419 interface_keys = (None,)
420 if member is not None:
421 member_keys = (None, member)
422 else:
423 member_keys = (None,)
424
425 for path in path_keys:
426 by_interface = self._signal_recipients_by_object_path.get(path,
427 None)
428 if by_interface is None:
429 continue
430 for dbus_interface in interface_keys:
431 by_member = by_interface.get(dbus_interface, None)
432 if by_member is None:
433 continue
434 for member in member_keys:
435 matches = by_member.get(member, None)
436 if matches is None:
437 continue
438 for m in matches:
439 yield m
440
441 - def remove_signal_receiver(self, handler_or_match,
442 signal_name=None,
443 dbus_interface=None,
444 bus_name=None,
445 path=None,
446 **keywords):
447 named_service = keywords.pop('named_service', None)
448 if named_service is not None:
449 if bus_name is not None:
450 raise TypeError('bus_name and named_service cannot both be '
451 'specified')
452 bus_name = named_service
453 from warnings import warn
454 warn('Passing the named_service parameter to '
455 'remove_signal_receiver by name is deprecated: please use '
456 'positional parameters',
457 DeprecationWarning, stacklevel=2)
458
459 new = []
460 deletions = []
461 self._signals_lock.acquire()
462 try:
463 by_interface = self._signal_recipients_by_object_path.get(path,
464 None)
465 if by_interface is None:
466 return
467 by_member = by_interface.get(dbus_interface, None)
468 if by_member is None:
469 return
470 matches = by_member.get(signal_name, None)
471 if matches is None:
472 return
473
474 for match in matches:
475 if (handler_or_match is match
476 or match.matches_removal_spec(bus_name,
477 path,
478 dbus_interface,
479 signal_name,
480 handler_or_match,
481 **keywords)):
482 deletions.append(match)
483 else:
484 new.append(match)
485 by_member[signal_name] = new
486 finally:
487 self._signals_lock.release()
488
489 for match in deletions:
490 self._clean_up_signal_match(match)
491
495
525
526 - def call_async(self, bus_name, object_path, dbus_interface, method,
527 signature, args, reply_handler, error_handler,
528 timeout=-1.0, utf8_strings=False, byte_arrays=False,
529 require_main_loop=True):
530 """Call the given method, asynchronously.
531
532 If the reply_handler is None, successful replies will be ignored.
533 If the error_handler is None, failures will be ignored. If both
534 are None, the implementation may request that no reply is sent.
535
536 :Returns: The dbus.lowlevel.PendingCall.
537 :Since: 0.81.0
538 """
539 if object_path == LOCAL_PATH:
540 raise DBusException('Methods may not be called on the reserved '
541 'path %s' % LOCAL_PATH)
542 if dbus_interface == LOCAL_IFACE:
543 raise DBusException('Methods may not be called on the reserved '
544 'interface %s' % LOCAL_IFACE)
545
546
547 get_args_opts = {'utf8_strings': utf8_strings,
548 'byte_arrays': byte_arrays}
549
550 message = MethodCallMessage(destination=bus_name,
551 path=object_path,
552 interface=dbus_interface,
553 method=method)
554
555 try:
556 message.append(signature=signature, *args)
557 except Exception, e:
558 logging.basicConfig()
559 _logger.error('Unable to set arguments %r according to '
560 'signature %r: %s: %s',
561 args, signature, e.__class__, e)
562 raise
563
564 if reply_handler is None and error_handler is None:
565
566 self.send_message(message)
567 return
568
569 if reply_handler is None:
570 reply_handler = _noop
571 if error_handler is None:
572 error_handler = _noop
573
574 def msg_reply_handler(message):
575 if isinstance(message, MethodReturnMessage):
576 reply_handler(*message.get_args_list(**get_args_opts))
577 elif isinstance(message, ErrorMessage):
578 error_handler(DBusException(name=message.get_error_name(),
579 *message.get_args_list()))
580 else:
581 error_handler(TypeError('Unexpected type for reply '
582 'message: %r' % message))
583 return self.send_message_with_reply(message, msg_reply_handler,
584 timeout,
585 require_main_loop=require_main_loop)
586
587 - def call_blocking(self, bus_name, object_path, dbus_interface, method,
588 signature, args, timeout=-1.0, utf8_strings=False,
589 byte_arrays=False):
590 """Call the given method, synchronously.
591 :Since: 0.81.0
592 """
593 if object_path == LOCAL_PATH:
594 raise DBusException('Methods may not be called on the reserved '
595 'path %s' % LOCAL_PATH)
596 if dbus_interface == LOCAL_IFACE:
597 raise DBusException('Methods may not be called on the reserved '
598 'interface %s' % LOCAL_IFACE)
599
600
601 get_args_opts = {'utf8_strings': utf8_strings,
602 'byte_arrays': byte_arrays}
603
604 message = MethodCallMessage(destination=bus_name,
605 path=object_path,
606 interface=dbus_interface,
607 method=method)
608
609 try:
610 message.append(signature=signature, *args)
611 except Exception, e:
612 logging.basicConfig()
613 _logger.error('Unable to set arguments %r according to '
614 'signature %r: %s: %s',
615 args, signature, e.__class__, e)
616 raise
617
618
619 reply_message = self.send_message_with_reply_and_block(
620 message, timeout)
621 args_list = reply_message.get_args_list(**get_args_opts)
622 if len(args_list) == 0:
623 return None
624 elif len(args_list) == 1:
625 return args_list[0]
626 else:
627 return tuple(args_list)
628
630 """Arrange for `callable` to be called with one argument (this
631 Connection object) when the Connection becomes
632 disconnected.
633
634 :Since: 0.83.0
635 """
636 self.__call_on_disconnection.append(callable)
637