~jderose/ubuntu/precise/dbus-python/oneiric-backport

« back to all changes in this revision

Viewing changes to .pc/python3-support.patch/dbus/connection.py

  • Committer: Package Import Robot
  • Author(s): Barry Warsaw
  • Date: 2012-01-12 14:47:33 UTC
  • Revision ID: package-import@ubuntu.com-20120112144733-xtfbmgw30h0j40d2
Tags: 0.84.0-2ubuntu1
* debian/patches:
  - since-0.84.0.patch: Upstream unreleased changes from git tag
    dbus-python-0.84.0 to HEAD.  This is a precursor to the following.
  - python3-support.patch: Upstream unreleased changes from git
    `python3` branch for supporting Python 3. (LP: #893091)
* debian/rules: Enable the test suite.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
 
2
#
 
3
# Permission is hereby granted, free of charge, to any person
 
4
# obtaining a copy of this software and associated documentation
 
5
# files (the "Software"), to deal in the Software without
 
6
# restriction, including without limitation the rights to use, copy,
 
7
# modify, merge, publish, distribute, sublicense, and/or sell copies
 
8
# of the Software, and to permit persons to whom the Software is
 
9
# furnished to do so, subject to the following conditions:
 
10
#
 
11
# The above copyright notice and this permission notice shall be
 
12
# included in all copies or substantial portions of the Software.
 
13
#
 
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
15
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
16
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
17
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 
18
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 
19
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
20
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 
21
# DEALINGS IN THE SOFTWARE.
 
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):
 
49
    pass
 
50
 
 
51
 
 
52
class SignalMatch(object):
 
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):
 
66
        if member is not None:
 
67
            validate_member_name(member)
 
68
        if dbus_interface is not None:
 
69
            validate_interface_name(dbus_interface)
 
70
        if sender is not None:
 
71
            validate_bus_name(sender)
 
72
        if object_path is not None:
 
73
            validate_object_path(object_path)
 
74
 
 
75
        self._rule = None
 
76
        self._conn_weakref = weakref.ref(conn)
 
77
        self._sender = sender
 
78
        self._interface = dbus_interface
 
79
        self._member = member
 
80
        self._path = object_path
 
81
        self._handler = handler
 
82
 
 
83
        # if the connection is actually a bus, it's responsible for changing
 
84
        # this later
 
85
        self._sender_name_owner = sender
 
86
 
 
87
        self._utf8_strings = utf8_strings
 
88
        self._byte_arrays = byte_arrays
 
89
        self._sender_keyword = sender_keyword
 
90
        self._path_keyword = path_keyword
 
91
        self._member_keyword = member_keyword
 
92
        self._interface_keyword = interface_keyword
 
93
        self._message_keyword = message_keyword
 
94
        self._destination_keyword = destination_keyword
 
95
 
 
96
        self._args_match = kwargs
 
97
        if not kwargs:
 
98
            self._int_args_match = None
 
99
        else:
 
100
            self._int_args_match = {}
 
101
            for kwarg in kwargs:
 
102
                if not kwarg.startswith('arg'):
 
103
                    raise TypeError('SignalMatch: unknown keyword argument %s'
 
104
                                    % kwarg)
 
105
                try:
 
106
                    index = int(kwarg[3:])
 
107
                except ValueError:
 
108
                    raise TypeError('SignalMatch: unknown keyword argument %s'
 
109
                                    % kwarg)
 
110
                if index < 0 or index > 63:
 
111
                    raise TypeError('SignalMatch: arg match index must be in '
 
112
                                    'range(64), not %d' % index)
 
113
                self._int_args_match[index] = kwargs[kwarg]
 
114
 
 
115
    def __hash__(self):
 
116
        """SignalMatch objects are compared by identity."""
 
117
        return hash(id(self))
 
118
 
 
119
    def __eq__(self, other):
 
120
        """SignalMatch objects are compared by identity."""
 
121
        return self is other
 
122
 
 
123
    def __ne__(self, other):
 
124
        """SignalMatch objects are compared by identity."""
 
