summaryrefslogtreecommitdiffstats
path: root/python/services.py
blob: 7e0b58b773a6218aff0fc430e704c86ad97f2769 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
from decorators import *

import dbus_bindings 

class Service:
    """A base class for exporting your own Services across the Bus

    Just inherit from Service, providing the name of your service
    (e.g. org.designfu.SampleService).
    """
    def __init__(self, named_service, bus=None):
        self._named_service = named_service
                             
        if bus == None:
            # Get the default bus
            self._bus = Bus()
        else:
            self._bus = bus

        dbus_bindings.bus_request_name(self._bus.get_connection(), named_service)

    def get_bus(self):
        """Get the Bus this Service is on"""
        return self._bus

    def get_name(self):
        """Get the name of this service"""
        return self._named_service

def _dispatch_dbus_method_call(target_methods, self, argument_list, message):
    """Calls method_to_call using argument_list, but handles
    exceptions, etc, and generates a reply to the DBus Message message
    """
    try:
        target_method = None
        
        dbus_interface = message.get_interface()
        if dbus_interface == None:
            if target_methods:
                target_method = target_methods[0]
        else:
            for dbus_method in target_methods:
                if dbus_method._dbus_interface == dbus_interface:
                    target_method = dbus_method
                    break
        
        if target_method:
            retval = target_method(self, *argument_list)
        else:
            if not dbus_interface:
                raise UnknownMethodException('%s is not a valid method'%(message.get_member()))
            else:
                raise UnknownMethodException('%s is not a valid method of interface %s'%(message.get_member(), dbus_interface))
    except Exception, e:
        if e.__module__ == '__main__':
            # FIXME: is it right to use .__name__ here?
            error_name = e.__class__.__name__
        else:
            error_name = e.__module__ + '.' + str(e.__class__.__name__)
            error_contents = str(e)
            reply = dbus_bindings.Error(message, error_name, error_contents)
    else:
        reply = dbus_bindings.MethodReturn(message)
        if retval != None:
            iter = reply.get_iter(append=True)
            iter.append(retval)
	    
    return reply

class ObjectType(type):
    def __init__(cls, name, bases, dct):

        #generate out vtable
        method_vtable = getattr(cls, '_dbus_method_vtable', {})
        reflection_data = getattr(cls, '_dbus_reflection_data', "")

        reflection_interface_method_hash = {}
        reflection_interface_signal_hash = {}

        for func in dct.values():
            if getattr(func, '_dbus_is_method', False):
                if method_vtable.has_key(func.__name__):
                    method_vtable[func.__name__].append(func)
                else:
	            method_vtable[func.__name__] = [func]
                
                #generate a hash of interfaces so we can group
                #methods in the xml data
                if reflection_interface_method_hash.has_key(func._dbus_interface):
                    reflection_interface_method_hash[func._dbus_interface].append(func)
                else:
                    reflection_interface_method_hash[func._dbus_interface] = [func]

            elif getattr(func, '_dbus_is_signal', False):
                if reflection_interface_signal_hash.has_key(func._dbus_interface):
                    reflection_interface_signal_hash[func._dbus_interface].append(func)
                else:
                    reflection_interface_signal_hash[func._dbus_interface] = [func]

	for interface in reflection_interface_method_hash.keys():
            reflection_data = reflection_data + '  <interface name="%s">\n'%(interface)
            for func in reflection_interface_method_hash[interface]:
                reflection_data = reflection_data + cls._reflect_on_method(func)

            if reflection_interface_signal_hash.has_key(interface):
                for func in reflection_interface_signal_hash[interface]:
                    reflection_data = reflection_data + cls._reflect_on_signal(func)

                del reflection_interface_signal_hash[interface]
                
            reflection_data = reflection_data + '  </interface>\n'

	for interface in reflection_interface_signal_hash.keys():
            reflection_data = reflection_data + '  <interface name="%s">\n'%(interface)
            
            for func in reflection_interface_signal_hash[interface]:
                reflection_data = reflection_data + cls._reflect_on_signal(func)

            reflection_data = reflection_data + '  </interface>\n'

        cls._dbus_reflection_data = reflection_data  
	cls._dbus_method_vtable = method_vtable
        
        super(ObjectType, cls).__init__(name, bases, dct)

    #reflections on methods and signals may look like similar code but may in fact
    #diverge in the future so keep them seperate
    def _reflect_on_method(cls, func):
        reflection_data = '    <method name="%s">\n'%(func.__name__)
        for arg in func._dbus_args:
            reflection_data = reflection_data + '      <arg name="%s" type="v" />\n'%(arg)

            #reclaim some memory
            func._dbus_args = None
            reflection_data = reflection_data + '    </method>\n'

        return reflection_data  
             
    def _reflect_on_signal(cls, func):
        reflection_data = '    <signal name="%s">\n'%(func.__name__)
        for arg in func._dbus_args:
            reflection_data = reflection_data + '      <arg name="%s" type="v" />\n'%(arg)
            #reclaim some memory
            func._dbus_args = None
            reflection_data = reflection_data + '    </signal>\n'

        return reflection_data

class Object:
    """A base class for exporting your own Objects across the Bus.

    Just inherit from Object and provide a list of methods to share
    across the Bus. These will appear as member functions of your
    ServiceObject.
    """
    __metaclass__ = ObjectType
    
    def __init__(self, object_path, service):
        self._object_path = object_path
        self._service = service
        self._bus = service.get_bus()
            
        self._connection = self._bus.get_connection()

        self._connection.register_object_path(object_path, self._unregister_cb, self._message_cb)

    def _unregister_cb(self, connection):
        print ("Unregister")

    def _message_cb(self, connection, message):
        target_method_name = message.get_member()
        target_methods = self._dbus_method_vtable[target_method_name]
        args = message.get_args_list()
        
        reply = _dispatch_dbus_method_call(target_methods, self, args, message)

        self._connection.send(reply)

    @method('org.freedesktop.DBus.Introspectable')
    def Introspect(self):
        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'
        reflection_data = reflection_data + '<node name="%s">\n'%(self._object_path)
        reflection_data = reflection_data + self._dbus_reflection_data
        reflection_data = reflection_data + '</node>\n'

        return reflection_data