* bus/dispatch.c, test/test-service.c: Add testcase
[platform/upstream/dbus.git] / python / service.py
1 from decorators import *
2
3 import dbus_bindings 
4
5 class BusName:
6     """A base class for exporting your own Named Services across the Bus
7     """
8     def __init__(self, named_service, bus=None):
9         self._named_service = named_service
10                              
11         if bus == None:
12             # Get the default bus
13             self._bus = Bus()
14         else:
15             self._bus = bus
16
17         dbus_bindings.bus_request_name(self._bus.get_connection(), named_service)
18
19     def get_bus(self):
20         """Get the Bus this Service is on"""
21         return self._bus
22
23     def get_name(self):
24         """Get the name of this service"""
25         return self._named_service
26
27 def _dispatch_dbus_method_call(target_methods, self, argument_list, message):
28     """Calls method_to_call using argument_list, but handles
29     exceptions, etc, and generates a reply to the DBus Message message
30     """
31     try:
32         target_method = None
33         
34         dbus_interface = message.get_interface()
35         if dbus_interface == None:
36             if target_methods:
37                 target_method = target_methods[0]
38         else:
39             for dbus_method in target_methods:
40                 if dbus_method._dbus_interface == dbus_interface:
41                     target_method = dbus_method
42                     break
43         
44         if target_method:
45             retval = target_method(self, *argument_list)
46         else:
47             if not dbus_interface:
48                 raise UnknownMethodException('%s is not a valid method'%(message.get_member()))
49             else:
50                 raise UnknownMethodException('%s is not a valid method of interface %s'%(message.get_member(), dbus_interface))
51     except Exception, e:
52         if e.__module__ == '__main__':
53             # FIXME: is it right to use .__name__ here?
54             error_name = e.__class__.__name__
55         else:
56             error_name = e.__module__ + '.' + str(e.__class__.__name__)
57             error_contents = str(e)
58             reply = dbus_bindings.Error(message, error_name, error_contents)
59     else:
60         reply = dbus_bindings.MethodReturn(message)
61         if retval != None:
62             iter = reply.get_iter(append=True)
63             iter.append(retval)
64             
65     return reply
66
67 class ObjectType(type):
68     def __init__(cls, name, bases, dct):
69
70         #generate out vtable
71         method_vtable = getattr(cls, '_dbus_method_vtable', {})
72         reflection_data = getattr(cls, '_dbus_reflection_data', "")
73
74         reflection_interface_method_hash = {}
75         reflection_interface_signal_hash = {}
76
77         for func in dct.values():
78             if getattr(func, '_dbus_is_method', False):
79                 if method_vtable.has_key(func.__name__):
80                     method_vtable[func.__name__].append(func)
81                 else:
82                     method_vtable[func.__name__] = [func]
83                 
84                 #generate a hash of interfaces so we can group
85                 #methods in the xml data
86                 if reflection_interface_method_hash.has_key(func._dbus_interface):
87                     reflection_interface_method_hash[func._dbus_interface].append(func)
88                 else:
89                     reflection_interface_method_hash[func._dbus_interface] = [func]
90
91             elif getattr(func, '_dbus_is_signal', False):
92                 if reflection_interface_signal_hash.has_key(func._dbus_interface):
93                     reflection_interface_signal_hash[func._dbus_interface].append(func)
94                 else:
95                     reflection_interface_signal_hash[func._dbus_interface] = [func]
96
97         for interface in reflection_interface_method_hash.keys():
98             reflection_data = reflection_data + '  <interface name="%s">\n'%(interface)
99             for func in reflection_interface_method_hash[interface]:
100                 reflection_data = reflection_data + cls._reflect_on_method(func)
101
102             if reflection_interface_signal_hash.has_key(interface):
103                 for func in reflection_interface_signal_hash[interface]:
104                     reflection_data = reflection_data + cls._reflect_on_signal(func)
105
106                 del reflection_interface_signal_hash[interface]
107                 
108             reflection_data = reflection_data + '  </interface>\n'
109
110         for interface in reflection_interface_signal_hash.keys():
111             reflection_data = reflection_data + '  <interface name="%s">\n'%(interface)
112             
113             for func in reflection_interface_signal_hash[interface]:
114                 reflection_data = reflection_data + cls._reflect_on_signal(func)
115
116             reflection_data = reflection_data + '  </interface>\n'
117
118         cls._dbus_reflection_data = reflection_data  
119         cls._dbus_method_vtable = method_vtable
120         
121         super(ObjectType, cls).__init__(name, bases, dct)
122
123     #reflections on methods and signals may look like similar code but may in fact
124     #diverge in the future so keep them seperate
125     def _reflect_on_method(cls, func):
126         reflection_data = '    <method name="%s">\n'%(func.__name__)
127         for arg in func._dbus_args:
128             reflection_data = reflection_data + '      <arg name="%s" type="v" />\n'%(arg)
129
130             #reclaim some memory
131             func._dbus_args = None
132             reflection_data = reflection_data + '    </method>\n'
133
134         return reflection_data  
135              
136     def _reflect_on_signal(cls, func):
137         reflection_data = '    <signal name="%s">\n'%(func.__name__)
138         for arg in func._dbus_args:
139             reflection_data = reflection_data + '      <arg name="%s" type="v" />\n'%(arg)
140             #reclaim some memory
141             func._dbus_args = None
142             reflection_data = reflection_data + '    </signal>\n'
143
144         return reflection_data
145
146 class Object:
147     """A base class for exporting your own Objects across the Bus.
148
149     Just inherit from Object and provide a list of methods to share
150     across the Bus
151     """
152     __metaclass__ = ObjectType
153     
154     def __init__(self, bus_name, object_path):
155         self._object_path = object_path
156         self._name = bus_name 
157         self._bus = name.get_bus()
158             
159         self._connection = self._bus.get_connection()
160
161         self._connection.register_object_path(object_path, self._unregister_cb, self._message_cb)
162
163     def _unregister_cb(self, connection):
164         print ("Unregister")
165
166     def _message_cb(self, connection, message):
167         target_method_name = message.get_member()
168         target_methods = self._dbus_method_vtable[target_method_name]
169         args = message.get_args_list()
170         
171         reply = _dispatch_dbus_method_call(target_methods, self, args, message)
172
173         self._connection.send(reply)
174
175     @method('org.freedesktop.DBus.Introspectable')
176     def Introspect(self):
177         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'
178         reflection_data = reflection_data + '<node name="%s">\n'%(self._object_path)
179         reflection_data = reflection_data + self._dbus_reflection_data
180         reflection_data = reflection_data + '</node>\n'
181
182         return reflection_data
183