125
        return self is not other
 
126
 
 
127
    sender = property(lambda self: self._sender)
 
128
 
 
129
    def __str__(self):
 
130
        if self._rule is None:
 
131
            rule = ["type='signal'"]
 
132
            if self._sender is not None:
 
133
                rule.append("sender='%s'" % self._sender)
 
134
            if self._path is not None:
 
135
                rule.append("path='%s'" % self._path)
 
136
            if self._interface is not None:
 
137
                rule.append("interface='%s'" % self._interface)
 
138
            if self._member is not None:
 
139
                rule.append("member='%s'" % self._member)
 
140
            if self._int_args_match is not None:
 
141
                for index, value in self._int_args_match.iteritems():
 
142
                    rule.append("arg%d='%s'" % (index, value))
 
143
 
 
144
            self._rule = ','.join(rule)
 
145
 
 
146
        return self._rule
 
147
 
 
148
    def __repr__(self):
 
149
        return ('<%s at %x "%s" on conn %r>'
 
150
                % (self.__class__, id(self), self._rule, self._conn_weakref()))
 
151
 
 
152
    def set_sender_name_owner(self, new_name):
 
153
        self._sender_name_owner = new_name
 
154
 
 
155
    def matches_removal_spec(self, sender, object_path,
 
156
                             dbus_interface, member, handler, **kwargs):
 
157
        if handler not in (None, self._handler):
 
158
            return False
 
159
        if sender != self._sender:
 
160
            return False
 
161
        if object_path != self._path:
 
162
            return False
 
163
        if dbus_interface != self._interface:
 
164
            return False
 
165
        if member != self._member:
 
166
            return False
 
167
        if kwargs != self._args_match:
 
168
            return False
 
169
        return True
 
170
 
 
171
    def maybe_handle_message(self, message):
 
172
        args = None
 
173
 
 
174
        # these haven't been checked yet by the match tree
 
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
            # extracting args with utf8_strings and byte_arrays is less work
 
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
        # these have likely already been checked by the match tree
 
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
            # minor optimization: if we already extracted the args with the
 
196
            # right calling convention to do the args match, don't bother
 
197
            # doing so again
 
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
            # basicConfig is a no-op if logging is already configured
 
217
            logging.basicConfig()
 
218
            _logger.error('Exception in handler for D-Bus signal:', exc_info=1)
 
219
 
 
220
        return True
 
221
 
 
222
    def remove(self):
 
223
        conn = self._conn_weakref()
 
224
        # do nothing if the connection has already vanished
 
225
        if conn is not None:
 
226
            conn.remove_signal_receiver(self, self._member,
 
227
                                        self._interface, self._sender,
 
228
                                        self._path,
 
229
                                        **self._args_match)
 
230
 
 
231
 
 
232
class Connection(_Connection):
 
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
 
 
241
    def __init__(self, *args, **kwargs):
 
242
        super(Connection, self).__init__(*args, **kwargs)
 
243
 
 
244
        # this if-block is needed because shared bus connections can be
 
245
        # __init__'ed more than once
 
246
        if not hasattr(self, '_dbus_Connection_initialized'):
 
247
            self._dbus_Connection_initialized = 1
 
248
 
 
249
            self.__call_on_disconnection = []
 
250
 
 
251
            self._signal_recipients_by_object_path = {}
 
252
            """Map from object path to dict mapping dbus_interface to dict
 
253
            mapping member to list of SignalMatch objects."""
 
254
 
 
255
            self._signals_lock = thread.allocate_lock()
 
256
            """Lock used to protect signal data structures"""
 
257
 
 
258
            self.add_message_filter(self.__class__._signal_func)
 
259
 
 
260
    def activate_name_owner(self, bus_name):
 
261
        """Return the unique name for the given bus name, activating it
 
262
        if necessary and possible.
 
263
 
 
264
        If the name is already unique or this connection is not to a
 
265
        bus daemon, just return it.
 
266
 
 
267
        :Returns: a bus name. If the given `bus_name` exists, the returned
 
268
            name identifies its current owner; otherwise the returned name
 
269
            does not exist.
 
270
        :Raises DBusException: if the implementation has failed
 
271
            to activate the given bus name.
 
272
        :Since: 0.81.0
 
273
        """
 
