| 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 |