Don't leak memory in _StringBase and _LongBase repr()
[dbus-python-phuang.git] / dbus / service.py
blob9f92a965f27b0ff396d5d3e92f2adcf41c4baf0a
1 # Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
2 # Copyright (C) 2003 David Zeuthen
3 # Copyright (C) 2004 Rob Taylor
4 # Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
6 # Licensed under the Academic Free License version 2.1
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 __all__ = ('BusName', 'Object', 'method', 'signal')
23 __docformat__ = 'restructuredtext'
25 import sys
26 import logging
27 import operator
28 import traceback
30 import _dbus_bindings
31 import dbus._dbus as _dbus
32 from dbus.exceptions import NameExistsException
33 from dbus.exceptions import UnknownMethodException
34 from dbus.decorators import method
35 from dbus.decorators import signal
38 _logger = logging.getLogger('dbus.service')
41 class _VariantSignature(object):
42 """A fake method signature which, when iterated, yields an endless stream
43 of 'v' characters representing variants (handy with zip()).
45 It has no string representation.
46 """
47 def __iter__(self):
48 """Return self."""
49 return self
51 def next(self):
52 """Return 'v' whenever called."""
53 return 'v'
55 class BusName(object):
56 """A base class for exporting your own Named Services across the Bus.
58 When instantiated, objects of this class attempt to claim the given
59 well-known name on the given bus for the current process. The name is
60 released when the BusName object becomes unreferenced.
62 If a well-known name is requested multiple times, multiple references
63 to the same BusName object will be returned.
65 Caveats
66 -------
67 - Assumes that named services are only ever requested using this class -
68 if you request names from the bus directly, confusion may occur.
69 - Does not handle queueing.
70 """
71 def __new__(cls, name, bus=None, allow_replacement=False , replace_existing=False, do_not_queue=False):
72 """Constructor, which may either return an existing cached object
73 or a new object.
75 :Parameters:
76 `name` : str
77 The well-known name to be advertised
78 `bus` : dbus.Bus
79 A Bus on which this service will be advertised; if None
80 (default) the session bus will be used
81 `allow_replacement` : bool
82 If True, other processes trying to claim the same well-known
83 name will take precedence over this one.
84 `replace_existing` : bool
85 If True, this process can take over the well-known name
86 from other processes already holding it.
87 `do_not_queue` : bool
88 If True, this service will not be placed in the queue of
89 services waiting for the requested name if another service
90 already holds it.
91 """
92 # get default bus
93 if bus == None:
94 bus = _dbus.Bus()
96 # see if this name is already defined, return it if so
97 # FIXME: accessing internals of Bus
98 if name in bus._bus_names:
99 return bus._bus_names[name]
101 # otherwise register the name
102 name_flags = (
103 (allow_replacement and _dbus_bindings.NAME_FLAG_ALLOW_REPLACEMENT or 0) |
104 (replace_existing and _dbus_bindings.NAME_FLAG_REPLACE_EXISTING or 0) |
105 (do_not_queue and _dbus_bindings.NAME_FLAG_DO_NOT_QUEUE or 0))
107 retval = bus.request_name(name, name_flags)
109 # TODO: more intelligent tracking of bus name states?
110 if retval == _dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER:
111 pass
112 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_IN_QUEUE:
113 # queueing can happen by default, maybe we should
114 # track this better or let the user know if they're
115 # queued or not?
116 pass
117 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
118 raise NameExistsException(name)
119 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_ALREADY_OWNER:
120 # if this is a shared bus which is being used by someone
121 # else in this process, this can happen legitimately
122 pass
123 else:
124 raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name, retval))
126 # and create the object
127 bus_name = object.__new__(cls)
128 bus_name._bus = bus
129 bus_name._name = name
131 # cache instance (weak ref only)
132 # FIXME: accessing Bus internals again
133 bus._bus_names[name] = bus_name
135 return bus_name
137 # do nothing because this is called whether or not the bus name
138 # object was retrieved from the cache or created new
139 def __init__(self, *args, **keywords):
140 pass
142 # we can delete the low-level name here because these objects
143 # are guaranteed to exist only once for each bus name
144 def __del__(self):
145 self._bus.release_name(self._name)
146 pass
148 def get_bus(self):
149 """Get the Bus this Service is on"""
150 return self._bus
152 def get_name(self):
153 """Get the name of this service"""
154 return self._name
156 def __repr__(self):
157 return '<dbus.service.BusName %s on %r at %#x>' % (self._name, self._bus, id(self))
158 __str__ = __repr__
161 def _method_lookup(self, method_name, dbus_interface):
162 """Walks the Python MRO of the given class to find the method to invoke.
164 Returns two methods, the one to call, and the one it inherits from which
165 defines its D-Bus interface name, signature, and attributes.
167 parent_method = None
168 candidate_class = None
169 successful = False
171 # split up the cases when we do and don't have an interface because the
172 # latter is much simpler
173 if dbus_interface:
174 # search through the class hierarchy in python MRO order
175 for cls in self.__class__.__mro__:
176 # if we haven't got a candidate class yet, and we find a class with a
177 # suitably named member, save this as a candidate class
178 if (not candidate_class and method_name in cls.__dict__):
179 if ("_dbus_is_method" in cls.__dict__[method_name].__dict__
180 and "_dbus_interface" in cls.__dict__[method_name].__dict__):
181 # however if it is annotated for a different interface
182 # than we are looking for, it cannot be a candidate
183 if cls.__dict__[method_name]._dbus_interface == dbus_interface:
184 candidate_class = cls
185 parent_method = cls.__dict__[method_name]
186 successful = True
187 break
188 else:
189 pass
190 else:
191 candidate_class = cls
193 # if we have a candidate class, carry on checking this and all
194 # superclasses for a method annoated as a dbus method
195 # on the correct interface
196 if (candidate_class and method_name in cls.__dict__
197 and "_dbus_is_method" in cls.__dict__[method_name].__dict__
198 and "_dbus_interface" in cls.__dict__[method_name].__dict__
199 and cls.__dict__[method_name]._dbus_interface == dbus_interface):
200 # the candidate class has a dbus method on the correct interface,
201 # or overrides a method that is, success!
202 parent_method = cls.__dict__[method_name]
203 successful = True
204 break
206 else:
207 # simpler version of above
208 for cls in self.__class__.__mro__:
209 if (not candidate_class and method_name in cls.__dict__):
210 candidate_class = cls
212 if (candidate_class and method_name in cls.__dict__
213 and "_dbus_is_method" in cls.__dict__[method_name].__dict__):
214 parent_method = cls.__dict__[method_name]
215 successful = True
216 break
218 if successful:
219 return (candidate_class.__dict__[method_name], parent_method)
220 else:
221 if dbus_interface:
222 raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface))
223 else:
224 raise UnknownMethodException('%s is not a valid method' % method_name)
227 # FIXME: if signature is '', we used to accept anything, but now insist on
228 # zero args. Which was desired? -smcv
229 def _method_reply_return(connection, message, method_name, signature, *retval):
230 reply = _dbus_bindings.MethodReturnMessage(message)
231 try:
232 reply.append(signature=signature, *retval)
233 except Exception, e:
234 if signature is None:
235 try:
236 signature = reply.guess_signature(retval) + ' (guessed)'
237 except Exception, e:
238 _logger.error('Unable to guess signature for arguments %r: '
239 '%s: %s', retval, e.__class__, e)
240 raise
241 _logger.error('Unable to append %r to message with signature %s: '
242 '%s: %s', retval, signature, e.__class__, e)
243 raise
245 connection.send_message(reply)
248 def _method_reply_error(connection, message, exception):
249 if hasattr(exception, '_dbus_error_name'):
250 name = exception._dbus_error_name
251 elif getattr(exception, '__module__', '') in ('', '__main__'):
252 name = 'org.freedesktop.DBus.Python.%s' % exception.__class__.__name__
253 else:
254 name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__)
256 contents = traceback.format_exc()
257 reply = _dbus_bindings.ErrorMessage(message, name, contents)
259 connection.send_message(reply)
262 class InterfaceType(type):
263 def __init__(cls, name, bases, dct):
264 # these attributes are shared between all instances of the Interface
265 # object, so this has to be a dictionary that maps class names to
266 # the per-class introspection/interface data
267 class_table = getattr(cls, '_dbus_class_table', {})
268 cls._dbus_class_table = class_table
269 interface_table = class_table[cls.__module__ + '.' + name] = {}
271 # merge all the name -> method tables for all the interfaces
272 # implemented by our base classes into our own
273 for b in bases:
274 base_name = b.__module__ + '.' + b.__name__
275 if getattr(b, '_dbus_class_table', False):
276 for (interface, method_table) in class_table[base_name].iteritems():
277 our_method_table = interface_table.setdefault(interface, {})
278 our_method_table.update(method_table)
280 # add in all the name -> method entries for our own methods/signals
281 for func in dct.values():
282 if getattr(func, '_dbus_interface', False):
283 method_table = interface_table.setdefault(func._dbus_interface, {})
284 method_table[func.__name__] = func
286 super(InterfaceType, cls).__init__(name, bases, dct)
288 # methods are different to signals, so we have two functions... :)
289 def _reflect_on_method(cls, func):
290 args = func._dbus_args
292 if func._dbus_in_signature:
293 # convert signature into a tuple so length refers to number of
294 # types, not number of characters. the length is checked by
295 # the decorator to make sure it matches the length of args.
296 in_sig = tuple(_dbus_bindings.Signature(func._dbus_in_signature))
297 else:
298 # magic iterator which returns as many v's as we need
299 in_sig = _VariantSignature()
301 if func._dbus_out_signature:
302 out_sig = _dbus_bindings.Signature(func._dbus_out_signature)
303 else:
304 # its tempting to default to _dbus_bindings.Signature('v'), but
305 # for methods that return nothing, providing incorrect
306 # introspection data is worse than providing none at all
307 out_sig = []
309 reflection_data = ' <method name="%s">\n' % (func.__name__)
310 for pair in zip(in_sig, args):
311 reflection_data += ' <arg direction="in" type="%s" name="%s" />\n' % pair
312 for type in out_sig:
313 reflection_data += ' <arg direction="out" type="%s" />\n' % type
314 reflection_data += ' </method>\n'
316 return reflection_data
318 def _reflect_on_signal(cls, func):
319 args = func._dbus_args
321 if func._dbus_signature:
322 # convert signature into a tuple so length refers to number of
323 # types, not number of characters
324 sig = tuple(_dbus_bindings.Signature(func._dbus_signature))
325 else:
326 # magic iterator which returns as many v's as we need
327 sig = _VariantSignature()
329 reflection_data = ' <signal name="%s">\n' % (func.__name__)
330 for pair in zip(sig, args):
331 reflection_data = reflection_data + ' <arg type="%s" name="%s" />\n' % pair
332 reflection_data = reflection_data + ' </signal>\n'
334 return reflection_data
336 class Interface(object):
337 __metaclass__ = InterfaceType
339 class Object(Interface):
340 r"""A base class for exporting your own Objects across the Bus.
342 Just inherit from Object and mark exported methods with the
343 @\ `dbus.service.method` or @\ `dbus.service.signal` decorator.
345 Example::
347 class Example(dbus.service.object):
348 def __init__(self, object_path):
349 dbus.service.Object.__init__(self, dbus.SessionBus(), path)
350 self._last_input = None
352 @dbus.service.method(interface='com.example.Sample',
353 in_signature='v', out_signature='s')
354 def StringifyVariant(self, var):
355 self.LastInputChanged(var) # emits the signal
356 return str(var)
358 @dbus.service.signal(interface='com.example.Sample',
359 signature='v')
360 def LastInputChanged(self, var):
361 # run just before the signal is actually emitted
362 # just put "pass" if nothing should happen
363 self._last_input = var
365 @dbus.service.method(interface='com.example.Sample',
366 in_signature='', out_signature='v')
367 def GetLastInput(self):
368 return self._last_input
371 # the signature of __init__ is a bit mad, for backwards compatibility
372 def __init__(self, conn=None, object_path=None, bus_name=None):
373 """Constructor. Either conn or bus_name is required; object_path
374 is also required.
376 :Parameters:
377 `conn` : dbus.Connection
378 The connection on which to export this object.
380 If None, use the Bus associated with the given ``bus_name``,
381 or raise TypeError if there is no ``bus_name`` either.
383 For backwards compatibility, if an instance of
384 dbus.service.BusName is passed as the first parameter,
385 this is equivalent to passing its associated Bus as
386 ``conn``, and passing the BusName itself as ``bus_name``.
388 `object_path` : str
389 The D-Bus object path at which to export this Object.
391 `bus_name` : dbus.service.BusName
392 Represents a well-known name claimed by this process. A
393 reference to the BusName object will be held by this
394 Object, preventing the name from being released during this
395 Object's lifetime (unless it's released manually).
397 if object_path is None:
398 raise TypeError('The object_path argument is required')
400 if isinstance(conn, BusName):
401 # someone's using the old API; don't gratuitously break them
402 bus_name = conn
403 conn = bus_name.get_bus()
404 elif conn is None:
405 # someone's using the old API but naming arguments, probably
406 if bus_name is None:
407 raise TypeError('Either conn or bus_name is required')
408 conn = bus_name.get_bus()
410 self._object_path = object_path
411 self._name = bus_name
412 self._bus = conn
414 self._connection = self._bus.get_connection()
416 self._connection._register_object_path(object_path, self._message_cb, self._unregister_cb)
418 __dbus_object_path__ = property(lambda self: self._object_path, None, None,
419 "The D-Bus object path of this object")
421 def _unregister_cb(self, connection):
422 _logger.info('Unregistering exported object %r', self)
424 def _message_cb(self, connection, message):
425 try:
426 # lookup candidate method and parent method
427 method_name = message.get_member()
428 interface_name = message.get_interface()
429 (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)
431 # set up method call parameters
432 args = message.get_args_list(**parent_method._dbus_get_args_options)
433 keywords = {}
435 if parent_method._dbus_out_signature is not None:
436 signature = _dbus_bindings.Signature(parent_method._dbus_out_signature)
437 else:
438 signature = None
440 # set up async callback functions
441 if parent_method._dbus_async_callbacks:
442 (return_callback, error_callback) = parent_method._dbus_async_callbacks
443 keywords[return_callback] = lambda *retval: _method_reply_return(connection, message, method_name, signature, *retval)
444 keywords[error_callback] = lambda exception: _method_reply_error(connection, message, exception)
446 # include the sender if desired
447 if parent_method._dbus_sender_keyword:
448 keywords[parent_method._dbus_sender_keyword] = message.get_sender()
450 # call method
451 retval = candidate_method(self, *args, **keywords)
453 # we're done - the method has got callback functions to reply with
454 if parent_method._dbus_async_callbacks:
455 return
457 # otherwise we send the return values in a reply. if we have a
458 # signature, use it to turn the return value into a tuple as
459 # appropriate
460 if signature is not None:
461 signature_tuple = tuple(signature)
462 # if we have zero or one return values we want make a tuple
463 # for the _method_reply_return function, otherwise we need
464 # to check we're passing it a sequence
465 if len(signature_tuple) == 0:
466 if retval == None:
467 retval = ()
468 else:
469 raise TypeError('%s has an empty output signature but did not return None' %
470 method_name)
471 elif len(signature_tuple) == 1:
472 retval = (retval,)
473 else:
474 if operator.isSequenceType(retval):
475 # multi-value signature, multi-value return... proceed unchanged
476 pass
477 else:
478 raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
479 (method_name, signature))
481 # no signature, so just turn the return into a tuple and send it as normal
482 else:
483 if retval == None:
484 retval = ()
485 else:
486 retval = (retval,)
488 _method_reply_return(connection, message, method_name, signature, *retval)
489 except Exception, exception:
490 # send error reply
491 _method_reply_error(connection, message, exception)
493 @method('org.freedesktop.DBus.Introspectable', in_signature='', out_signature='s')
494 def Introspect(self):
495 """Return a string of XML encoding this object's supported interfaces,
496 methods and signals.
498 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'
499 reflection_data += '<node name="%s">\n' % (self._object_path)
501 interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__]
502 for (name, funcs) in interfaces.iteritems():
503 reflection_data += ' <interface name="%s">\n' % (name)
505 for func in funcs.values():
506 if getattr(func, '_dbus_is_method', False):
507 reflection_data += self.__class__._reflect_on_method(func)
508 elif getattr(func, '_dbus_is_signal', False):
509 reflection_data += self.__class__._reflect_on_signal(func)
511 reflection_data += ' </interface>\n'
513 reflection_data += '</node>\n'
515 return reflection_data
517 def __repr__(self):
518 return '<dbus.service.Object %s on %r at %#x>' % (self._object_path, self._name, id(self))
519 __str__ = __repr__