1 __all__
= ('BusName', 'Object', 'method', 'signal')
2 __docformat__
= 'restructuredtext'
9 from exceptions
import NameExistsException
10 from exceptions
import UnknownMethodException
11 from decorators
import method
12 from decorators
import signal
14 class BusName(object):
15 """A base class for exporting your own Named Services across the Bus.
17 When instantiated, objects of this class attempt to claim the given
18 well-known name on the given bus for the current process. The name is
19 released when the BusName object becomes unreferenced.
21 If a well-known name is requested multiple times, multiple references
22 to the same BusName object will be returned.
26 - Assumes that named services are only ever requested using this class -
27 if you request names from the bus directly, confusion may occur.
28 - Does not handle queueing.
30 def __new__(cls
, name
, bus
=None, allow_replacement
=False , replace_existing
=False, do_not_queue
=False):
31 """Constructor, which may either return an existing cached object
36 The well-known name to be advertised
38 A Bus on which this service will be advertised; if None
39 (default) a default bus will be used
40 `allow_replacement` : bool
41 If True, other processes trying to claim the same well-known
42 name will take precedence over this one.
43 `replace_existing` : bool
44 If True, this process can take over the well-known name
45 from other processes already holding it.
47 If True, this service will not be placed in the queue of
48 services waiting for the requested name if another service
55 # see if this name is already defined, return it if so
56 # FIXME: accessing internals of Bus
57 if name
in bus
._bus
_names
:
58 return bus
._bus
_names
[name
]
60 # otherwise register the name
62 _dbus_bindings
.NAME_FLAG_ALLOW_REPLACEMENT
* allow_replacement
+ \
63 _dbus_bindings
.NAME_FLAG_REPLACE_EXISTING
* replace_existing
+ \
64 _dbus_bindings
.NAME_FLAG_DO_NOT_QUEUE
* do_not_queue
66 retval
= _dbus_bindings
.bus_request_name(bus
.get_connection(), name
, name_flags
)
68 # TODO: more intelligent tracking of bus name states?
69 if retval
== _dbus_bindings
.REQUEST_NAME_REPLY_PRIMARY_OWNER
:
71 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_IN_QUEUE
:
72 # queueing can happen by default, maybe we should
73 # track this better or let the user know if they're
76 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_EXISTS
:
77 raise NameExistsException(name
)
78 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_ALREADY_OWNER
:
79 # if this is a shared bus which is being used by someone
80 # else in this process, this can happen legitimately
83 raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name
, retval
))
85 # and create the object
86 bus_name
= object.__new
__(cls
)
90 # cache instance (weak ref only)
91 # FIXME: accessing Bus internals again
92 bus
._bus
_names
[name
] = bus_name
96 # do nothing because this is called whether or not the bus name
97 # object was retrieved from the cache or created new
98 def __init__(self
, *args
, **keywords
):
101 # we can delete the low-level name here because these objects
102 # are guaranteed to exist only once for each bus name
104 _dbus_bindings
.bus_release_name(self
._bus
.get_connection(), self
._name
)
108 """Get the Bus this Service is on"""
112 """Get the name of this service"""
116 return '<dbus.service.BusName %s on %r at %#x>' % (self
._name
, self
._bus
, id(self
))
120 def _method_lookup(self
, method_name
, dbus_interface
):
121 """Walks the Python MRO of the given class to find the method to invoke.
123 Returns two methods, the one to call, and the one it inherits from which
124 defines its D-Bus interface name, signature, and attributes.
127 candidate_class
= None
130 # split up the cases when we do and don't have an interface because the
131 # latter is much simpler
133 # search through the class hierarchy in python MRO order
134 for cls
in self
.__class
__.__mro
__:
135 # if we haven't got a candidate class yet, and we find a class with a
136 # suitably named member, save this as a candidate class
137 if (not candidate_class
and method_name
in cls
.__dict
__):
138 if ("_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__
139 and "_dbus_interface" in cls
.__dict
__[method_name
].__dict
__):
140 # however if it is annotated for a different interface
141 # than we are looking for, it cannot be a candidate
142 if cls
.__dict
__[method_name
]._dbus
_interface
== dbus_interface
:
143 candidate_class
= cls
144 parent_method
= cls
.__dict
__[method_name
]
150 candidate_class
= cls
152 # if we have a candidate class, carry on checking this and all
153 # superclasses for a method annoated as a dbus method
154 # on the correct interface
155 if (candidate_class
and method_name
in cls
.__dict
__
156 and "_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__
157 and "_dbus_interface" in cls
.__dict
__[method_name
].__dict
__
158 and cls
.__dict
__[method_name
]._dbus
_interface
== dbus_interface
):
159 # the candidate class has a dbus method on the correct interface,
160 # or overrides a method that is, success!
161 parent_method
= cls
.__dict
__[method_name
]
166 # simpler version of above
167 for cls
in self
.__class
__.__mro
__:
168 if (not candidate_class
and method_name
in cls
.__dict
__):
169 candidate_class
= cls
171 if (candidate_class
and method_name
in cls
.__dict
__
172 and "_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__):
173 parent_method
= cls
.__dict
__[method_name
]
178 return (candidate_class
.__dict
__[method_name
], parent_method
)
181 raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name
, dbus_interface
))
183 raise UnknownMethodException('%s is not a valid method' % method_name
)
186 def _method_reply_return(connection
, message
, method_name
, signature
, *retval
):
187 reply
= _dbus_bindings
.MethodReturn(message
)
188 iter = reply
.get_iter(append
=True)
190 # do strict adding if an output signature was provided
192 if len(signature
) > len(retval
):
193 raise TypeError('output signature %s is longer than the number of values returned by %s' %
194 (signature
, method_name
))
195 elif len(retval
) > len(signature
):
196 raise TypeError('output signature %s is shorter than the number of values returned by %s' %
197 (signature
, method_name
))
199 for (value
, sig
) in zip(retval
, signature
):
200 iter.append_strict(value
, sig
)
202 # no signature, try and guess the return type by inspection
207 connection
.send(reply
)
210 def _method_reply_error(connection
, message
, exception
):
211 if '_dbus_error_name' in exception
.__dict
__:
212 name
= exception
._dbus
_error
_name
213 elif exception
.__module
__ == '__main__':
214 name
= 'org.freedesktop.DBus.Python.%s' % exception
.__class
__.__name
__
216 name
= 'org.freedesktop.DBus.Python.%s.%s' % (exception
.__module
__, exception
.__class
__.__name
__)
218 contents
= traceback
.format_exc()
219 reply
= _dbus_bindings
.Error(message
, name
, contents
)
221 connection
.send(reply
)
224 class InterfaceType(type):
225 def __init__(cls
, name
, bases
, dct
):
226 # these attributes are shared between all instances of the Interface
227 # object, so this has to be a dictionary that maps class names to
228 # the per-class introspection/interface data
229 class_table
= getattr(cls
, '_dbus_class_table', {})
230 cls
._dbus
_class
_table
= class_table
231 interface_table
= class_table
[cls
.__module
__ + '.' + name
] = {}
233 # merge all the name -> method tables for all the interfaces
234 # implemented by our base classes into our own
236 base_name
= b
.__module
__ + '.' + b
.__name
__
237 if getattr(b
, '_dbus_class_table', False):
238 for (interface
, method_table
) in class_table
[base_name
].iteritems():
239 our_method_table
= interface_table
.setdefault(interface
, {})
240 our_method_table
.update(method_table
)
242 # add in all the name -> method entries for our own methods/signals
243 for func
in dct
.values():
244 if getattr(func
, '_dbus_interface', False):
245 method_table
= interface_table
.setdefault(func
._dbus
_interface
, {})
246 method_table
[func
.__name
__] = func
248 super(InterfaceType
, cls
).__init
__(name
, bases
, dct
)
250 # methods are different to signals, so we have two functions... :)
251 def _reflect_on_method(cls
, func
):
252 args
= func
._dbus
_args
254 if func
._dbus
_in
_signature
:
255 # convert signature into a tuple so length refers to number of
256 # types, not number of characters. the length is checked by
257 # the decorator to make sure it matches the length of args.
258 in_sig
= tuple(_dbus_bindings
.Signature(func
._dbus
_in
_signature
))
260 # magic iterator which returns as many v's as we need
261 in_sig
= _dbus_bindings
.VariantSignature()
263 if func
._dbus
_out
_signature
:
264 out_sig
= _dbus_bindings
.Signature(func
._dbus
_out
_signature
)
266 # its tempting to default to _dbus_bindings.Signature('v'), but
267 # for methods that return nothing, providing incorrect
268 # introspection data is worse than providing none at all
271 reflection_data
= ' <method name="%s">\n' % (func
.__name
__)
272 for pair
in zip(in_sig
, args
):
273 reflection_data
+= ' <arg direction="in" type="%s" name="%s" />\n' % pair
275 reflection_data
+= ' <arg direction="out" type="%s" />\n' % type
276 reflection_data
+= ' </method>\n'
278 return reflection_data
280 def _reflect_on_signal(cls
, func
):
281 args
= func
._dbus
_args
283 if func
._dbus
_signature
:
284 # convert signature into a tuple so length refers to number of
285 # types, not number of characters
286 sig
= tuple(_dbus_bindings
.Signature(func
._dbus
_signature
))
288 # magic iterator which returns as many v's as we need
289 sig
= _dbus_bindings
.VariantSignature()
291 reflection_data
= ' <signal name="%s">\n' % (func
.__name
__)
292 for pair
in zip(sig
, args
):
293 reflection_data
= reflection_data
+ ' <arg type="%s" name="%s" />\n' % pair
294 reflection_data
= reflection_data
+ ' </signal>\n'
296 return reflection_data
298 class Interface(object):
299 __metaclass__
= InterfaceType
301 class Object(Interface
):
302 """A base class for exporting your own Objects across the Bus.
304 Just inherit from Object and provide a list of methods to share
309 - The constructor takes a well-known name: this is wrong. There should be
310 no requirement to register a well-known name in order to export bus
313 def __init__(self
, bus_name
, object_path
):
318 Represents a well-known name claimed by this process. A
319 reference to the BusName object will be held by this
320 Object, preventing the name from being released during this
321 Object's lifetime (subject to status).
323 The D-Bus object path at which to export this Object.
325 self
._object
_path
= object_path
326 self
._name
= bus_name
327 self
._bus
= bus_name
.get_bus()
329 self
._connection
= self
._bus
.get_connection()
331 self
._connection
.register_object_path(object_path
, self
._unregister
_cb
, self
._message
_cb
)
333 def _unregister_cb(self
, connection
):
336 def _message_cb(self
, connection
, message
):
338 # lookup candidate method and parent method
339 method_name
= message
.get_member()
340 interface_name
= message
.get_interface()
341 (candidate_method
, parent_method
) = _method_lookup(self
, method_name
, interface_name
)
343 # set up method call parameters
344 args
= message
.get_args_list()
347 # iterate signature into list of complete types
348 if parent_method
._dbus
_out
_signature
:
349 signature
= tuple(_dbus_bindings
.Signature(parent_method
._dbus
_out
_signature
))
353 # set up async callback functions
354 if parent_method
._dbus
_async
_callbacks
:
355 (return_callback
, error_callback
) = parent_method
._dbus
_async
_callbacks
356 keywords
[return_callback
] = lambda *retval
: _method_reply_return(connection
, message
, method_name
, signature
, *retval
)
357 keywords
[error_callback
] = lambda exception
: _method_reply_error(connection
, message
, exception
)
359 # include the sender if desired
360 if parent_method
._dbus
_sender
_keyword
:
361 keywords
[parent_method
._dbus
_sender
_keyword
] = message
.get_sender()
364 retval
= candidate_method(self
, *args
, **keywords
)
366 # we're done - the method has got callback functions to reply with
367 if parent_method
._dbus
_async
_callbacks
:
370 # otherwise we send the return values in a reply. if we have a
371 # signature, use it to turn the return value into a tuple as
373 if parent_method
._dbus
_out
_signature
:
374 # if we have zero or one return values we want make a tuple
375 # for the _method_reply_return function, otherwise we need
376 # to check we're passing it a sequence
377 if len(signature
) == 0:
381 raise TypeError('%s has an empty output signature but did not return None' %
383 elif len(signature
) == 1:
386 if operator
.isSequenceType(retval
):
387 # multi-value signature, multi-value return... proceed unchanged
390 raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
391 (method_name
, signature
))
393 # no signature, so just turn the return into a tuple and send it as normal
401 _method_reply_return(connection
, message
, method_name
, signature
, *retval
)
402 except Exception, exception
:
404 _method_reply_error(connection
, message
, exception
)
406 @method('org.freedesktop.DBus.Introspectable', in_signature
='', out_signature
='s')
407 def Introspect(self
):
408 """Return a string of XML encoding this object's supported interfaces,
411 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'
412 reflection_data
+= '<node name="%s">\n' % (self
._object
_path
)
414 interfaces
= self
._dbus
_class
_table
[self
.__class
__.__module
__ + '.' + self
.__class
__.__name
__]
415 for (name
, funcs
) in interfaces
.iteritems():
416 reflection_data
+= ' <interface name="%s">\n' % (name
)
418 for func
in funcs
.values():
419 if getattr(func
, '_dbus_is_method', False):
420 reflection_data
+= self
.__class
__._reflect
_on
_method
(func
)
421 elif getattr(func
, '_dbus_is_signal', False):
422 reflection_data
+= self
.__class
__._reflect
_on
_signal
(func
)
424 reflection_data
+= ' </interface>\n'
426 reflection_data
+= '</node>\n'
428 return reflection_data
431 return '<dbus.service.Object %s on %r at %#x>' % (self
._object
_path
, self
._name
, id(self
))