274
        return bus_name
 
275
 
 
276
    def get_object(self, bus_name=None, object_path=None, introspect=True,
 
277
                   **kwargs):
 
278
        """Return a local proxy for the given remote object.
 
279
 
 
280
        Method calls on the proxy are translated into method calls on the
 
281
        remote object.
 
282
 
 
283
        :Parameters:
 
284
            `bus_name` : str
 
285
                A bus name (either the unique name or a well-known name)
 
286
                of the application owning the object. The keyword argument
 
287
                named_service is a deprecated alias for this.
 
288
            `object_path` : str
 
289
                The object path of the desired object
 
290
            `introspect` : bool
 
291
                If true (default), attempt to introspect the remote
 
292
                object to find out supported methods and their signatures
 
293
 
 
294
        :Returns: a `dbus.proxies.ProxyObject`
 
295
        """
 
296
        named_service = kwargs.pop('named_service', None)
 
297
        if named_service is not None:
 
298
            if bus_name is not None:
 
299
                raise TypeError('bus_name and named_service cannot both '
 
300
                                'be specified')
 
301
            from warnings import warn
 
302
            warn('Passing the named_service parameter to get_object by name '
 
303
                 'is deprecated: please use positional parameters',
 
304
                 DeprecationWarning, stacklevel=2)
 
305
            bus_name = named_service
 
306
        if kwargs:
 
307
            raise TypeError('get_object does not take these keyword '
 
308
                            'arguments: %s' % ', '.join(kwargs.iterkeys()))
 
309
 
 
310
        return self.ProxyObjectClass(self, bus_name, object_path,
 
311
                                     introspect=introspect)
 
312
 
 
313
    def add_signal_receiver(self, handler_function,
 
314
                                  signal_name=None,
 
315
                                  dbus_interface=None,
 
316
                                  bus_name=None,
 
317
                                  path=None,
 
318
                                  **keywords):
 
319
        """Arrange for the given function to be called when a signal matching
 
320
        the parameters is received.
 
321
 
 
322
        :Parameters:
 
323
            `handler_function` : callable
 
324
                The function to be called. Its positional arguments will
 
325
                be the arguments of the signal. By default it will receive
 
326
                no keyword arguments, but see the description of
 
327
                the optional keyword arguments below.
 
328
            `signal_name` : str
 
329
                The signal name; None (the default) matches all names
 
330
            `dbus_interface` : str
 
331
                The D-Bus interface name with which to qualify the signal;
 
332
                None (the default) matches all interface names
 
333
            `bus_name` : str
 
334
                A bus name for the sender, which will be resolved to a
 
335
                unique name if it is not already; None (the default) matches
 
336
                any sender.
 
337
            `path` : str
 
338
                The object path of the object which must have emitted the
 
339
                signal; None (the default) matches any object path
 
340
        :Keywords:
 
341
            `utf8_strings` : bool
 
342
                If True, the handler function will receive any string
 
343
                arguments as dbus.UTF8String objects (a subclass of str
 
344
                guaranteed to be UTF-8). If False (default) it will receive
 
345
                any string arguments as dbus.String objects (a subclass of
 
346
                unicode).
 
347
            `byte_arrays` : bool
 
348
                If True, the handler function will receive any byte-array
 
349
                arguments as dbus.ByteArray objects (a subclass of str).
 
350
                If False (default) it will receive any byte-array
 
351
                arguments as a dbus.Array of dbus.Byte (subclasses of:
 
352
                a list of ints).
 
353
            `sender_keyword` : str
 
354
                If not None (the default), the handler function will receive
 
355
                the unique name of the sending endpoint as a keyword
 
356
                argument with this name.
 
357
            `destination_keyword` : str
 
358
                If not None (the default), the handler function will receive
 
359
                the bus name of the destination (or None if the signal is a
 
360
                broadcast, as is usual) as a keyword argument with this name.
 
361
            `interface_keyword` : str
 
362
                If not None (the default), the handler function will receive
 
363
                the signal interface as a keyword argument with this name.
 
364
            `member_keyword` : str
 
365
                If not None (the default), the handler function will receive
 
366
                the signal name as a keyword argument with this name.
 
367
            `path_keyword` : str
 
368
                If not None (the default), the handler function will receive
 
369
                the object-path of the sending object as a keyword argument
 
370
                with this name.
 
371
            `message_keyword` : str
 
372
                If not None (the default), the handler function will receive
 
373
                the `dbus.lowlevel.SignalMessage` as a keyword argument with
 
374
                this name.
 
375
            `arg...` : unicode or UTF-8 str
 
376
                If there are additional keyword parameters of the form
 
377
                ``arg``\ *n*, match only signals where the *n*\ th argument
 
378
                is the value given for that keyword parameter. As of this
 
379
                time only string arguments can be matched (in particular,
 
380
                object paths and signatures can't).
 
381
            `named_service` : str
 
382
                A deprecated alias for `bus_name`.
 
383
        """
 
