_dbus_bindings: Expose name-validation functions to Python code.
[dbus-python-phuang.git] / dbus / proxies.py
blob9c0648522467dde81a38dfd8ce2ba37802242884
1 import sys
2 import logging
4 import _dbus_bindings
5 import dbus.introspect_parser as introspect_parser
6 from dbus.exceptions import MissingReplyHandlerException, MissingErrorHandlerException, IntrospectionParserException, DBusException
8 __docformat__ = 'restructuredtext'
11 _logger = logging.getLogger('dbus.proxies')
14 class _ReplyHandler(object):
15 __slots__ = ('_on_error', '_on_reply')
16 def __init__(self, on_reply, on_error):
17 self._on_error = on_error
18 self._on_reply = on_reply
20 def __call__(self, message):
21 if isinstance(message, _dbus_bindings.MethodReturnMessage):
22 self._on_reply(*message.get_args_list())
23 elif isinstance(message, _dbus_bindings.ErrorMessage):
24 args = message.get_args_list()
25 if len(args) > 0:
26 self._on_error(DBusException(args[0]))
27 else:
28 self._on_error(DBusException())
29 else:
30 self._on_error(DBusException('Unexpected reply message type: %s'
31 % message))
34 class DeferedMethod:
35 """A DeferedMethod
37 This is returned instead of ProxyMethod when we are defering DBus calls
38 while waiting for introspection data to be returned
39 """
40 def __init__(self, proxy_method):
41 self._proxy_method = proxy_method
42 self._method_name = proxy_method._method_name
44 def __call__(self, *args, **keywords):
45 reply_handler = None
46 if keywords.has_key('reply_handler'):
47 reply_handler = keywords['reply_handler']
49 #block for now even on async
50 # FIXME: put ret in async queue in future if we have a reply handler
52 self._proxy_method._proxy._pending_introspect._block()
53 ret = self._proxy_method (*args, **keywords)
55 return ret
57 class ProxyMethod:
58 """A proxy Method.
60 Typically a member of a ProxyObject. Calls to the
61 method produce messages that travel over the Bus and are routed
62 to a specific named Service.
63 """
64 def __init__(self, proxy, connection, named_service, object_path, method_name, iface):
65 self._proxy = proxy
66 self._connection = connection
67 self._named_service = named_service
68 self._object_path = object_path
69 self._method_name = method_name
70 self._dbus_interface = iface
72 def __call__(self, *args, **keywords):
73 timeout = -1
74 if keywords.has_key('timeout'):
75 timeout = keywords['timeout']
77 reply_handler = None
78 if keywords.has_key('reply_handler'):
79 reply_handler = keywords['reply_handler']
81 error_handler = None
82 if keywords.has_key('error_handler'):
83 error_handler = keywords['error_handler']
85 ignore_reply = False
86 if keywords.has_key('ignore_reply'):
87 ignore_reply = keywords['ignore_reply']
90 if not(reply_handler and error_handler):
91 if reply_handler:
92 raise MissingErrorHandlerException()
93 elif error_handler:
94 raise MissingReplyHandlerException()
96 dbus_interface = self._dbus_interface
97 if keywords.has_key('dbus_interface'):
98 dbus_interface = keywords['dbus_interface']
100 tmp_iface = ''
101 if dbus_interface:
102 tmp_iface = dbus_interface + '.'
104 key = tmp_iface + self._method_name
106 introspect_sig = None
107 if self._proxy._introspect_method_map.has_key (key):
108 introspect_sig = self._proxy._introspect_method_map[key]
110 message = _dbus_bindings.MethodCallMessage(destination=None,
111 path=self._object_path,
112 interface=dbus_interface,
113 method=self._method_name)
114 message.set_destination(self._named_service)
116 # Add the arguments to the function
117 try:
118 message.append(signature=introspect_sig, *args)
119 except Exception, e:
120 _logger.error('Unable to set arguments %r according to '
121 'introspected signature %r: %s: %s',
122 args, introspect_sig, e.__class__, e)
123 raise
125 # FIXME: using private API on Connection while I decide whether to
126 # make it public or what
127 if ignore_reply:
128 result = self._connection._send(message)
129 return None
130 elif reply_handler:
131 result = self._connection._send_with_reply(message, _ReplyHandler(reply_handler, error_handler), timeout/1000.0)
132 return None
133 else:
134 reply_message = self._connection._send_with_reply_and_block(message, timeout)
135 args_list = reply_message.get_args_list()
136 if len(args_list) == 0:
137 return None
138 elif len(args_list) == 1:
139 return args_list[0]
140 else:
141 return tuple(args_list)
144 class ProxyObject:
145 """A proxy to the remote Object.
147 A ProxyObject is provided by the Bus. ProxyObjects
148 have member functions, and can be called like normal Python objects.
150 ProxyMethodClass = ProxyMethod
151 DeferedMethodClass = DeferedMethod
153 INTROSPECT_STATE_DONT_INTROSPECT = 0
154 INTROSPECT_STATE_INTROSPECT_IN_PROGRESS = 1
155 INTROSPECT_STATE_INTROSPECT_DONE = 2
157 def __init__(self, bus, named_service, object_path, introspect=True):
158 """Initialize the proxy object.
160 :Parameters:
161 `bus` : `dbus.Bus`
162 The bus on which to find this object
163 `named_service` : str
164 A bus name for the endpoint owning the object (need not
165 actually be a service name)
166 `object_path` : str
167 The object path at which the endpoint exports the object
168 `introspect` : bool
169 If true (default), attempt to introspect the remote
170 object to find out supported methods and their signatures
172 self._bus = bus
173 self._named_service = named_service
174 self._object_path = object_path
176 #PendingCall object for Introspect call
177 self._pending_introspect = None
178 #queue of async calls waiting on the Introspect to return
179 self._pending_introspect_queue = []
180 #dictionary mapping method names to their input signatures
181 self._introspect_method_map = {}
183 if not introspect:
184 self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
185 else:
186 self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS
188 self._pending_introspect = self._Introspect()
191 def connect_to_signal(self, signal_name, handler_function, dbus_interface=None, **keywords):
192 """Arrange for the given function to be called when the given signal
193 is received.
195 :Parameters:
196 `signal_name` : str
197 The name of the signal
198 `handler_function` : callable
199 A function to be called (FIXME arguments?) when the signal
200 is emitted by the remote object.
201 `dbus_interface` : str
202 Optional interface with which to qualify the signal name.
203 The default is to use the interface this Interface represents.
204 `sender_keyword` : str
205 If not None (the default), the handler function will receive
206 the unique name of the sending endpoint as a keyword
207 argument with this name
208 `path_keyword` : str
209 If not None (the default), the handler function will receive
210 the object-path of the sending object as a keyword argument
211 with this name
212 `keywords`
213 If there are additional keyword parameters of the form
214 ``arg``\ *n*, match only signals where the *n*\ th argument
215 is the value given for that keyword parameter
217 self._bus.add_signal_receiver(handler_function,
218 signal_name=signal_name,
219 dbus_interface=dbus_interface,
220 named_service=self._named_service,
221 path=self._object_path,
222 **keywords)
224 def _Introspect(self):
225 message = _dbus_bindings.MethodCallMessage(None, self._object_path, 'org.freedesktop.DBus.Introspectable', 'Introspect')
226 message.set_destination(self._named_service)
228 result = self._bus.get_connection()._send_with_reply(message, _ReplyHandler(self._introspect_reply_handler, self._introspect_error_handler), -1)
229 return result
231 def _introspect_execute_queue(self):
232 for call in self._pending_introspect_queue:
233 (member, iface, args, keywords) = call
235 introspect_sig = None
237 tmp_iface = ''
238 if iface:
239 tmp_iface = iface + '.'
241 key = tmp_iface + '.' + member
242 if self._introspect_method_map.has_key (key):
243 introspect_sig = self._introspect_method_map[key]
246 call_object = self.ProxyMethodClass(self._bus.get_connection(),
247 self._named_service,
248 self._object_path,
249 iface,
250 member,
251 introspect_sig)
253 call_object(args, keywords)
255 def _introspect_reply_handler(self, data):
256 try:
257 self._introspect_method_map = introspect_parser.process_introspection_data(data)
258 except IntrospectionParserException, e:
259 self._introspect_error_handler(e)
260 return
262 self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_DONE
263 #self._introspect_execute_queue()
265 def _introspect_error_handler(self, error):
266 self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
267 self._introspect_execute_queue()
268 sys.stderr.write("Introspect error: " + str(error) + "\n")
270 def __getattr__(self, member, dbus_interface=None):
271 if member == '__call__':
272 return object.__call__
273 elif member.startswith('__') and member.endswith('__'):
274 raise AttributeError(member)
275 else:
276 ret = self.ProxyMethodClass(self, self._bus.get_connection(),
277 self._named_service,
278 self._object_path, member,
279 dbus_interface)
281 if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
282 ret = self.DeferedMethodClass(ret)
284 return ret
286 def __repr__(self):
287 return '<ProxyObject wrapping %s %s %s at %#x>'%(
288 self._bus, self._named_service, self._object_path , id(self))
289 __str__ = __repr__