summaryrefslogtreecommitdiffstats
path: root/python/proxies.py
blob: 80e9ac2784fe4e24f889ffccd2070859410027ae (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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
import dbus_bindings
import introspect_parser
import sys
from exceptions import MissingReplyHandlerException, MissingErrorHandlerException, IntrospectionParserException

class DeferedMethod:
    """A DeferedMethod
    
    This is returned instead of ProxyMethod when we are defering DBus calls
    while waiting for introspection data to be returned
    """
    def __init__(self, proxy_method):
        self._proxy_method = proxy_method
        self._method_name  = proxy_method._method_name
    
    def __call__(self, *args, **keywords):
        reply_handler = None
        if keywords.has_key('reply_handler'):
            reply_handler = keywords['reply_handler']

        #block for now even on async
        # FIXME: put ret in async queue in future if we have a reply handler

        self._proxy_method._proxy._pending_introspect.block()
        ret = self._proxy_method (*args, **keywords)
        
        return ret

class ProxyMethod:
    """A proxy Method.

    Typically a member of a ProxyObject. Calls to the
    method produce messages that travel over the Bus and are routed
    to a specific named Service.
    """
    def __init__(self, proxy, connection, named_service, object_path, method_name, iface):
        self._proxy          = proxy
        self._connection     = connection
        self._named_service  = named_service
        self._object_path    = object_path
        self._method_name    = method_name
        self._dbus_interface = iface

    def __call__(self, *args, **keywords):
        timeout = -1
        if keywords.has_key('timeout'):
            timeout = keywords['timeout']

        reply_handler = None
        if keywords.has_key('reply_handler'):
            reply_handler = keywords['reply_handler']

        error_handler = None
        if keywords.has_key('error_handler'):
            error_handler = keywords['error_handler']            

        ignore_reply = False
        if keywords.has_key('ignore_reply'):
            ignore_reply = keywords['ignore_reply']


        if not(reply_handler and error_handler):
            if reply_handler:
                raise MissingErrorHandlerException()
            elif error_handler:
                raise MissingReplyHandlerException()

        dbus_interface = self._dbus_interface 
        if keywords.has_key('dbus_interface'):
            dbus_interface = keywords['dbus_interface']

        tmp_iface = ''
        if dbus_interface:
	    tmp_iface = dbus_interface + '.'

        key = tmp_iface + self._method_name

        introspect_sig = None
        if self._proxy._introspect_method_map.has_key (key):
            introspect_sig = self._proxy._introspect_method_map[key]

        message = dbus_bindings.MethodCall(self._object_path, dbus_interface, self._method_name)
        message.set_destination(self._named_service)
        
        # Add the arguments to the function
        iter = message.get_iter(True)

        if introspect_sig:
            for (arg, sig) in zip(args, dbus_bindings.Signature(introspect_sig)):
                iter.append_strict(arg, sig)
        else:
            for arg in args:
                iter.append(arg)

        if ignore_reply:
            result = self._connection.send(message)
            args_tuple = (result,)
        elif reply_handler:
            result = self._connection.send_with_reply_handlers(message, timeout, reply_handler, error_handler)
            args_tuple = result
        else:
            reply_message = self._connection.send_with_reply_and_block(message, timeout)
            args_tuple = reply_message.get_args_list()

        if len(args_tuple) == 0:
            return
        elif len(args_tuple) == 1:
            return args_tuple[0]
        else:
            return args_tuple


class ProxyObject:
    """A proxy to the remote Object.

    A ProxyObject is provided by the Bus. ProxyObjects
    have member functions, and can be called like normal Python objects.
    """
    ProxyMethodClass = ProxyMethod
    DeferedMethodClass = DeferedMethod

    INTROSPECT_STATE_DONT_INTROSPECT = 0
    INTROSPECT_STATE_INTROSPECT_IN_PROGRESS = 1
    INTROSPECT_STATE_INTROSPECT_DONE = 2

    def __init__(self, bus, named_service, object_path, introspect=True):
        self._bus           = bus
        self._named_service = named_service
        self._object_path   = object_path

        #PendingCall object for Introspect call
        self._pending_introspect = None
        #queue of async calls waiting on the Introspect to return 
        self._pending_introspect_queue = []
        #dictionary mapping method names to their input signatures
        self._introspect_method_map = {}

        if not introspect:
            self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
        else:
            self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS
            
            (result, self._pending_introspect) = self._Introspect()
            

    def connect_to_signal(self, signal_name, handler_function, dbus_interface=None, **keywords):
        self._bus.add_signal_receiver(handler_function,
                                      signal_name=signal_name,
                                      dbus_interface=dbus_interface,
                                      named_service=self._named_service,
                                      path=self._object_path,
                                      **keywords)

    def _Introspect(self):
        message = dbus_bindings.MethodCall(self._object_path, 'org.freedesktop.DBus.Introspectable', 'Introspect')
        message.set_destination(self._named_service)
        
        result = self._bus.get_connection().send_with_reply_handlers(message, -1, 
                                                                     self._introspect_reply_handler, 
                                                                     self._introspect_error_handler)
        return result   
    
    def _introspect_execute_queue(self): 
        for call in self._pending_introspect_queue:
            (member, iface, args, keywords) = call

            introspect_sig = None

            tmp_iface = ''
            if iface:
                tmp_iface = iface + '.'
                    
            key = tmp_iface + '.' + member
            if self._introspect_method_map.has_key (key):
                introspect_sig = self._introspect_method_map[key]

            
            call_object = self.ProxyMethodClass(self._bus.get_connection(),
                                                self._named_service,
                                                self._object_path, 
                                                iface, 
                                                member,
                                                introspect_sig)
                                                                       
            call_object(args, keywords)

    def _introspect_reply_handler(self, data):
        try:
            self._introspect_method_map = introspect_parser.process_introspection_data(data)
        except IntrospectionParserException, e:
            self._introspect_error_handler(e)
            return
        
        self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_DONE
        #self._introspect_execute_queue()

    def _introspect_error_handler(self, error):
        self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
        self._introspect_execute_queue()
        sys.stderr.write("Introspect error: " + str(error) + "\n")

    def __getattr__(self, member, dbus_interface=None):
        if member == '__call__':
            return object.__call__
        elif member.startswith('__') and member.endswith('__'):
            raise AttributeError(member)
        else:
            ret = self.ProxyMethodClass(self, self._bus.get_connection(),
                                        self._named_service,
                                        self._object_path, member, 
                                        dbus_interface)
        
            if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
                ret = self.DeferedMethodClass(ret)
                    
            return ret

    def __repr__(self):
        return '<ProxyObject wrapping %s %s %s at %#x>'%(
            self._bus, self._named_service, self._object_path , id(self))
    __str__ = __repr__