384
        self._require_main_loop()
 
385
 
 
386
        named_service = keywords.pop('named_service', None)
 
387
        if named_service is not None:
 
388
            if bus_name is not None:
 
389
                raise TypeError('bus_name and named_service cannot both be '
 
390
                                'specified')
 
391
            bus_name = named_service
 
392
            from warnings import warn
 
393
            warn('Passing the named_service parameter to add_signal_receiver '
 
394
                 'by name is deprecated: please use positional parameters',
 
395
                 DeprecationWarning, stacklevel=2)
 
396
 
 
397
        match = SignalMatch(self, bus_name, path, dbus_interface,
 
398
                            signal_name, handler_function, **keywords)
 
399
 
 
400
        self._signals_lock.acquire()
 
401
        try:
 
402
            by_interface = self._signal_recipients_by_object_path.setdefault(
 
403
                    path, {})
 
404
            by_member = by_interface.setdefault(dbus_interface, {})
 
405
            matches = by_member.setdefault(signal_name, [])
 
406
 
 
407
            matches.append(match)
 
408
        finally:
 
409
            self._signals_lock.release()
 
410
 
 
411
        return match
 
412
 
 
413
    def _iter_easy_matches(self, path, dbus_interface, member):
 
414
        if path is not None:
 
415
            path_keys = (None, path)
 
416
        else:
 
417
            path_keys = (None,)
 
418
        if dbus_interface is not None:
 
419
            interface_keys = (None, dbus_interface)
 
420
        else:
 
421
            interface_keys = (None,)
 
422
        if member is not None:
 
423
            member_keys = (None, member)
 
424
        else:
 
425
            member_keys = (None,)
 
426
 
 
427
        for path in path_keys:
 
428
            by_interface = self._signal_recipients_by_object_path.get(path,
 
429
                                                                      None)
 
430
            if by_interface is None:
 
431
                continue
 
432
            for dbus_interface in interface_keys:
 
433
                by_member = by_interface.get(dbus_interface, None)
 
434
                if by_member is None:
 
435
                    continue
 
436
                for member in member_keys:
 
437
                    matches = by_member.get(member, None)
 
438
                    if matches is None:
 
439
                        continue
 
440
                    for m in matches:
 
441
                        yield m
 
442
 
 
443
    def remove_signal_receiver(self, handler_or_match,
 
444
                               signal_name=None,
 
445
                               dbus_interface=None,
 
446
                               bus_name=None,
 
447
                               path=None,
 
448
                               **keywords):
 
449
        named_service = keywords.pop('named_service', None)
 
450
        if named_service is not None:
 
451
            if bus_name is not None:
 
452
                raise TypeError('bus_name and named_service cannot both be '
 
453
                                'specified')
 
454
            bus_name = named_service
 
455
            from warnings import warn
 
