Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(493)

Side by Side Diff: mojo/public/python/mojo_bindings/interface_reflection.py

Issue 2250183003: Make the fuchsia mojo/public repo the source of truth. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """
6 The metaclasses used by the mojo python bindings for interfaces.
7
8 It is splitted from mojo_bindings.reflection because it uses some generated code
9 that would create a cyclic dependency.
10 """
11
12 import logging
13 import sys
14
15 # pylint: disable=F0401
16 import interface_control_messages_mojom
17 import mojo_bindings.messaging as messaging
18 import mojo_bindings.promise as promise
19 import mojo_bindings.reflection as reflection
20 import mojo_bindings.serialization as serialization
21 import mojo_system
22
23
24 class MojoInterfaceType(type):
25 """Meta class for interfaces.
26
27 Usage:
28 class MyInterface(object):
29 __metaclass__ = MojoInterfaceType
30 DESCRIPTOR = {
31 'fully_qualified_name': 'service::MyInterface'
32 'version': 3,
33 'methods': [
34 {
35 'name': 'FireAndForget',
36 'ordinal': 0,
37 'parameters': [
38 SingleFieldGroup('x', _descriptor.TYPE_INT32, 0, 0),
39 ]
40 },
41 {
42 'name': 'Ping',
43 'ordinal': 1,
44 'parameters': [
45 SingleFieldGroup('x', _descriptor.TYPE_INT32, 0, 0),
46 ],
47 'responses': [
48 SingleFieldGroup('x', _descriptor.TYPE_INT32, 0, 0),
49 ],
50 },
51 ],
52 }
53 """
54
55 def __new__(mcs, name, bases, dictionary):
56 # If one of the base class is already an interface type, do not edit the
57 # class.
58 for base in bases:
59 if isinstance(base, mcs):
60 return type.__new__(mcs, name, bases, dictionary)
61
62 descriptor = dictionary.pop('DESCRIPTOR', {})
63
64 methods = [_MethodDescriptor(x) for x in descriptor.get('methods', [])]
65 for method in methods:
66 dictionary[method.name] = _NotImplemented
67 fully_qualified_name = descriptor['fully_qualified_name']
68
69 interface_manager = InterfaceManager(
70 name, descriptor['version'], methods, fully_qualified_name)
71 dictionary.update({
72 'manager': None,
73 '_interface_manager': interface_manager,
74 })
75
76 interface_class = type.__new__(mcs, name, bases, dictionary)
77 interface_manager.interface_class = interface_class
78 return interface_class
79
80 @property
81 def manager(cls):
82 return cls._interface_manager
83
84 # Prevent adding new attributes, or mutating constants.
85 def __setattr__(cls, key, value):
86 raise AttributeError('can\'t set attribute')
87
88 # Prevent deleting constants.
89 def __delattr__(cls, key):
90 raise AttributeError('can\'t delete attribute')
91
92
93 class InterfaceManager(object):
94 """
95 Manager for an interface class. The manager contains the operation that allows
96 to bind an implementation to a pipe, or to generate a proxy for an interface
97 over a pipe.
98 """
99
100 def __init__(self, name, version, methods, service_name):
101 self.name = name
102 self.version = version
103 self.methods = methods
104 self.service_name = service_name
105 self.interface_class = None
106 self._proxy_class = None
107 self._stub_class = None
108
109 def Proxy(self, handle, version=0):
110 router = messaging.Router(handle)
111 error_handler = _ProxyErrorHandler()
112 router.SetErrorHandler(error_handler)
113 router.Start()
114 return self._InternalProxy(router, error_handler, version)
115
116 # pylint: disable=W0212
117 def Bind(self, impl, handle):
118 router = messaging.Router(handle)
119 router.SetIncomingMessageReceiver(self._Stub(impl))
120 error_handler = _ProxyErrorHandler()
121 router.SetErrorHandler(error_handler)
122
123 # Retain the router, until an error happen.
124 retainer = _Retainer(router)
125 def Cleanup(_):
126 retainer.release()
127 error_handler.AddCallback(Cleanup)
128
129 # Give an instance manager to the implementation to allow it to close
130 # the connection.
131 impl.manager = InstanceManager(self, router, error_handler)
132
133 router.Start()
134
135 def NewRequest(self):
136 pipe = mojo_system.MessagePipe()
137 return (self.Proxy(pipe.handle0), reflection.InterfaceRequest(pipe.handle1))
138
139 def _InternalProxy(self, router, error_handler, version):
140 if error_handler is None:
141 error_handler = _ProxyErrorHandler()
142
143 if not self._proxy_class:
144 dictionary = {
145 '__module__': __name__,
146 '__init__': _ProxyInit,
147 }
148 for method in self.methods:
149 dictionary[method.name] = _ProxyMethodCall(method)
150 self._proxy_class = type(
151 '%sProxy' % self.name,
152 (self.interface_class, reflection.InterfaceProxy),
153 dictionary)
154
155 proxy = self._proxy_class(router, error_handler)
156 # Give an instance manager to the proxy to allow to close the connection.
157 proxy.manager = ProxyInstanceManager(
158 self, proxy, router, error_handler, version)
159 return proxy
160
161 def _Stub(self, impl):
162 if not self._stub_class:
163 accept_method = _StubAccept(self.methods)
164 dictionary = {
165 '__module__': __name__,
166 '__init__': _StubInit,
167 'Accept': accept_method,
168 'AcceptWithResponder': accept_method,
169 }
170 self._stub_class = type('%sStub' % self.name,
171 (messaging.MessageReceiverWithResponder,),
172 dictionary)
173 return self._stub_class(impl)
174
175
176 class InstanceManager(object):
177 """
178 Manager for the implementation of an interface or a proxy. The manager allows
179 to control the connection over the pipe.
180 """
181 def __init__(self, interface_manager, router, error_handler):
182 self.interface_manager = interface_manager
183 self._router = router
184 self._error_handler = error_handler
185 assert self._error_handler is not None
186
187 def Close(self):
188 self._error_handler.OnClose()
189 self._router.Close()
190
191 def PassMessagePipe(self):
192 self._error_handler.OnClose()
193 return self._router.PassMessagePipe()
194
195 def AddOnErrorCallback(self, callback):
196 self._error_handler.AddCallback(lambda _: callback(), False)
197
198
199 class ProxyInstanceManager(InstanceManager):
200 """
201 Manager for the implementation of a proxy. The manager allows to control the
202 connection over the pipe.
203 """
204 def __init__(self, interface_manager, proxy, router, error_handler, version):
205 super(ProxyInstanceManager, self).__init__(
206 interface_manager, router, error_handler)
207 self.proxy = proxy
208 self.version = version
209 self._run_method = _ProxyMethodCall(_BaseMethodDescriptor(
210 'Run',
211 interface_control_messages_mojom.RUN_MESSAGE_ID,
212 interface_control_messages_mojom.RunMessageParams,
213 interface_control_messages_mojom.RunResponseMessageParams))
214 self._run_or_close_pipe_method = _ProxyMethodCall(_BaseMethodDescriptor(
215 'RunOrClosePipe',
216 interface_control_messages_mojom.RUN_OR_CLOSE_PIPE_MESSAGE_ID,
217 interface_control_messages_mojom.RunOrClosePipeMessageParams,
218 None))
219
220 def QueryVersion(self):
221 params = interface_control_messages_mojom.RunMessageParams()
222 params.reserved0 = 16
223 params.reserved1 = 0
224 params.query_version = (
225 interface_control_messages_mojom.QueryVersion())
226 def ToVersion(r):
227 self.version = r.query_version_result.version
228 return self.version
229 return self._run_method(self.proxy, **params.AsDict()).Then(ToVersion)
230
231 def RequireVersion(self, version):
232 if self.version >= version:
233 return
234 self.version = version
235 params = interface_control_messages_mojom.RunOrClosePipeMessageParams()
236 params.reserved0 = 16
237 params.reserved1 = 0
238 params.require_version = interface_control_messages_mojom.RequireVersion()
239 params.require_version.version = version
240 return self._run_or_close_pipe_method(self.proxy, **params.AsDict())
241
242
243 class _BaseMethodDescriptor(object):
244 def __init__(self, name, ordinal, parameters_struct, response_struct):
245 self.name = name
246 self.ordinal = ordinal
247 self.parameters_struct = parameters_struct
248 self.response_struct = response_struct
249
250
251 class _MethodDescriptor(_BaseMethodDescriptor):
252 def __init__(self, descriptor):
253 name = descriptor['name']
254 super(_MethodDescriptor, self).__init__(
255 name,
256 descriptor['ordinal'],
257 _ConstructParameterStruct(
258 descriptor['parameters'], name, "Parameters"),
259 _ConstructParameterStruct(
260 descriptor.get('responses'), name, "Responses"))
261
262
263 def _ConstructParameterStruct(descriptor, name, suffix):
264 if descriptor is None:
265 return None
266 parameter_dictionary = {
267 '__metaclass__': reflection.MojoStructType,
268 '__module__': __name__,
269 'DESCRIPTOR': descriptor,
270 }
271 return reflection.MojoStructType(
272 '%s%s' % (name, suffix),
273 (object,),
274 parameter_dictionary)
275
276
277 class _ProxyErrorHandler(messaging.ConnectionErrorHandler):
278 def __init__(self):
279 messaging.ConnectionErrorHandler.__init__(self)
280 self._callbacks = dict()
281
282 def OnError(self, result):
283 if self._callbacks is None:
284 return
285 exception = messaging.MessagingException('Mojo error: %d' % result)
286 for (callback, _) in self._callbacks.iteritems():
287 callback(exception)
288 self._callbacks = None
289
290 def OnClose(self):
291 if self._callbacks is None:
292 return
293 exception = messaging.MessagingException('Router has been closed.')
294 for (callback, call_on_close) in self._callbacks.iteritems():
295 if call_on_close:
296 callback(exception)
297 self._callbacks = None
298
299 def AddCallback(self, callback, call_on_close=True):
300 if self._callbacks is not None:
301 self._callbacks[callback] = call_on_close
302
303 def RemoveCallback(self, callback):
304 if self._callbacks:
305 del self._callbacks[callback]
306
307
308 class _Retainer(object):
309
310 # Set to force instances to be retained.
311 _RETAINED = set()
312
313 def __init__(self, retained):
314 self._retained = retained
315 _Retainer._RETAINED.add(self)
316
317 def release(self):
318 self._retained = None
319 _Retainer._RETAINED.remove(self)
320
321
322 def _ProxyInit(self, router, error_handler):
323 self._router = router
324 self._error_handler = error_handler
325
326
327 # pylint: disable=W0212
328 def _ProxyMethodCall(method):
329 flags = messaging.NO_FLAG
330 if method.response_struct:
331 flags = messaging.MESSAGE_EXPECTS_RESPONSE_FLAG
332 def _Call(self, *args, **kwargs):
333 def GenerationMethod(resolve, reject):
334 message = _GetMessage(method, flags, None, *args, **kwargs)
335 if method.response_struct:
336 def Accept(message):
337 try:
338 assert message.header.message_type == method.ordinal
339 payload = message.payload
340 response = method.response_struct.Deserialize(
341 serialization.RootDeserializationContext(payload.data,
342 payload.handles))
343 as_dict = response.AsDict()
344 if len(as_dict) == 1:
345 value = as_dict.values()[0]
346 if not isinstance(value, dict):
347 response = value
348 resolve(response)
349 return True
350 except Exception as e:
351 # Adding traceback similarly to python 3.0 (pep-3134)
352 e.__traceback__ = sys.exc_info()[2]
353 reject(e)
354 return False
355 finally:
356 self._error_handler.RemoveCallback(reject)
357
358 self._error_handler.AddCallback(reject)
359 if not self._router.AcceptWithResponder(
360 message, messaging.ForwardingMessageReceiver(Accept)):
361 self._error_handler.RemoveCallback(reject)
362 reject(messaging.MessagingException("Unable to send message."))
363 else:
364 if (self._router.Accept(message)):
365 resolve(None)
366 else:
367 reject(messaging.MessagingException("Unable to send message."))
368 return promise.Promise(GenerationMethod)
369 return _Call
370
371
372 def _GetMessageWithStruct(struct, ordinal, flags, request_id):
373 header = messaging.MessageHeader(
374 ordinal, flags, 0 if request_id is None else request_id)
375 data = header.Serialize()
376 (payload, handles) = struct.Serialize()
377 data.extend(payload)
378 return messaging.Message(data, handles, header)
379
380
381 def _GetMessage(method, flags, request_id, *args, **kwargs):
382 if flags == messaging.MESSAGE_IS_RESPONSE_FLAG:
383 struct = method.response_struct(*args, **kwargs)
384 else:
385 struct = method.parameters_struct(*args, **kwargs)
386 return _GetMessageWithStruct(struct, method.ordinal, flags, request_id)
387
388
389 def _StubInit(self, impl):
390 self.impl = impl
391
392
393 def _StubAccept(methods):
394 methods_by_ordinal = dict((m.ordinal, m) for m in methods)
395 def Accept(self, message, responder=None):
396 try:
397 header = message.header
398 assert header.expects_response == bool(responder)
399 if header.message_type == interface_control_messages_mojom.RUN_MESSAGE_ID:
400 return _RunMessage(self.impl.manager, message, responder)
401 if (header.message_type ==
402 interface_control_messages_mojom.RUN_OR_CLOSE_PIPE_MESSAGE_ID):
403 return _RunMessageOrClosePipe(self.impl.manager, message)
404 assert header.message_type in methods_by_ordinal
405 method = methods_by_ordinal[header.message_type]
406 payload = message.payload
407 parameters = method.parameters_struct.Deserialize(
408 serialization.RootDeserializationContext(
409 payload.data, payload.handles)).AsDict()
410 response = getattr(self.impl, method.name)(**parameters)
411 if header.expects_response:
412 @promise.async
413 def SendResponse(response):
414 if isinstance(response, dict):
415 response_message = _GetMessage(method,
416 messaging.MESSAGE_IS_RESPONSE_FLAG,
417 header.request_id,
418 **response)
419 else:
420 response_message = _GetMessage(method,
421 messaging.MESSAGE_IS_RESPONSE_FLAG,
422 header.request_id,
423 response)
424 return responder.Accept(response_message)
425 p = SendResponse(response)
426 if self.impl.manager:
427 # Close the connection in case of error.
428 p.Catch(lambda _: self.impl.manager.Close())
429 return True
430 # pylint: disable=W0702
431 except:
432 # Close the connection in case of error.
433 logging.warning(
434 'Error occured in accept method. Connection will be closed.')
435 logging.debug("Exception", exc_info=True)
436 if self.impl.manager:
437 self.impl.manager.Close()
438 return False
439 return Accept
440
441
442 def _RunMessage(manager, message, responder):
443 response = interface_control_messages_mojom.RunResponseMessageParams()
444 response.reserved0 = 16
445 response.reserved1 = 0
446 response.query_version_result = (
447 interface_control_messages_mojom.QueryVersionResult())
448 response.query_version_result.version = manager.interface_manager.version
449 response_message = _GetMessageWithStruct(
450 response,
451 interface_control_messages_mojom.RUN_MESSAGE_ID,
452 messaging.MESSAGE_IS_RESPONSE_FLAG,
453 message.header.request_id)
454 return responder.Accept(response_message)
455
456
457 def _RunMessageOrClosePipe(manager, message):
458 payload = message.payload
459 query = (
460 interface_control_messages_mojom.RunOrClosePipeMessageParams.Deserialize(
461 serialization.RootDeserializationContext(payload.data,
462 payload.handles)))
463 return query.require_version.version <= manager.interface_manager.version
464
465
466 def _NotImplemented(*_1, **_2):
467 raise NotImplementedError()
OLDNEW
« no previous file with comments | « mojo/public/python/mojo_bindings/descriptor.py ('k') | mojo/public/python/mojo_bindings/messaging.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698