1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import sys
27 import logging
28
29 try:
30 from threading import RLock
31 except ImportError:
32 from dummy_threading import RLock
33
34 import _dbus_bindings
35 from dbus._expat_introspect_parser import process_introspection_data
36 from dbus.exceptions import MissingReplyHandlerException, MissingErrorHandlerException, IntrospectionParserException, DBusException
37
38 __docformat__ = 'restructuredtext'
39
40
41 _logger = logging.getLogger('dbus.proxies')
42
43 from _dbus_bindings import LOCAL_PATH, \
44 BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE,\
45 INTROSPECTABLE_IFACE
46
47
49 """A proxy method which will only get called once we have its
50 introspection reply.
51 """
52 - def __init__(self, proxy_method, append, block):
53 self._proxy_method = proxy_method
54
55 self._method_name = proxy_method._method_name
56 self._append = append
57 self._block = block
58
60 if (keywords.has_key('reply_handler') or
61 keywords.get('ignore_reply', False)):
62
63 self._append(self._proxy_method, args, keywords)
64 return None
65 else:
66
67 self._block()
68 return self._proxy_method(*args, **keywords)
69
71 self._append(self._proxy_method, args, keywords)
72
73
75 """A proxy method.
76
77 Typically a member of a ProxyObject. Calls to the
78 method produce messages that travel over the Bus and are routed
79 to a specific named Service.
80 """
81 - def __init__(self, proxy, connection, bus_name, object_path, method_name,
82 iface):
100
102 reply_handler = keywords.pop('reply_handler', None)
103 error_handler = keywords.pop('error_handler', None)
104 ignore_reply = keywords.pop('ignore_reply', False)
105 signature = keywords.pop('signature', None)
106
107 if reply_handler is not None or error_handler is not None:
108 if reply_handler is None:
109 raise MissingReplyHandlerException()
110 elif error_handler is None:
111 raise MissingErrorHandlerException()
112 elif ignore_reply:
113 raise TypeError('ignore_reply and reply_handler cannot be '
114 'used together')
115
116 dbus_interface = keywords.pop('dbus_interface', self._dbus_interface)
117
118 if signature is None:
119 if dbus_interface is None:
120 key = self._method_name
121 else:
122 key = dbus_interface + '.' + self._method_name
123
124 signature = self._proxy._introspect_method_map.get(key, None)
125
126 if ignore_reply or reply_handler is not None:
127 self._connection.call_async(self._named_service,
128 self._object_path,
129 dbus_interface,
130 self._method_name,
131 signature,
132 args,
133 reply_handler,
134 error_handler,
135 **keywords)
136 else:
137 return self._connection.call_blocking(self._named_service,
138 self._object_path,
139 dbus_interface,
140 self._method_name,
141 signature,
142 args,
143 **keywords)
144
146 reply_handler = keywords.pop('reply_handler', None)
147 error_handler = keywords.pop('error_handler', None)
148 signature = keywords.pop('signature', None)
149
150 dbus_interface = keywords.pop('dbus_interface', self._dbus_interface)
151
152 if signature is None:
153 if dbus_interface:
154 key = dbus_interface + '.' + self._method_name
155 else:
156 key = self._method_name
157 signature = self._proxy._introspect_method_map.get(key, None)
158
159 self._connection.call_async(self._named_service,
160 self._object_path,
161 dbus_interface,
162 self._method_name,
163 signature,
164 args,
165 reply_handler,
166 error_handler,
167 **keywords)
168
169
171 """A proxy to the remote Object.
172
173 A ProxyObject is provided by the Bus. ProxyObjects
174 have member functions, and can be called like normal Python objects.
175 """
176 ProxyMethodClass = _ProxyMethod
177 DeferredMethodClass = _DeferredMethod
178
179 INTROSPECT_STATE_DONT_INTROSPECT = 0
180 INTROSPECT_STATE_INTROSPECT_IN_PROGRESS = 1
181 INTROSPECT_STATE_INTROSPECT_DONE = 2
182
183 - def __init__(self, conn=None, bus_name=None, object_path=None,
184 introspect=True, follow_name_owner_changes=False, **kwargs):
185 """Initialize the proxy object.
186
187 :Parameters:
188 `conn` : `dbus.connection.Connection`
189 The bus or connection on which to find this object.
190 The keyword argument `bus` is a deprecated alias for this.
191 `bus_name` : str
192 A bus name for the application owning the object, to be used
193 as the destination for method calls and the sender for
194 signal matches. The keyword argument ``named_service`` is a
195 deprecated alias for this.
196 `object_path` : str
197 The object path at which the application exports the object
198 `introspect` : bool
199 If true (default), attempt to introspect the remote
200 object to find out supported methods and their signatures
201 `follow_name_owner_changes` : bool
202 If true (default is false) and the `bus_name` is a
203 well-known name, follow ownership changes for that name
204 """
205 bus = kwargs.pop('bus', None)
206 if bus is not None:
207 if conn is not None:
208 raise TypeError('conn and bus cannot both be specified')
209 conn = bus
210 from warnings import warn
211 warn('Passing the bus parameter to ProxyObject by name is '
212 'deprecated: please use positional parameters',
213 DeprecationWarning, stacklevel=2)
214 named_service = kwargs.pop('named_service', None)
215 if named_service is not None:
216 if bus_name is not None:
217 raise TypeError('bus_name and named_service cannot both be '
218 'specified')
219 bus_name = named_service
220 from warnings import warn
221 warn('Passing the named_service parameter to ProxyObject by name '
222 'is deprecated: please use positional parameters',
223 DeprecationWarning, stacklevel=2)
224 if kwargs:
225 raise TypeError('ProxyObject.__init__ does not take these '
226 'keyword arguments: %s'
227 % ', '.join(kwargs.iterkeys()))
228
229 if follow_name_owner_changes:
230
231
232 conn._require_main_loop()
233
234 self._bus = conn
235
236 if bus_name is not None:
237 _dbus_bindings.validate_bus_name(bus_name)
238
239
240 self._named_service = self._requested_bus_name = bus_name
241
242 _dbus_bindings.validate_object_path(object_path)
243 self.__dbus_object_path__ = object_path
244
245 if not follow_name_owner_changes:
246 self._named_service = conn.activate_name_owner(bus_name)
247
248
249 self._pending_introspect = None
250
251 self._pending_introspect_queue = []
252
253 self._introspect_method_map = {}
254
255
256
257 self._introspect_lock = RLock()
258
259 if not introspect or self.__dbus_object_path__ == LOCAL_PATH:
260 self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
261 else:
262 self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS
263
264 self._pending_introspect = self._Introspect()
265
266 bus_name = property(lambda self: self._named_service, None, None,
267 """The bus name to which this proxy is bound. (Read-only,
268 may change.)
269
270 If the proxy was instantiated using a unique name, this property
271 is that unique name.
272
273 If the proxy was instantiated with a well-known name and with
274 ``follow_name_owner_changes`` set false (the default), this
275 property is the unique name of the connection that owned that
276 well-known name when the proxy was instantiated, which might
277 not actually own the requested well-known name any more.
278
279 If the proxy was instantiated with a well-known name and with
280 ``follow_name_owner_changes`` set true, this property is that
281 well-known name.
282 """)
283
284 requested_bus_name = property(lambda self: self._requested_bus_name,
285 None, None,
286 """The bus name which was requested when this proxy was
287 instantiated.
288 """)
289
290 object_path = property(lambda self: self.__dbus_object_path__,
291 None, None,
292 """The object-path of this proxy.""")
293
294
295
296
297
298
299
300
301
302 - def connect_to_signal(self, signal_name, handler_function, dbus_interface=None, **keywords):
303 """Arrange for the given function to be called when the given signal
304 is received.
305
306 :Parameters:
307 `signal_name` : str
308 The name of the signal
309 `handler_function` : callable
310 A function to be called when the signal is emitted by
311 the remote object. Its positional arguments will be the
312 arguments of the signal; optionally, it may be given
313 keyword arguments as described below.
314 `dbus_interface` : str
315 Optional interface with which to qualify the signal name.
316 If None (the default) the handler will be called whenever a
317 signal of the given member name is received, whatever
318 its interface.
319 :Keywords:
320 `utf8_strings` : bool
321 If True, the handler function will receive any string
322 arguments as dbus.UTF8String objects (a subclass of str
323 guaranteed to be UTF-8). If False (default) it will receive
324 any string arguments as dbus.String objects (a subclass of
325 unicode).
326 `byte_arrays` : bool
327 If True, the handler function will receive any byte-array
328 arguments as dbus.ByteArray objects (a subclass of str).
329 If False (default) it will receive any byte-array
330 arguments as a dbus.Array of dbus.Byte (subclasses of:
331 a list of ints).
332 `sender_keyword` : str
333 If not None (the default), the handler function will receive
334 the unique name of the sending endpoint as a keyword
335 argument with this name
336 `destination_keyword` : str
337 If not None (the default), the handler function will receive
338 the bus name of the destination (or None if the signal is a
339 broadcast, as is usual) as a keyword argument with this name.
340 `interface_keyword` : str
341 If not None (the default), the handler function will receive
342 the signal interface as a keyword argument with this name.
343 `member_keyword` : str
344 If not None (the default), the handler function will receive
345 the signal name as a keyword argument with this name.
346 `path_keyword` : str
347 If not None (the default), the handler function will receive
348 the object-path of the sending object as a keyword argument
349 with this name
350 `message_keyword` : str
351 If not None (the default), the handler function will receive
352 the `dbus.lowlevel.SignalMessage` as a keyword argument with
353 this name.
354 `arg...` : unicode or UTF-8 str
355 If there are additional keyword parameters of the form
356 ``arg``\ *n*, match only signals where the *n*\ th argument
357 is the value given for that keyword parameter. As of this time
358 only string arguments can be matched (in particular,
359 object paths and signatures can't).
360 """
361 return \
362 self._bus.add_signal_receiver(handler_function,
363 signal_name=signal_name,
364 dbus_interface=dbus_interface,
365 bus_name=self._named_service,
366 path=self.__dbus_object_path__,
367 **keywords)
368
377
379
380
381
382 for (proxy_method, args, keywords) in self._pending_introspect_queue:
383 proxy_method(*args, **keywords)
384 self._pending_introspect_queue = []
385
400
402 logging.basicConfig()
403 _logger.error("Introspect error on %s:%s: %s.%s: %s",
404 self._named_service, self.__dbus_object_path__,
405 error.__class__.__module__, error.__class__.__name__,
406 error)
407 self._introspect_lock.acquire()
408 try:
409 _logger.debug('Executing introspect queue due to error')
410 self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
411 self._pending_introspect = None
412 self._introspect_execute_queue()
413 finally:
414 self._introspect_lock.release()
415
417 self._introspect_lock.acquire()
418 try:
419 if self._pending_introspect is not None:
420 self._pending_introspect.block()
421
422
423 finally:
424 self._introspect_lock.release()
425
427 self._introspect_lock.acquire()
428 try:
429 if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
430 self._pending_introspect_queue.append((callback, args, kwargs))
431 else:
432
433
434 callback(*args, **kwargs)
435 finally:
436 self._introspect_lock.release()
437
439 if member.startswith('__') and member.endswith('__'):
440 raise AttributeError(member)
441 else:
442 return self.get_dbus_method(member)
443
445 """Return a proxy method representing the given D-Bus method. The
446 returned proxy method can be called in the usual way. For instance, ::
447
448 proxy.get_dbus_method("Foo", dbus_interface='com.example.Bar')(123)
449
450 is equivalent to::
451
452 proxy.Foo(123, dbus_interface='com.example.Bar')
453
454 or even::
455
456 getattr(proxy, "Foo")(123, dbus_interface='com.example.Bar')
457
458 However, using `get_dbus_method` is the only way to call D-Bus
459 methods with certain awkward names - if the author of a service
460 implements a method called ``connect_to_signal`` or even
461 ``__getattr__``, you'll need to use `get_dbus_method` to call them.
462
463 For services which follow the D-Bus convention of CamelCaseMethodNames
464 this won't be a problem.
465 """
466
467 ret = self.ProxyMethodClass(self, self._bus,
468 self._named_service,
469 self.__dbus_object_path__, member,
470 dbus_interface)
471
472
473
474
475
476 if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
477 ret = self.DeferredMethodClass(ret, self._introspect_add_to_queue,
478 self._introspect_block)
479
480 return ret
481
483 return '<ProxyObject wrapping %s %s %s at %#x>'%(
484 self._bus, self._named_service, self.__dbus_object_path__, id(self))
485 __str__ = __repr__
486
487
489 """An interface into a remote object.
490
491 An Interface can be used to wrap ProxyObjects
492 so that calls can be routed to their correct
493 D-Bus interface.
494 """
495
496 - def __init__(self, object, dbus_interface):
497 """Construct a proxy for the given interface on the given object.
498
499 :Parameters:
500 `object` : `dbus.proxies.ProxyObject` or `dbus.Interface`
501 The remote object or another of its interfaces
502 `dbus_interface` : str
503 An interface the `object` implements
504 """
505 if isinstance(object, Interface):
506 self._obj = object.proxy_object
507 else:
508 self._obj = object
509 self._dbus_interface = dbus_interface
510
511 object_path = property (lambda self: self._obj.object_path, None, None,
512 "The D-Bus object path of the underlying object")
513 __dbus_object_path__ = object_path
514 bus_name = property (lambda self: self._obj.bus_name, None, None,
515 "The bus name to which the underlying proxy object "
516 "is bound")
517 requested_bus_name = property (lambda self: self._obj.requested_bus_name,
518 None, None,
519 "The bus name which was requested when the "
520 "underlying object was created")
521 proxy_object = property (lambda self: self._obj, None, None,
522 """The underlying proxy object""")
523 dbus_interface = property (lambda self: self._dbus_interface, None, None,
524 """The D-Bus interface represented""")
525
526 - def connect_to_signal(self, signal_name, handler_function,
527 dbus_interface=None, **keywords):
528 """Arrange for a function to be called when the given signal is
529 emitted.
530
531 The parameters and keyword arguments are the same as for
532 `dbus.proxies.ProxyObject.connect_to_signal`, except that if
533 `dbus_interface` is None (the default), the D-Bus interface that
534 was passed to the `Interface` constructor is used.
535 """
536 if not dbus_interface:
537 dbus_interface = self._dbus_interface
538
539 return self._obj.connect_to_signal(signal_name, handler_function,
540 dbus_interface, **keywords)
541
543 if member.startswith('__') and member.endswith('__'):
544 raise AttributeError(member)
545 else:
546 return self._obj.get_dbus_method(member, self._dbus_interface)
547
549 """Return a proxy method representing the given D-Bus method.
550
551 This is the same as `dbus.proxies.ProxyObject.get_dbus_method`
552 except that if `dbus_interface` is None (the default),
553 the D-Bus interface that was passed to the `Interface` constructor
554 is used.
555 """
556 if dbus_interface is None:
557 dbus_interface = self._dbus_interface
558 return self._obj.get_dbus_method(member, dbus_interface)
559
561 return '<Interface %r implementing %r at %#x>'%(
562 self._obj, self._dbus_interface, id(self))
563 __str__ = __repr__
564