home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / share / pycentral / python-dbus / site-packages / dbus / service.py < prev    next >
Encoding:
Python Source  |  2006-07-17  |  15.0 KB  |  371 lines

  1. import dbus_bindings
  2. import _dbus
  3. import operator
  4. import traceback
  5.  
  6. from exceptions import NameExistsException
  7. from exceptions import UnknownMethodException
  8. from decorators import method
  9. from decorators import signal
  10.  
  11. class BusName(object):
  12.     """A base class for exporting your own Named Services across the Bus
  13.     """
  14.     def __new__(cls, name, bus=None):
  15.         # get default bus
  16.         if bus == None:
  17.             bus = _dbus.Bus()
  18.  
  19.         # see if this name is already defined, return it if so
  20.         if name in bus._bus_names:
  21.             return bus._bus_names[name]
  22.  
  23.         # otherwise register the name
  24.         retval = dbus_bindings.bus_request_name(bus.get_connection(), name)
  25.  
  26.         # TODO: more intelligent tracking of bus name states?
  27.         if retval == dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER:
  28.             pass
  29.         elif retval == dbus_bindings.REQUEST_NAME_REPLY_IN_QUEUE:
  30.             # queueing can happen by default, maybe we should
  31.             # track this better or let the user know if they're
  32.             # queued or not?
  33.             pass
  34.         elif retval == dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
  35.             raise NameExistsException(name)
  36.         elif retval == dbus_bindings.REQUEST_NAME_REPLY_ALREADY_OWNER:
  37.             # if this is a shared bus which is being used by someone
  38.             # else in this process, this can happen legitimately
  39.             pass
  40.         else:
  41.             raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name, retval))
  42.  
  43.         # and create the object
  44.         bus_name = object.__new__(cls)
  45.         bus_name._bus = bus
  46.         bus_name._name = name
  47.  
  48.         # cache instance
  49.         bus._bus_names[name] = bus_name
  50.  
  51.         return bus_name
  52.  
  53.     # do nothing because this is called whether or not the bus name
  54.     # object was retrieved from the cache or created new
  55.     def __init__(self, *args, **keywords):
  56.         pass
  57.  
  58.     # we can delete the low-level name here because these objects
  59.     # are guaranteed to exist only once for each bus name
  60.     def __del__(self):
  61.         dbus_bindings.bus_release_name(self._bus.get_connection(), self._name)
  62.         pass
  63.  
  64.     def get_bus(self):
  65.         """Get the Bus this Service is on"""
  66.         return self._bus
  67.  
  68.     def get_name(self):
  69.         """Get the name of this service"""
  70.         return self._name
  71.  
  72.     def __repr__(self):
  73.         return '<dbus.service.BusName %s on %r at %#x>' % (self._name, self._bus, id(self))
  74.     __str__ = __repr__
  75.  
  76.  
  77. def _method_lookup(self, method_name, dbus_interface):
  78.     """Walks the Python MRO of the given class to find the method to invoke.
  79.  
  80.     Returns two methods, the one to call, and the one it inherits from which
  81.     defines its D-Bus interface name, signature, and attributes.
  82.     """
  83.     parent_method = None
  84.     candidate_class = None
  85.     successful = False
  86.  
  87.     # split up the cases when we do and don't have an interface because the
  88.     # latter is much simpler
  89.     if dbus_interface:
  90.         # search through the class hierarchy in python MRO order
  91.         for cls in self.__class__.__mro__:
  92.             # if we haven't got a candidate class yet, and we find a class with a
  93.             # suitably named member, save this as a candidate class
  94.             if (not candidate_class and method_name in cls.__dict__):
  95.                 if ("_dbus_is_method" in cls.__dict__[method_name].__dict__
  96.                     and "_dbus_interface" in cls.__dict__[method_name].__dict__):
  97.                     # however if it is annotated for a different interface
  98.                     # than we are looking for, it cannot be a candidate
  99.                     if cls.__dict__[method_name]._dbus_interface == dbus_interface:
  100.                         candidate_class = cls
  101.                         parent_method = cls.__dict__[method_name]
  102.                         successful = True
  103.                         break
  104.                     else:
  105.                         pass
  106.                 else:
  107.                     candidate_class = cls
  108.  
  109.             # if we have a candidate class, carry on checking this and all
  110.             # superclasses for a method annoated as a dbus method
  111.             # on the correct interface
  112.             if (candidate_class and method_name in cls.__dict__
  113.                 and "_dbus_is_method" in cls.__dict__[method_name].__dict__
  114.                 and "_dbus_interface" in cls.__dict__[method_name].__dict__
  115.                 and cls.__dict__[method_name]._dbus_interface == dbus_interface):
  116.                 # the candidate class has a dbus method on the correct interface,
  117.                 # or overrides a method that is, success!
  118.                 parent_method = cls.__dict__[method_name]
  119.                 successful = True
  120.                 break
  121.  
  122.     else:
  123.         # simpler version of above
  124.         for cls in self.__class__.__mro__:
  125.             if (not candidate_class and method_name in cls.__dict__):
  126.                 candidate_class = cls
  127.  
  128.             if (candidate_class and method_name in cls.__dict__
  129.                 and "_dbus_is_method" in cls.__dict__[method_name].__dict__):
  130.                 parent_method = cls.__dict__[method_name]
  131.                 successful = True
  132.                 break
  133.  
  134.     if successful:
  135.         return (candidate_class.__dict__[method_name], parent_method)
  136.     else:
  137.         if dbus_interface:
  138.             raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface))
  139.         else:
  140.             raise UnknownMethodException('%s is not a valid method' % method_name)
  141.  
  142.  
  143. def _method_reply_return(connection, message, method_name, signature, *retval):
  144.     reply = dbus_bindings.MethodReturn(message)
  145.     iter = reply.get_iter(append=True)
  146.  
  147.     # do strict adding if an output signature was provided
  148.     if signature:
  149.         if len(signature) > len(retval):
  150.             raise TypeError('output signature %s is longer than the number of values returned by %s' %
  151.                 (signature, method_name))
  152.         elif len(retval) > len(signature):
  153.             raise TypeError('output signature %s is shorter than the number of values returned by %s' %
  154.                 (signature, method_name))
  155.         else:
  156.             for (value, sig) in zip(retval, signature):
  157.                 iter.append_strict(value, sig)
  158.  
  159.     # no signature, try and guess the return type by inspection
  160.     else:
  161.         for value in retval:
  162.             iter.append(value)
  163.  
  164.     connection.send(reply)
  165.  
  166.  
  167. def _method_reply_error(connection, message, exception):
  168.     if '_dbus_error_name' in exception.__dict__:
  169.         name = exception._dbus_error_name
  170.     elif exception.__module__ == '__main__':
  171.         name = 'org.freedesktop.DBus.Python.%s' % exception.__class__.__name__
  172.     else:
  173.         name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__)
  174.  
  175.     contents = traceback.format_exc()
  176.     reply = dbus_bindings.Error(message, name, contents)
  177.  
  178.     connection.send(reply)
  179.  
  180.  
  181. class InterfaceType(type):
  182.     def __init__(cls, name, bases, dct):
  183.         # these attributes are shared between all instances of the Interface
  184.         # object, so this has to be a dictionary that maps class names to
  185.         # the per-class introspection/interface data
  186.         class_table = getattr(cls, '_dbus_class_table', {})
  187.         cls._dbus_class_table = class_table
  188.         interface_table = class_table[cls.__module__ + '.' + name] = {}
  189.  
  190.         # merge all the name -> method tables for all the interfaces
  191.         # implemented by our base classes into our own
  192.         for b in bases:
  193.             base_name = b.__module__ + '.' + b.__name__
  194.             if getattr(b, '_dbus_class_table', False):
  195.                 for (interface, method_table) in class_table[base_name].iteritems():
  196.                     our_method_table = interface_table.setdefault(interface, {})
  197.                     our_method_table.update(method_table)
  198.  
  199.         # add in all the name -> method entries for our own methods/signals
  200.         for func in dct.values():
  201.             if getattr(func, '_dbus_interface', False):
  202.                 method_table = interface_table.setdefault(func._dbus_interface, {})
  203.                 method_table[func.__name__] = func
  204.  
  205.         super(InterfaceType, cls).__init__(name, bases, dct)
  206.  
  207.     # methods are different to signals, so we have two functions... :)
  208.     def _reflect_on_method(cls, func):
  209.         args = func._dbus_args
  210.  
  211.         if func._dbus_in_signature:
  212.             # convert signature into a tuple so length refers to number of
  213.             # types, not number of characters. the length is checked by
  214.             # the decorator to make sure it matches the length of args.
  215.             in_sig = tuple(dbus_bindings.Signature(func._dbus_in_signature))
  216.         else:
  217.             # magic iterator which returns as many v's as we need
  218.             in_sig = dbus_bindings.VariantSignature()
  219.  
  220.         if func._dbus_out_signature:
  221.             out_sig = dbus_bindings.Signature(func._dbus_out_signature)
  222.         else:
  223.             # its tempting to default to dbus_bindings.Signature('v'), but
  224.             # for methods that return nothing, providing incorrect
  225.             # introspection data is worse than providing none at all
  226.             out_sig = []
  227.  
  228.         reflection_data = '    <method name="%s">\n' % (func.__name__)
  229.         for pair in zip(in_sig, args):
  230.             reflection_data += '      <arg direction="in"  type="%s" name="%s" />\n' % pair
  231.         for type in out_sig:
  232.             reflection_data += '      <arg direction="out" type="%s" />\n' % type
  233.         reflection_data += '    </method>\n'
  234.  
  235.         return reflection_data
  236.  
  237.     def _reflect_on_signal(cls, func):
  238.         args = func._dbus_args
  239.  
  240.         if func._dbus_signature:
  241.             # convert signature into a tuple so length refers to number of
  242.             # types, not number of characters
  243.             sig = tuple(dbus_bindings.Signature(func._dbus_signature))
  244.         else:
  245.             # magic iterator which returns as many v's as we need
  246.             sig = dbus_bindings.VariantSignature()
  247.  
  248.         reflection_data = '    <signal name="%s">\n' % (func.__name__)
  249.         for pair in zip(sig, args):
  250.             reflection_data = reflection_data + '      <arg type="%s" name="%s" />\n' % pair
  251.         reflection_data = reflection_data + '    </signal>\n'
  252.  
  253.         return reflection_data
  254.  
  255. class Interface(object):
  256.     __metaclass__ = InterfaceType
  257.  
  258. class Object(Interface):
  259.     """A base class for exporting your own Objects across the Bus.
  260.  
  261.     Just inherit from Object and provide a list of methods to share
  262.     across the Bus
  263.     """
  264.     def __init__(self, bus_name, object_path):
  265.         self._object_path = object_path
  266.         self._name = bus_name 
  267.         self._bus = bus_name.get_bus()
  268.             
  269.         self._connection = self._bus.get_connection()
  270.  
  271.         self._connection.register_object_path(object_path, self._unregister_cb, self._message_cb)
  272.  
  273.     def _unregister_cb(self, connection):
  274.         print ("Unregister")
  275.  
  276.     def _message_cb(self, connection, message):
  277.         try:
  278.             # lookup candidate method and parent method
  279.             method_name = message.get_member()
  280.             interface_name = message.get_interface()
  281.             (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)
  282.  
  283.             # set up method call parameters
  284.             args = message.get_args_list()
  285.             keywords = {}
  286.  
  287.             # iterate signature into list of complete types
  288.             if parent_method._dbus_out_signature:
  289.                 signature = tuple(dbus_bindings.Signature(parent_method._dbus_out_signature))
  290.             else:
  291.                 signature = None
  292.  
  293.             # set up async callback functions
  294.             if parent_method._dbus_async_callbacks:
  295.                 (return_callback, error_callback) = parent_method._dbus_async_callbacks
  296.                 keywords[return_callback] = lambda *retval: _method_reply_return(connection, message, method_name, signature, *retval)
  297.                 keywords[error_callback] = lambda exception: _method_reply_error(connection, message, exception)
  298.  
  299.             # include the sender if desired
  300.             if parent_method._dbus_sender_keyword:
  301.                 keywords[parent_method._dbus_sender_keyword] = message.get_sender()
  302.  
  303.             # call method
  304.             retval = candidate_method(self, *args, **keywords)
  305.  
  306.             # we're done - the method has got callback functions to reply with
  307.             if parent_method._dbus_async_callbacks:
  308.                 return
  309.  
  310.             # otherwise we send the return values in a reply. if we have a
  311.             # signature, use it to turn the return value into a tuple as
  312.             # appropriate
  313.             if parent_method._dbus_out_signature:
  314.                 # if we have zero or one return values we want make a tuple
  315.                 # for the _method_reply_return function, otherwise we need
  316.                 # to check we're passing it a sequence
  317.                 if len(signature) == 0:
  318.                     if retval == None:
  319.                         retval = ()
  320.                     else:
  321.                         raise TypeError('%s has an empty output signature but did not return None' %
  322.                             method_name)
  323.                 elif len(signature) == 1:
  324.                     retval = (retval,)
  325.                 else:
  326.                     if operator.isSequenceType(retval):
  327.                         # multi-value signature, multi-value return... proceed unchanged
  328.                         pass
  329.                     else:
  330.                         raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
  331.                             (method_name, signature))
  332.  
  333.             # no signature, so just turn the return into a tuple and send it as normal
  334.             else:
  335.                 signature = None
  336.                 if retval == None:
  337.                     retval = ()
  338.                 else:
  339.                     retval = (retval,)
  340.  
  341.             _method_reply_return(connection, message, method_name, signature, *retval)
  342.         except Exception, exception:
  343.             # send error reply
  344.             _method_reply_error(connection, message, exception)
  345.  
  346.     @method('org.freedesktop.DBus.Introspectable', in_signature='', out_signature='s')
  347.     def Introspect(self):
  348.         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'
  349.         reflection_data += '<node name="%s">\n' % (self._object_path)
  350.  
  351.         interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__]
  352.         for (name, funcs) in interfaces.iteritems():
  353.             reflection_data += '  <interface name="%s">\n' % (name)
  354.  
  355.             for func in funcs.values():
  356.                 if getattr(func, '_dbus_is_method', False):
  357.                     reflection_data += self.__class__._reflect_on_method(func)
  358.                 elif getattr(func, '_dbus_is_signal', False):
  359.                     reflection_data += self.__class__._reflect_on_signal(func)
  360.  
  361.             reflection_data += '  </interface>\n'
  362.  
  363.         reflection_data += '</node>\n'
  364.  
  365.         return reflection_data
  366.  
  367.     def __repr__(self):
  368.         return '<dbus.service.Object %s on %r at %#x>' % (self._object_path, self._name, id(self))
  369.     __str__ = __repr__
  370.  
  371.