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