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