1 __all__
= ('BusName', 'Object', 'method', 'signal')
2 __docformat__
= 'restructuredtext'
10 import dbus
._dbus
as _dbus
11 from dbus
.exceptions
import NameExistsException
12 from dbus
.exceptions
import UnknownMethodException
13 from dbus
.decorators
import method
14 from dbus
.decorators
import signal
17 _logger
= logging
.getLogger('dbus.service')
20 class _VariantSignature(object):
21 """A fake method signature which, when iterated, yields an endless stream
22 of 'v' characters representing variants (handy with zip()).
24 It has no string representation.
31 """Return 'v' whenever called."""
34 class BusName(object):
35 """A base class for exporting your own Named Services across the Bus.
37 When instantiated, objects of this class attempt to claim the given
38 well-known name on the given bus for the current process. The name is
39 released when the BusName object becomes unreferenced.
41 If a well-known name is requested multiple times, multiple references
42 to the same BusName object will be returned.
46 - Assumes that named services are only ever requested using this class -
47 if you request names from the bus directly, confusion may occur.
48 - Does not handle queueing.
50 def __new__(cls
, name
, bus
=None, allow_replacement
=False , replace_existing
=False, do_not_queue
=False):
51 """Constructor, which may either return an existing cached object
56 The well-known name to be advertised
58 A Bus on which this service will be advertised; if None
59 (default) a default bus will be used
60 `allow_replacement` : bool
61 If True, other processes trying to claim the same well-known
62 name will take precedence over this one.
63 `replace_existing` : bool
64 If True, this process can take over the well-known name
65 from other processes already holding it.
67 If True, this service will not be placed in the queue of
68 services waiting for the requested name if another service
75 # see if this name is already defined, return it if so
76 # FIXME: accessing internals of Bus
77 if name
in bus
._bus
_names
:
78 return bus
._bus
_names
[name
]
80 # otherwise register the name
82 _dbus_bindings
.NAME_FLAG_ALLOW_REPLACEMENT
* allow_replacement
+ \
83 _dbus_bindings
.NAME_FLAG_REPLACE_EXISTING
* replace_existing
+ \
84 _dbus_bindings
.NAME_FLAG_DO_NOT_QUEUE
* do_not_queue
86 retval
= bus
.request_name(name
, name_flags
)
88 # TODO: more intelligent tracking of bus name states?
89 if retval
== _dbus_bindings
.REQUEST_NAME_REPLY_PRIMARY_OWNER
:
91 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_IN_QUEUE
:
92 # queueing can happen by default, maybe we should
93 # track this better or let the user know if they're
96 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_EXISTS
:
97 raise NameExistsException(name
)
98 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_ALREADY_OWNER
:
99 # if this is a shared bus which is being used by someone
100 # else in this process, this can happen legitimately
103 raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name
, retval
))
105 # and create the object
106 bus_name
= object.__new
__(cls
)
108 bus_name
._name
= name
110 # cache instance (weak ref only)
111 # FIXME: accessing Bus internals again
112 bus
._bus
_names
[name
] = bus_name
116 # do nothing because this is called whether or not the bus name
117 # object was retrieved from the cache or created new
118 def __init__(self
, *args
, **keywords
):
121 # we can delete the low-level name here because these objects
122 # are guaranteed to exist only once for each bus name
124 self
._bus
.release_name(self
._name
)
128 """Get the Bus this Service is on"""
132 """Get the name of this service"""
136 return '<dbus.service.BusName %s on %r at %#x>' % (self
._name
, self
._bus
, id(self
))
140 def _method_lookup(self
, method_name
, dbus_interface
):
141 """Walks the Python MRO of the given class to find the method to invoke.
143 Returns two methods, the one to call, and the one it inherits from which
144 defines its D-Bus interface name, signature, and attributes.
147 candidate_class
= None
150 # split up the cases when we do and don't have an interface because the
151 # latter is much simpler
153 # search through the class hierarchy in python MRO order
154 for cls
in self
.__class
__.__mro
__:
155 # if we haven't got a candidate class yet, and we find a class with a
156 # suitably named member, save this as a candidate class
157 if (not candidate_class
and method_name
in cls
.__dict
__):
158 if ("_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__
159 and "_dbus_interface" in cls
.__dict
__[method_name
].__dict
__):
160 # however if it is annotated for a different interface
161 # than we are looking for, it cannot be a candidate
162 if cls
.__dict
__[method_name
]._dbus
_interface
== dbus_interface
:
163 candidate_class
= cls
164 parent_method
= cls
.__dict
__[method_name
]
170 candidate_class
= cls
172 # if we have a candidate class, carry on checking this and all
173 # superclasses for a method annoated as a dbus method
174 # on the correct interface
175 if (candidate_class
and method_name
in cls
.__dict
__
176 and "_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__
177 and "_dbus_interface" in cls
.__dict
__[method_name
].__dict
__
178 and cls
.__dict
__[method_name
]._dbus
_interface
== dbus_interface
):
179 # the candidate class has a dbus method on the correct interface,
180 # or overrides a method that is, success!
181 parent_method
= cls
.__dict
__[method_name
]
186 # simpler version of above
187 for cls
in self
.__class
__.__mro
__:
188 if (not candidate_class
and method_name
in cls
.__dict
__):
189 candidate_class
= cls
191 if (candidate_class
and method_name
in cls
.__dict
__
192 and "_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__):
193 parent_method
= cls
.__dict
__[method_name
]
198 return (candidate_class
.__dict
__[method_name
], parent_method
)
201 raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name
, dbus_interface
))
203 raise UnknownMethodException('%s is not a valid method' % method_name
)
206 # FIXME: if signature is '', we used to accept anything, but now insist on
207 # zero args. Which was desired? -smcv
208 def _method_reply_return(connection
, message
, method_name
, signature
, *retval
):
209 reply
= _dbus_bindings
.MethodReturnMessage(message
)
211 reply
.append(signature
=signature
, *retval
)
213 if signature
is None:
215 signature
= reply
.guess_signature(retval
) + ' (guessed)'
217 _logger
.error('Unable to guess signature for arguments %r: '
218 '%s: %s', retval
, e
.__class
__, e
)
220 _logger
.error('Unable to append %r to message with signature %s: '
221 '%s: %s', retval
, signature
, e
.__class
__, e
)
224 connection
._send
(reply
)
227 def _method_reply_error(connection
, message
, exception
):
228 if '_dbus_error_name' in exception
.__dict
__:
229 name
= exception
._dbus
_error
_name
230 elif exception
.__module
__ == '__main__':
231 name
= 'org.freedesktop.DBus.Python.%s' % exception
.__class
__.__name
__
233 name
= 'org.freedesktop.DBus.Python.%s.%s' % (exception
.__module
__, exception
.__class
__.__name
__)
235 contents
= traceback
.format_exc()
236 reply
= _dbus_bindings
.ErrorMessage(message
, name
, contents
)
238 connection
._send
(reply
)
241 class InterfaceType(type):
242 def __init__(cls
, name
, bases
, dct
):
243 # these attributes are shared between all instances of the Interface
244 # object, so this has to be a dictionary that maps class names to
245 # the per-class introspection/interface data
246 class_table
= getattr(cls
, '_dbus_class_table', {})
247 cls
._dbus
_class
_table
= class_table
248 interface_table
= class_table
[cls
.__module
__ + '.' + name
] = {}
250 # merge all the name -> method tables for all the interfaces
251 # implemented by our base classes into our own
253 base_name
= b
.__module
__ + '.' + b
.__name
__
254 if getattr(b
, '_dbus_class_table', False):
255 for (interface
, method_table
) in class_table
[base_name
].iteritems():
256 our_method_table
= interface_table
.setdefault(interface
, {})
257 our_method_table
.update(method_table
)
259 # add in all the name -> method entries for our own methods/signals
260 for func
in dct
.values():
261 if getattr(func
, '_dbus_interface', False):
262 method_table
= interface_table
.setdefault(func
._dbus
_interface
, {})
263 method_table
[func
.__name
__] = func
265 super(InterfaceType
, cls
).__init
__(name
, bases
, dct
)
267 # methods are different to signals, so we have two functions... :)
268 def _reflect_on_method(cls
, func
):
269 args
= func
._dbus
_args
271 if func
._dbus
_in
_signature
:
272 # convert signature into a tuple so length refers to number of
273 # types, not number of characters. the length is checked by
274 # the decorator to make sure it matches the length of args.
275 in_sig
= tuple(_dbus_bindings
.Signature(func
._dbus
_in
_signature
))
277 # magic iterator which returns as many v's as we need
278 in_sig
= _VariantSignature()
280 if func
._dbus
_out
_signature
:
281 out_sig
= _dbus_bindings
.Signature(func
._dbus
_out
_signature
)
283 # its tempting to default to _dbus_bindings.Signature('v'), but
284 # for methods that return nothing, providing incorrect
285 # introspection data is worse than providing none at all
288 reflection_data
= ' <method name="%s">\n' % (func
.__name
__)
289 for pair
in zip(in_sig
, args
):
290 reflection_data
+= ' <arg direction="in" type="%s" name="%s" />\n' % pair
292 reflection_data
+= ' <arg direction="out" type="%s" />\n' % type
293 reflection_data
+= ' </method>\n'
295 return reflection_data
297 def _reflect_on_signal(cls
, func
):
298 args
= func
._dbus
_args
300 if func
._dbus
_signature
:
301 # convert signature into a tuple so length refers to number of
302 # types, not number of characters
303 sig
= tuple(_dbus_bindings
.Signature(func
._dbus
_signature
))
305 # magic iterator which returns as many v's as we need
306 sig
= _VariantSignature()
308 reflection_data
= ' <signal name="%s">\n' % (func
.__name
__)
309 for pair
in zip(sig
, args
):
310 reflection_data
= reflection_data
+ ' <arg type="%s" name="%s" />\n' % pair
311 reflection_data
= reflection_data
+ ' </signal>\n'
313 return reflection_data
315 class Interface(object):
316 __metaclass__
= InterfaceType
318 class Object(Interface
):
319 """A base class for exporting your own Objects across the Bus.
321 Just inherit from Object and provide a list of methods to share
326 - The constructor takes a well-known name: this is wrong. There should be
327 no requirement to register a well-known name in order to export bus
330 def __init__(self
, bus_name
, object_path
):
335 Represents a well-known name claimed by this process. A
336 reference to the BusName object will be held by this
337 Object, preventing the name from being released during this
338 Object's lifetime (subject to status).
340 The D-Bus object path at which to export this Object.
342 self
._object
_path
= object_path
343 self
._name
= bus_name
344 self
._bus
= bus_name
.get_bus()
346 self
._connection
= self
._bus
.get_connection()
348 self
._connection
._register
_object
_path
(object_path
, self
._message
_cb
, self
._unregister
_cb
)
350 def _unregister_cb(self
, connection
):
351 _logger
.info('Unregistering exported object %r', self
)
353 def _message_cb(self
, connection
, message
):
355 # lookup candidate method and parent method
356 method_name
= message
.get_member()
357 interface_name
= message
.get_interface()
358 (candidate_method
, parent_method
) = _method_lookup(self
, method_name
, interface_name
)
360 # set up method call parameters
361 args
= message
.get_args_list()
364 if parent_method
._dbus
_out
_signature
is not None:
365 signature
= _dbus_bindings
.Signature(parent_method
._dbus
_out
_signature
)
369 # set up async callback functions
370 if parent_method
._dbus
_async
_callbacks
:
371 (return_callback
, error_callback
) = parent_method
._dbus
_async
_callbacks
372 keywords
[return_callback
] = lambda *retval
: _method_reply_return(connection
, message
, method_name
, signature
, *retval
)
373 keywords
[error_callback
] = lambda exception
: _method_reply_error(connection
, message
, exception
)
375 # include the sender if desired
376 if parent_method
._dbus
_sender
_keyword
:
377 keywords
[parent_method
._dbus
_sender
_keyword
] = message
.get_sender()
380 retval
= candidate_method(self
, *args
, **keywords
)
382 # we're done - the method has got callback functions to reply with
383 if parent_method
._dbus
_async
_callbacks
:
386 # otherwise we send the return values in a reply. if we have a
387 # signature, use it to turn the return value into a tuple as
389 if signature
is not None:
390 signature_tuple
= tuple(signature
)
391 # if we have zero or one return values we want make a tuple
392 # for the _method_reply_return function, otherwise we need
393 # to check we're passing it a sequence
394 if len(signature_tuple
) == 0:
398 raise TypeError('%s has an empty output signature but did not return None' %
400 elif len(signature_tuple
) == 1:
403 if operator
.isSequenceType(retval
):
404 # multi-value signature, multi-value return... proceed unchanged
407 raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
408 (method_name
, signature
))
410 # no signature, so just turn the return into a tuple and send it as normal
417 _method_reply_return(connection
, message
, method_name
, signature
, *retval
)
418 except Exception, exception
:
420 _method_reply_error(connection
, message
, exception
)
422 @method('org.freedesktop.DBus.Introspectable', in_signature
='', out_signature
='s')
423 def Introspect(self
):
424 """Return a string of XML encoding this object's supported interfaces,
427 reflection_data
= '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">\n'
428 reflection_data
+= '<node name="%s">\n' % (self
._object
_path
)
430 interfaces
= self
._dbus
_class
_table
[self
.__class
__.__module
__ + '.' + self
.__class
__.__name
__]
431 for (name
, funcs
) in interfaces
.iteritems():
432 reflection_data
+= ' <interface name="%s">\n' % (name
)
434 for func
in funcs
.values():
435 if getattr(func
, '_dbus_is_method', False):
436 reflection_data
+= self
.__class
__._reflect
_on
_method
(func
)
437 elif getattr(func
, '_dbus_is_signal', False):
438 reflection_data
+= self
.__class
__._reflect
_on
_signal
(func
)
440 reflection_data
+= ' </interface>\n'
442 reflection_data
+= '</node>\n'
444 return reflection_data
447 return '<dbus.service.Object %s on %r at %#x>' % (self
._object
_path
, self
._name
, id(self
))