456
            warn('Passing the named_service parameter to '
 
457
                 'remove_signal_receiver by name is deprecated: please use '
 
458
                 'positional parameters',
 
459
                 DeprecationWarning, stacklevel=2)
 
460
 
 
461
        new = []
 
462
        deletions = []
 
463
        self._signals_lock.acquire()
 
464
        try:
 
465
            by_interface = self._signal_recipients_by_object_path.get(path,
 
466
                                                                      None)
 
467
            if by_interface is None:
 
468
                return
 
469
            by_member = by_interface.get(dbus_interface, None)
 
470
            if by_member is None:
 
471
                return
 
472
            matches = by_member.get(signal_name, None)
 
473
            if matches is None:
 
474
                return
 
475
 
 
476
            for match in matches:
 
477
                if (handler_or_match is match
 
478
                    or match.matches_removal_spec(bus_name,
 
479
                                                  path,
 
480
                                                  dbus_interface,
 
481
                                                  signal_name,
 
482
                                                  handler_or_match,
 
483
                                                  **keywords)):
 
484
                    deletions.append(match)
 
485
                else:
 
486
                    new.append(match)
 
487
 
 
488
            if new:
 
489
                by_member[signal_name] = new
 
490
            else:
 
491
                del by_member[signal_name]
 
492
                if not by_member:
 
493
                    del by_interface[dbus_interface]
 
494
                    if not by_interface:
 
495
                        del self._signal_recipients_by_object_path[path]
 
496
        finally:
 
497
            self._signals_lock.release()
 
498
 
 
499
        for match in deletions:
 
500
            self._clean_up_signal_match(match)
 
501
 
 
502
    def _clean_up_signal_match(self, match):
 
503
        # Now called without the signals lock held (it was held in <= 0.81.0)
 
504
        pass
 
505
 
 
506
    def _signal_func(self, message):
 
507
        """D-Bus filter function. Handle signals by dispatching to Python
 
508
        callbacks kept in the match-rule tree.
 
509
        """
 
510
 
 
511
        if not isinstance(message, SignalMessage):
 
512
            return HANDLER_RESULT_NOT_YET_HANDLED
 
513
 
 
514
        dbus_interface = message.get_interface()
 
515
        path = message.get_path()
 
516
        signal_name = message.get_member()
 
517
 
 
518
        for match in self._iter_easy_matches(path, dbus_interface,
 
519
                                             signal_name):
 
520
            match.maybe_handle_message(message)
 
521
 
 
522
        if (dbus_interface == LOCAL_IFACE and
 
523
            path == LOCAL_PATH and
 
524
            signal_name == 'Disconnected'):
 
525
            for cb in self.__call_on_disconnection:
 
526
                try:
 
527
                    cb(self)
 
528
                except Exception as e:
 
529
                    # basicConfig is a no-op if logging is already configured
 
530
                    logging.basicConfig()
 
531
                    _logger.error('Exception in handler for Disconnected '
 
532
                        'signal:', exc_info=1)
 
533
 
 
534
        return HANDLER_RESULT_NOT_YET_HANDLED
 
535
 
 
536
    def call_async(self, bus_name, object_path, dbus_interface, method,
 
537
                   signature, args, reply_handler, error_handler,
 
538
                   timeout=-1.0, utf8_strings=False, byte_arrays=False,
 
539
                   require_main_loop=True):
 
540
        """Call the given method, asynchronously.
 
541
 
 
542
        If the reply_handler is None, successful replies will be ignored.
 
543
        If the error_handler is None, failures will be ignored. If both
 
544
        are None, the implementation may request that no reply is sent.
 
545
 
 
546
        :Returns: The dbus.lowlevel.PendingCall.
 
547
        :Since: 0.81.0
 
548
        """
 
549
        if object_path == LOCAL_PATH:
 
550
            raise DBusException('Methods may not be called on the reserved '
 
551
                                'path %s' % LOCAL_PATH)
 
552
        if dbus_interface == LOCAL_IFACE:
 
