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

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

Issue 814543006: Move //mojo/{public, edk} underneath //third_party (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 11 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 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()
OLDNEW
« no previous file with comments | « mojo/public/python/mojo_bindings/promise.py ('k') | mojo/public/python/mojo_bindings/serialization.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698