OLD | NEW |
| (Empty) |
1 # Copyright 2014 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 """The metaclasses used by the mojo python bindings.""" | |
6 | |
7 import itertools | |
8 import logging | |
9 import sys | |
10 | |
11 # pylint: disable=F0401 | |
12 import mojo_bindings.messaging as messaging | |
13 import mojo_bindings.promise as promise | |
14 import mojo_bindings.serialization as serialization | |
15 | |
16 | |
17 class MojoEnumType(type): | |
18 """Meta class for enumerations. | |
19 | |
20 Usage: | |
21 class MyEnum(object): | |
22 __metaclass__ = MojoEnumType | |
23 VALUES = [ | |
24 ('A', 0), | |
25 'B', | |
26 ('C', 5), | |
27 ] | |
28 | |
29 This will define a enum with 3 values, 'A' = 0, 'B' = 1 and 'C' = 5. | |
30 """ | |
31 | |
32 def __new__(mcs, name, bases, dictionary): | |
33 dictionary['__slots__'] = () | |
34 dictionary['__new__'] = None | |
35 for value in dictionary.pop('VALUES', []): | |
36 if not isinstance(value, tuple): | |
37 raise ValueError('incorrect value: %r' % value) | |
38 key, enum_value = value | |
39 if isinstance(key, str) and isinstance(enum_value, int): | |
40 dictionary[key] = enum_value | |
41 else: | |
42 raise ValueError('incorrect value: %r' % value) | |
43 return type.__new__(mcs, name, bases, dictionary) | |
44 | |
45 def __setattr__(cls, key, value): | |
46 raise AttributeError('can\'t set attribute') | |
47 | |
48 def __delattr__(cls, key): | |
49 raise AttributeError('can\'t delete attribute') | |
50 | |
51 | |
52 class MojoStructType(type): | |
53 """Meta class for structs. | |
54 | |
55 Usage: | |
56 class MyStruct(object): | |
57 __metaclass__ = MojoStructType | |
58 DESCRIPTOR = { | |
59 'constants': { | |
60 'C1': 1, | |
61 'C2': 2, | |
62 }, | |
63 'enums': { | |
64 'ENUM1': [ | |
65 ('V1', 1), | |
66 'V2', | |
67 ], | |
68 'ENUM2': [ | |
69 ('V1', 1), | |
70 'V2', | |
71 ], | |
72 }, | |
73 'fields': [ | |
74 SingleFieldGroup('x', _descriptor.TYPE_INT32, 0, 0), | |
75 ], | |
76 } | |
77 | |
78 This will define an struct, with: | |
79 - 2 constants 'C1' and 'C2'; | |
80 - 2 enums 'ENUM1' and 'ENUM2', each of those having 2 values, 'V1' and | |
81 'V2'; | |
82 - 1 int32 field named 'x'. | |
83 """ | |
84 | |
85 def __new__(mcs, name, bases, dictionary): | |
86 dictionary['__slots__'] = ('_fields') | |
87 descriptor = dictionary.pop('DESCRIPTOR', {}) | |
88 | |
89 # Add constants | |
90 dictionary.update(descriptor.get('constants', {})) | |
91 | |
92 # Add enums | |
93 enums = descriptor.get('enums', {}) | |
94 for key in enums: | |
95 dictionary[key] = MojoEnumType(key, (object,), { 'VALUES': enums[key] }) | |
96 | |
97 # Add fields | |
98 groups = descriptor.get('fields', []) | |
99 | |
100 fields = list( | |
101 itertools.chain.from_iterable([group.descriptors for group in groups])) | |
102 fields.sort(key=lambda f: f.index) | |
103 for field in fields: | |
104 dictionary[field.name] = _BuildProperty(field) | |
105 | |
106 # Add init | |
107 dictionary['__init__'] = _StructInit(fields) | |
108 | |
109 # Add serialization method | |
110 serialization_object = serialization.Serialization(groups) | |
111 def Serialize(self, handle_offset=0): | |
112 return serialization_object.Serialize(self, handle_offset) | |
113 dictionary['Serialize'] = Serialize | |
114 | |
115 # pylint: disable=W0212 | |
116 def AsDict(self): | |
117 return self._fields | |
118 dictionary['AsDict'] = AsDict | |
119 | |
120 def Deserialize(cls, context): | |
121 result = cls.__new__(cls) | |
122 fields = {} | |
123 serialization_object.Deserialize(fields, context) | |
124 result._fields = fields | |
125 return result | |
126 dictionary['Deserialize'] = classmethod(Deserialize) | |
127 | |
128 dictionary['__eq__'] = _StructEq(fields) | |
129 dictionary['__ne__'] = _StructNe | |
130 | |
131 return type.__new__(mcs, name, bases, dictionary) | |
132 | |
133 # Prevent adding new attributes, or mutating constants. | |
134 def __setattr__(cls, key, value): | |
135 raise AttributeError('can\'t set attribute') | |
136 | |
137 # Prevent deleting constants. | |
138 def __delattr__(cls, key): | |
139 raise AttributeError('can\'t delete attribute') | |
140 | |
141 | |
142 class MojoInterfaceType(type): | |
143 """Meta class for interfaces. | |
144 | |
145 Usage: | |
146 class MyInterface(object): | |
147 __metaclass__ = MojoInterfaceType | |
148 DESCRIPTOR = { | |
149 'client': MyInterfaceClient, | |
150 'methods': [ | |
151 { | |
152 'name': 'FireAndForget', | |
153 'ordinal': 0, | |
154 'parameters': [ | |
155 SingleFieldGroup('x', _descriptor.TYPE_INT32, 0, 0), | |
156 ] | |
157 }, | |
158 { | |
159 'name': 'Ping', | |
160 'ordinal': 1, | |
161 'parameters': [ | |
162 SingleFieldGroup('x', _descriptor.TYPE_INT32, 0, 0), | |
163 ], | |
164 'responses': [ | |
165 SingleFieldGroup('x', _descriptor.TYPE_INT32, 0, 0), | |
166 ], | |
167 }, | |
168 ], | |
169 } | |
170 """ | |
171 | |
172 def __new__(mcs, name, bases, dictionary): | |
173 # If one of the base class is already an interface type, do not edit the | |
174 # class. | |
175 for base in bases: | |
176 if isinstance(base, mcs): | |
177 return type.__new__(mcs, name, bases, dictionary) | |
178 | |
179 descriptor = dictionary.pop('DESCRIPTOR', {}) | |
180 | |
181 methods = [_MethodDescriptor(x) for x in descriptor.get('methods', [])] | |
182 for method in methods: | |
183 dictionary[method.name] = _NotImplemented | |
184 client_class_getter = descriptor.get('client', None) | |
185 | |
186 interface_manager = InterfaceManager(name, methods, client_class_getter) | |
187 dictionary.update({ | |
188 'client': None, | |
189 'manager': None, | |
190 '_interface_manager': interface_manager, | |
191 }) | |
192 | |
193 interface_class = type.__new__(mcs, name, bases, dictionary) | |
194 interface_manager.interface_class = interface_class | |
195 return interface_class | |
196 | |
197 @property | |
198 def manager(cls): | |
199 return cls._interface_manager | |
200 | |
201 # Prevent adding new attributes, or mutating constants. | |
202 def __setattr__(cls, key, value): | |
203 raise AttributeError('can\'t set attribute') | |
204 | |
205 # Prevent deleting constants. | |
206 def __delattr__(cls, key): | |
207 raise AttributeError('can\'t delete attribute') | |
208 | |
209 | |
210 class InterfaceProxy(object): | |
211 """ | |
212 A proxy allows to access a remote interface through a message pipe. | |
213 """ | |
214 pass | |
215 | |
216 | |
217 class InterfaceRequest(object): | |
218 """ | |
219 An interface request allows to send a request for an interface to a remote | |
220 object and start using it immediately. | |
221 """ | |
222 | |
223 def __init__(self, handle): | |
224 self._handle = handle | |
225 | |
226 def IsPending(self): | |
227 return self._handle.IsValid() | |
228 | |
229 def PassMessagePipe(self): | |
230 result = self._handle | |
231 self._handle = None | |
232 return result | |
233 | |
234 | |
235 class InterfaceManager(object): | |
236 """ | |
237 Manager for an interface class. The manager contains the operation that allows | |
238 to bind an implementation to a pipe, or to generate a proxy for an interface | |
239 over a pipe. | |
240 """ | |
241 | |
242 def __init__(self, name, methods, client_class_getter): | |
243 self.name = name | |
244 self.methods = methods | |
245 self.interface_class = None | |
246 self._client_class_getter = client_class_getter | |
247 self._client_manager = None | |
248 self._client_manager_computed = False | |
249 self._proxy_class = None | |
250 self._stub_class = None | |
251 | |
252 @property | |
253 def client_manager(self): | |
254 if not self._client_manager_computed: | |
255 self._client_manager_computed = True | |
256 if self._client_class_getter: | |
257 self._client_manager = self._client_class_getter().manager | |
258 return self._client_manager | |
259 | |
260 def Proxy(self, handle): | |
261 router = messaging.Router(handle) | |
262 error_handler = _ProxyErrorHandler() | |
263 router.SetErrorHandler(error_handler) | |
264 router.Start() | |
265 return self._InternalProxy(router, error_handler) | |
266 | |
267 # pylint: disable=W0212 | |
268 def Bind(self, impl, handle): | |
269 router = messaging.Router(handle) | |
270 router.SetIncomingMessageReceiver(self._Stub(impl)) | |
271 error_handler = _ProxyErrorHandler() | |
272 router.SetErrorHandler(error_handler) | |
273 | |
274 # Retain the router, until an error happen. | |
275 retainer = _Retainer(router) | |
276 def Cleanup(_): | |
277 retainer.release() | |
278 error_handler.AddCallback(Cleanup) | |
279 | |
280 if self.client_manager: | |
281 impl.client = self.client_manager._InternalProxy(router, error_handler) | |
282 | |
283 # Give an instance manager to the implementation to allow it to close | |
284 # the connection. | |
285 impl.manager = InstanceManager(router) | |
286 | |
287 router.Start() | |
288 | |
289 def _InternalProxy(self, router, error_handler): | |
290 if not self._proxy_class: | |
291 dictionary = { | |
292 '__module__': __name__, | |
293 '__init__': _ProxyInit, | |
294 } | |
295 if self.client_manager: | |
296 dictionary['client'] = property(_ProxyGetClient, _ProxySetClient) | |
297 dictionary['manager'] = None | |
298 dictionary['_client_manager'] = self.client_manager | |
299 for method in self.methods: | |
300 dictionary[method.name] = _ProxyMethodCall(method) | |
301 self._proxy_class = type('%sProxy' % self.name, | |
302 (self.interface_class, InterfaceProxy), | |
303 dictionary) | |
304 | |
305 proxy = self._proxy_class(router, error_handler) | |
306 # Give an instance manager to the proxy to allow to close the connection. | |
307 proxy.manager = InstanceManager(router) | |
308 return proxy | |
309 | |
310 def _Stub(self, impl): | |
311 if not self._stub_class: | |
312 accept_method = _StubAccept(self.methods) | |
313 dictionary = { | |
314 '__module__': __name__, | |
315 '__init__': _StubInit, | |
316 'Accept': accept_method, | |
317 'AcceptWithResponder': accept_method, | |
318 } | |
319 self._stub_class = type('%sStub' % self.name, | |
320 (messaging.MessageReceiverWithResponder,), | |
321 dictionary) | |
322 return self._stub_class(impl) | |
323 | |
324 | |
325 class InstanceManager(object): | |
326 """ | |
327 Manager for the implementation of an interface or a proxy. The manager allows | |
328 to control the connection over the pipe. | |
329 """ | |
330 def __init__(self, router): | |
331 self.router = router | |
332 | |
333 def Close(self): | |
334 self.router.Close() | |
335 | |
336 def PassMessagePipe(self): | |
337 return self.router.PassMessagePipe() | |
338 | |
339 | |
340 class _MethodDescriptor(object): | |
341 def __init__(self, descriptor): | |
342 self.name = descriptor['name'] | |
343 self.ordinal = descriptor['ordinal'] | |
344 self.parameters_struct = _ConstructParameterStruct( | |
345 descriptor['parameters'], self.name, "Parameters") | |
346 self.response_struct = _ConstructParameterStruct( | |
347 descriptor.get('responses'), self.name, "Responses") | |
348 | |
349 | |
350 def _ConstructParameterStruct(descriptor, name, suffix): | |
351 if descriptor is None: | |
352 return None | |
353 parameter_dictionary = { | |
354 '__metaclass__': MojoStructType, | |
355 '__module__': __name__, | |
356 'DESCRIPTOR': descriptor, | |
357 } | |
358 return MojoStructType( | |
359 '%s%s' % (name, suffix), | |
360 (object,), | |
361 parameter_dictionary) | |
362 | |
363 | |
364 class _ProxyErrorHandler(messaging.ConnectionErrorHandler): | |
365 def __init__(self): | |
366 messaging.ConnectionErrorHandler.__init__(self) | |
367 self._callbacks = set() | |
368 | |
369 def OnError(self, result): | |
370 exception = messaging.MessagingException('Mojo error: %d' % result) | |
371 for callback in list(self._callbacks): | |
372 callback(exception) | |
373 self._callbacks = None | |
374 | |
375 def AddCallback(self, callback): | |
376 if self._callbacks is not None: | |
377 self._callbacks.add(callback) | |
378 | |
379 def RemoveCallback(self, callback): | |
380 if self._callbacks: | |
381 self._callbacks.remove(callback) | |
382 | |
383 | |
384 class _Retainer(object): | |
385 | |
386 # Set to force instances to be retained. | |
387 _RETAINED = set() | |
388 | |
389 def __init__(self, retained): | |
390 self._retained = retained | |
391 _Retainer._RETAINED.add(self) | |
392 | |
393 def release(self): | |
394 self._retained = None | |
395 _Retainer._RETAINED.remove(self) | |
396 | |
397 | |
398 def _StructInit(fields): | |
399 def _Init(self, *args, **kwargs): | |
400 if len(args) + len(kwargs) > len(fields): | |
401 raise TypeError('__init__() takes %d argument (%d given)' % | |
402 (len(fields), len(args) + len(kwargs))) | |
403 self._fields = {} | |
404 for f, a in zip(fields, args): | |
405 self.__setattr__(f.name, a) | |
406 remaining_fields = set(x.name for x in fields[len(args):]) | |
407 for name in kwargs: | |
408 if not name in remaining_fields: | |
409 if name in (x.name for x in fields[:len(args)]): | |
410 raise TypeError( | |
411 '__init__() got multiple values for keyword argument %r' % name) | |
412 raise TypeError('__init__() got an unexpected keyword argument %r' % | |
413 name) | |
414 self.__setattr__(name, kwargs[name]) | |
415 return _Init | |
416 | |
417 | |
418 def _BuildProperty(field): | |
419 """Build the property for the given field.""" | |
420 | |
421 # pylint: disable=W0212 | |
422 def Get(self): | |
423 if field.name not in self._fields: | |
424 self._fields[field.name] = field.GetDefaultValue() | |
425 return self._fields[field.name] | |
426 | |
427 # pylint: disable=W0212 | |
428 def Set(self, value): | |
429 self._fields[field.name] = field.field_type.Convert(value) | |
430 | |
431 return property(Get, Set) | |
432 | |
433 | |
434 def _StructEq(fields): | |
435 def _Eq(self, other): | |
436 if type(self) is not type(other): | |
437 return False | |
438 for field in fields: | |
439 if getattr(self, field.name) != getattr(other, field.name): | |
440 return False | |
441 return True | |
442 return _Eq | |
443 | |
444 def _StructNe(self, other): | |
445 return not self.__eq__(other) | |
446 | |
447 | |
448 def _ProxyInit(self, router, error_handler): | |
449 self._router = router | |
450 self._error_handler = error_handler | |
451 self._client = None | |
452 | |
453 | |
454 # pylint: disable=W0212 | |
455 def _ProxyGetClient(self): | |
456 return self._client | |
457 | |
458 | |
459 # pylint: disable=W0212 | |
460 def _ProxySetClient(self, client): | |
461 self._client = client | |
462 stub = self._client_manager._Stub(client) | |
463 self._router.SetIncomingMessageReceiver(stub) | |
464 | |
465 | |
466 # pylint: disable=W0212 | |
467 def _ProxyMethodCall(method): | |
468 flags = messaging.NO_FLAG | |
469 if method.response_struct: | |
470 flags = messaging.MESSAGE_EXPECTS_RESPONSE_FLAG | |
471 def _Call(self, *args, **kwargs): | |
472 def GenerationMethod(resolve, reject): | |
473 message = _GetMessage(method, flags, *args, **kwargs) | |
474 if method.response_struct: | |
475 def Accept(message): | |
476 try: | |
477 assert message.header.message_type == method.ordinal | |
478 payload = message.payload | |
479 response = method.response_struct.Deserialize( | |
480 serialization.RootDeserializationContext(payload.data, | |
481 payload.handles)) | |
482 as_dict = response.AsDict() | |
483 if len(as_dict) == 1: | |
484 value = as_dict.values()[0] | |
485 if not isinstance(value, dict): | |
486 response = value | |
487 resolve(response) | |
488 return True | |
489 except Exception as e: | |
490 # Adding traceback similarly to python 3.0 (pep-3134) | |
491 e.__traceback__ = sys.exc_info()[2] | |
492 reject(e) | |
493 return False | |
494 finally: | |
495 self._error_handler.RemoveCallback(reject) | |
496 | |
497 self._error_handler.AddCallback(reject) | |
498 if not self._router.AcceptWithResponder( | |
499 message, messaging.ForwardingMessageReceiver(Accept)): | |
500 self._error_handler.RemoveCallback(reject) | |
501 reject(messaging.MessagingException("Unable to send message.")) | |
502 else: | |
503 if (self._router.Accept(message)): | |
504 resolve(None) | |
505 else: | |
506 reject(messaging.MessagingException("Unable to send message.")) | |
507 return promise.Promise(GenerationMethod) | |
508 return _Call | |
509 | |
510 | |
511 def _GetMessage(method, flags, *args, **kwargs): | |
512 if flags == messaging.MESSAGE_IS_RESPONSE_FLAG: | |
513 struct = method.response_struct(*args, **kwargs) | |
514 else: | |
515 struct = method.parameters_struct(*args, **kwargs) | |
516 header = messaging.MessageHeader(method.ordinal, flags) | |
517 data = header.Serialize() | |
518 (payload, handles) = struct.Serialize() | |
519 data.extend(payload) | |
520 return messaging.Message(data, handles, header) | |
521 | |
522 | |
523 def _StubInit(self, impl): | |
524 self.impl = impl | |
525 | |
526 | |
527 def _StubAccept(methods): | |
528 methods_by_ordinal = dict((m.ordinal, m) for m in methods) | |
529 def Accept(self, message, responder=None): | |
530 try: | |
531 header = message.header | |
532 assert header.expects_response == bool(responder) | |
533 assert header.message_type in methods_by_ordinal | |
534 method = methods_by_ordinal[header.message_type] | |
535 payload = message.payload | |
536 parameters = method.parameters_struct.Deserialize( | |
537 serialization.RootDeserializationContext( | |
538 payload.data, payload.handles)).AsDict() | |
539 response = getattr(self.impl, method.name)(**parameters) | |
540 if header.expects_response: | |
541 def SendResponse(response): | |
542 if isinstance(response, dict): | |
543 response_message = _GetMessage(method, | |
544 messaging.MESSAGE_IS_RESPONSE_FLAG, | |
545 **response) | |
546 else: | |
547 response_message = _GetMessage(method, | |
548 messaging.MESSAGE_IS_RESPONSE_FLAG, | |
549 response) | |
550 response_message.header.request_id = header.request_id | |
551 responder.Accept(response_message) | |
552 p = promise.Promise.Resolve(response).Then(SendResponse) | |
553 if self.impl.manager: | |
554 # Close the connection in case of error. | |
555 p.Catch(lambda _: self.impl.manager.Close()) | |
556 return True | |
557 # pylint: disable=W0702 | |
558 except: | |
559 # Close the connection in case of error. | |
560 logging.warning( | |
561 'Error occured in accept method. Connection will be closed.') | |
562 if self.impl.manager: | |
563 self.impl.manager.Close() | |
564 return False | |
565 return Accept | |
566 | |
567 | |
568 def _NotImplemented(*_1, **_2): | |
569 raise NotImplementedError() | |
OLD | NEW |