553
            raise DBusException('Methods may not be called on the reserved '
 
554
                                'interface %s' % LOCAL_IFACE)
 
555
        # no need to validate other args - MethodCallMessage ctor will do
 
556
 
 
557
        get_args_opts = {'utf8_strings': utf8_strings,
 
558
                         'byte_arrays': byte_arrays}
 
559
 
 
560
        message = MethodCallMessage(destination=bus_name,
 
561
                                    path=object_path,
 
562
                                    interface=dbus_interface,
 
563
                                    method=method)
 
564
        # Add the arguments to the function
 
565
        try:
 
566
            message.append(signature=signature, *args)
 
567
        except Exception as e:
 
568
            logging.basicConfig()
 
569
            _logger.error('Unable to set arguments %r according to '
 
570
                          'signature %r: %s: %s',
 
571
                          args, signature, e.__class__, e)
 
572
            raise
 
573
 
 
574
        if reply_handler is None and error_handler is None:
 
575
            # we don't care what happens, so just send it
 
576
            self.send_message(message)
 
577
            return
 
578
 
 
579
        if reply_handler is None:
 
580
            reply_handler = _noop
 
581
        if error_handler is None:
 
582
            error_handler = _noop
 
583
 
 
584
        def msg_reply_handler(message):
 
585
            if isinstance(message, MethodReturnMessage):
 
586
                reply_handler(*message.get_args_list(**get_args_opts))
 
587
            elif isinstance(message, ErrorMessage):
 
588
                error_handler(DBusException(name=message.get_error_name(),
 
589
                                            *message.get_args_list()))
 
590
            else:
 
591
                error_handler(TypeError('Unexpected type for reply '
 
592
                                        'message: %r' % message))
 
593
        return self.send_message_with_reply(message, msg_reply_handler,
 
594
                                        timeout,
 
595
                                        require_main_loop=require_main_loop)
 
596
 
 
597
    def call_blocking(self, bus_name, object_path, dbus_interface, method,
 
598
                      signature, args, timeout=-1.0, utf8_strings=False,
 
599
                      byte_arrays=False):
 
600
        """Call the given method, synchronously.
 
601
        :Since: 0.81.0
 
602
        """
 
603
        if object_path == LOCAL_PATH:
 
604
            raise DBusException('Methods may not be called on the reserved '
 
605
                                'path %s' % LOCAL_PATH)
 
606
        if dbus_interface == LOCAL_IFACE:
 
607
            raise DBusException('Methods may not be called on the reserved '
 
608
                                'interface %s' % LOCAL_IFACE)
 
609
        # no need to validate other args - MethodCallMessage ctor will do
 
610
 
 
611
        get_args_opts = {'utf8_strings': utf8_strings,
 
612
                         'byte_arrays': byte_arrays}
 
613
 
 
614
        message = MethodCallMessage(destination=bus_name,
 
615
                                    path=object_path,
 
616
                                    interface=dbus_interface,
 
617
                                    method=method)
 
618
        # Add the arguments to the function
 
619
        try:
 
620
            message.append(signature=signature, *args)
 
621
        except Exception as e:
 
622
            logging.basicConfig()
 
623
            _logger.error('Unable to set arguments %r according to '
 
624
                          'signature %r: %s: %s',
 
625
                          args, signature, e.__class__, e)
 
626
            raise
 
627
 
 
628
        # make a blocking call
 
629
        reply_message = self.send_message_with_reply_and_block(
 
630
            message, timeout)
 
631
        args_list = reply_message.get_args_list(**get_args_opts)
 
632
        if len(args_list) == 0:
 
633
            return None
 
634
        elif len(args_list) == 1:
 
635
            return args_list[0]
 
636
        else:
 
637
            return tuple(args_list)
 
638
 
 
639
    def call_on_disconnection(self, callable):
 
640
        """Arrange for `callable` to be called with one argument (this
 
641
        Connection object) when the Connection becomes
 
642
        disconnected.
 
643
 
 
644
        :Since: 0.83.0
 
645
        """
 
646
        self.__call_on_disconnection.append(callable)