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 7import itertools 8 9# pylint: disable=F0401 10import mojo.bindings.serialization as serialization 11 12 13class MojoEnumType(type): 14 """Meta class for enumerations. 15 16 Usage: 17 class MyEnum(object): 18 __metaclass__ = MojoEnumType 19 VALUES = [ 20 ('A', 0), 21 'B', 22 ('C', 5), 23 ] 24 25 This will define a enum with 3 values, 'A' = 0, 'B' = 1 and 'C' = 5. 26 """ 27 28 def __new__(mcs, name, bases, dictionary): 29 dictionary['__slots__'] = () 30 dictionary['__new__'] = None 31 for value in dictionary.pop('VALUES', []): 32 if not isinstance(value, tuple): 33 raise ValueError('incorrect value: %r' % value) 34 key, enum_value = value 35 if isinstance(key, str) and isinstance(enum_value, int): 36 dictionary[key] = enum_value 37 else: 38 raise ValueError('incorrect value: %r' % value) 39 return type.__new__(mcs, name, bases, dictionary) 40 41 def __setattr__(mcs, key, value): 42 raise AttributeError, 'can\'t set attribute' 43 44 def __delattr__(mcs, key): 45 raise AttributeError, 'can\'t delete attribute' 46 47 48class MojoStructType(type): 49 """Meta class for structs. 50 51 Usage: 52 class MyStruct(object): 53 __metaclass__ = MojoStructType 54 DESCRIPTOR = { 55 'constants': { 56 'C1': 1, 57 'C2': 2, 58 }, 59 'enums': { 60 'ENUM1': [ 61 ('V1', 1), 62 'V2', 63 ], 64 'ENUM2': [ 65 ('V1', 1), 66 'V2', 67 ], 68 }, 69 'fields': [ 70 FieldDescriptor('x', _descriptor.TYPE_INT32, 0), 71 ], 72 } 73 74 This will define an struct, with: 75 - 2 constants 'C1' and 'C2'; 76 - 2 enums 'ENUM1' and 'ENUM2', each of those having 2 values, 'V1' and 77 'V2'; 78 - 1 int32 field named 'x'. 79 """ 80 81 def __new__(mcs, name, bases, dictionary): 82 dictionary['__slots__'] = ('_fields') 83 descriptor = dictionary.pop('DESCRIPTOR', {}) 84 85 # Add constants 86 dictionary.update(descriptor.get('constants', {})) 87 88 # Add enums 89 enums = descriptor.get('enums', {}) 90 for key in enums: 91 dictionary[key] = MojoEnumType(key, (object,), { 'VALUES': enums[key] }) 92 93 # Add fields 94 groups = descriptor.get('fields', []) 95 96 fields = list( 97 itertools.chain.from_iterable([group.descriptors for group in groups])) 98 for field in fields: 99 dictionary[field.name] = _BuildProperty(field) 100 101 # Add init 102 dictionary['__init__'] = _StructInit 103 104 # Add serialization method 105 serialization_object = serialization.Serialization(groups) 106 def Serialize(self, handle_offset=0): 107 return serialization_object.Serialize(self, handle_offset) 108 dictionary['Serialize'] = Serialize 109 110 def Deserialize(cls, data, handles): 111 result = cls.__new__(cls) 112 fields = {} 113 serialization_object.Deserialize(fields, data, handles) 114 result._fields = fields 115 return result 116 dictionary['Deserialize'] = classmethod(Deserialize) 117 118 return type.__new__(mcs, name, bases, dictionary) 119 120 # Prevent adding new attributes, or mutating constants. 121 def __setattr__(mcs, key, value): 122 raise AttributeError, 'can\'t set attribute' 123 124 # Prevent deleting constants. 125 def __delattr__(mcs, key): 126 raise AttributeError, 'can\'t delete attribute' 127 128 129def _StructInit(self, **kwargs): 130 self._fields = {} 131 for name in kwargs: 132 self.__setattr__(name, kwargs[name]) 133 134 135def _BuildProperty(field): 136 """Build the property for the given field.""" 137 138 # pylint: disable=W0212 139 def Get(self): 140 if field.name not in self._fields: 141 self._fields[field.name] = field.GetDefaultValue() 142 return self._fields[field.name] 143 144 # pylint: disable=W0212 145 def Set(self, value): 146 self._fields[field.name] = field.field_type.Convert(value) 147 148 return property(Get, Set) 149